Merge lp:~charlesk/indicator-display/adb-key-auth into lp:indicator-display/15.10
- adb-key-auth
- Merge into trunk.15.10
Status: | Merged |
---|---|
Approved by: | Xavi Garcia |
Approved revision: | 42 |
Merged at revision: | 18 |
Proposed branch: | lp:~charlesk/indicator-display/adb-key-auth |
Merge into: | lp:indicator-display/15.10 |
Diff against target: |
3348 lines (+2613/-310) 40 files modified
.bzr-builddeb/default.conf (+0/-2) CMakeLists.txt (+76/-60) cmake/FindGMock.cmake (+0/-10) cmake/GCov.cmake (+0/-51) cmake/GdbusCodegen.cmake (+0/-36) debian/control (+6/-0) po/POTFILES.in (+1/-0) src/CMakeLists.txt (+36/-28) src/adbd-client.cpp (+303/-0) src/adbd-client.h (+74/-0) src/dbus-names.h (+60/-0) src/greeter.cpp (+161/-0) src/greeter.h (+47/-0) src/indicator.cpp (+37/-0) src/indicator.h (+6/-7) src/main.cpp (+13/-0) src/rotation-lock.cpp (+1/-0) src/usb-manager.cpp (+180/-0) src/usb-manager.h (+48/-0) src/usb-monitor.cpp (+81/-0) src/usb-monitor.h (+52/-0) src/usb-snap.cpp (+250/-0) src/usb-snap.h (+42/-0) tests/CMakeLists.txt (+33/-33) tests/integration/CMakeLists.txt (+24/-0) tests/integration/usb-manager-test.cpp (+226/-0) tests/unit/CMakeLists.txt (+34/-0) tests/unit/adbd-client-test.cpp (+95/-0) tests/unit/rotation-lock-test.cpp (+3/-3) tests/unit/usb-snap-test.cpp (+143/-0) tests/utils/CMakeLists.txt (+17/-0) tests/utils/adbd-server.h (+150/-0) tests/utils/dbus-types.h (+42/-0) tests/utils/glib-fixture.h (+115/-64) tests/utils/gtest-qt-print-helpers.h (+45/-0) tests/utils/mock-greeter.h (+32/-0) tests/utils/mock-usb-monitor.h (+32/-0) tests/utils/qmain.cpp (+60/-0) tests/utils/qt-fixture.h (+74/-0) tests/utils/test-dbus-fixture.h (+14/-16) |
To merge this branch: | bzr merge lp:~charlesk/indicator-display/adb-key-auth |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Charles Kerr (community) | Approve | ||
Matthew Paul Thomas (community) | design | Needs Fixing | |
Xavi Garcia | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+289126@code.launchpad.net |
Commit message
When a new device appears to ADB, prompt the user whether or not to allow the connection.
Description of the change
When a new device appears to ADB, prompt the user whether or not to allow the connection.
Reference java implementation @ http://
Basically, the ADBD daemon talks to the indicator via a local domain socket. ADBD sends a request string "PK" + the public key. The indicator's job is to decode the public key and present the resulting fingerprint to the user to allow or deny. Reference Android prompt: http://
The indicator returns the strings 'OK' or 'NO' back down the socket depending on the user's choice. If the user clicked the 'remember this' checkbox, we'll also append the public key on its own new line to the textfile "/data/
NB 1: unity-notifications doesn't yet have a checkbox action, so this is omitted from this MP. To honor the most common use case first, this first cut remembers the public key if the user allows the connection.
NB 2: this code is currently being used for testing. The single line in main.cpp 'g_setenv(
PS Jenkins bot (ps-jenkins) wrote : | # |
- 38. By Charles Kerr
-
Remove the minimum version of 0.4 for libqtdbusmock1-dev, it's causing a failure on Jenkins wily and was only included due to copy-paste from indicator-network. So let's see how Wily goes without it.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:38
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 39. By Charles Kerr
-
use cmake-extras' EnableCoverageR
eport instead of home-rolled rules - 40. By Charles Kerr
-
in POTFILES.in, fix copy-paste typo
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:39
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 41. By Charles Kerr
-
in usb-manager.cpp, remove the remember_choice workaround since is already working around it
- 42. By Charles Kerr
-
tweak class description comments
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:42
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Xavi Garcia (xavi-garcia-mena) wrote : | # |
Looks good to me...
Just a minor comment that don't prevent this to be approved IMHO:
Would it make sense to have qDBusArgumentToMap and WAIT_FOR_SIGNALS defined in a common place?
I see both are defined twice and the code is the same...
In fact this is something I'm using in my tests and that maybe would be good to have in a common test-utils library or something... (maybe something to be done in the next future)
Xavi Garcia (xavi-garcia-mena) : | # |
- 43. By Charles Kerr
-
add tests/utils/
qdbus-helpers. h so that we only define qDBusArgumentTo Map() in one place - 44. By Charles Kerr
-
de-dupe use of dbus names
- 45. By Charles Kerr
-
introduce a QtFixture gtest base class to reduce redundancy in test fixtures' helper/util code
- 46. By Charles Kerr
-
sync with lp:~ubuntu-branches/ubuntu/wily/indicator-display/wily/ to pick up 0.1+15.
10.20150727- 0ubuntu2~ gcc5.1 - 47. By Charles Kerr
-
fix warning message
- 48. By Charles Kerr
-
sync with trunk
- 49. By Charles Kerr
-
re-add .bzr-builddeb, which got removed by accident
- 50. By Charles Kerr
-
add some extra debug statements to usb-manager.cpp to track user response and the act of writing the pk out to disk
Matthew Paul Thomas (mpt) wrote : | # |
Please change “Deny” to “Don’t Allow” for consistency with other permission prompts.
<https:/
- 51. By Charles Kerr
-
turn off verbose debugging
- 52. By Charles Kerr
-
if our USB device is disconnected while prompting the user for ADBD, cancel the prompt.
- 53. By Charles Kerr
-
in UsbManager, reset AdbdClient on usb disconnect
- 54. By Charles Kerr
-
add Greeter skeleton
- 55. By Charles Kerr
-
get greeter's IsActive property working
- 56. By Charles Kerr
-
fix typo
- 57. By Charles Kerr
-
make wait_for_signals() a macro again so that the GTest log messages will give the test file and line number
- 58. By Charles Kerr
-
in the mock ADB server, keep making a request until a response is received.
- 59. By Charles Kerr
-
add multiple usb requests + disconnects to confirm the notification appears on subsequent requests
- 60. By Charles Kerr
-
don't show the snap decision until we're out of the greeter
- 61. By Charles Kerr
-
add tests for not showing snap decisions in greeter mode
- 62. By Charles Kerr
-
replace text 'Deny' with 'Don't Allow' for consistency with other permission prompts
- 63. By Charles Kerr
-
keep the adbd socket open even when the lockscreen is closed. hold the pkrequest state in USBManager until the screen's unlocked.
- 64. By Charles Kerr
-
fix missing field initialization compiler warning
- 65. By Charles Kerr
-
fix UsbManager dtor issue found by valgrind
Charles Kerr (charlesk) wrote : | # |
re-approving up to r65 from ondra revie
- 66. By Charles Kerr
-
add tracer g_debug() calls for the benefit of the integration tests
Preview Diff
1 | === added directory '.bzr-builddeb' |
2 | === removed directory '.bzr-builddeb' |
3 | === added file '.bzr-builddeb/default.conf' |
4 | --- .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000 |
5 | +++ .bzr-builddeb/default.conf 2016-03-24 16:01:39 +0000 |
6 | @@ -0,0 +1,2 @@ |
7 | +[BUILDDEB] |
8 | +split = True |
9 | |
10 | === removed file '.bzr-builddeb/default.conf' |
11 | --- .bzr-builddeb/default.conf 2014-09-05 20:21:31 +0000 |
12 | +++ .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000 |
13 | @@ -1,2 +0,0 @@ |
14 | -[BUILDDEB] |
15 | -split = True |
16 | |
17 | === modified file 'CMakeLists.txt' |
18 | --- CMakeLists.txt 2014-08-21 03:56:45 +0000 |
19 | +++ CMakeLists.txt 2016-03-24 16:01:39 +0000 |
20 | @@ -1,72 +1,88 @@ |
21 | -project (indicator-display) |
22 | -cmake_minimum_required (VERSION 2.8.9) |
23 | - |
24 | -list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) |
25 | - |
26 | -set (PROJECT_VERSION "14.10.0") |
27 | -set (PACKAGE ${CMAKE_PROJECT_NAME}) |
28 | -set (GETTEXT_PACKAGE indicator-display) |
29 | -add_definitions (-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}" |
30 | - -DGNOMELOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}") |
31 | - |
32 | -set (SERVICE_LIB ${PACKAGE}) |
33 | -set (SERVICE_EXEC "${PACKAGE}-service") |
34 | - |
35 | -option (enable_tests "Build the package's automatic tests." ON) |
36 | -option (enable_lcov "Generate lcov code coverage reports." ON) |
37 | +project(indicator-display LANGUAGES C CXX) |
38 | +cmake_minimum_required(VERSION 2.8.9) |
39 | + |
40 | +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) |
41 | + |
42 | +set(PACKAGE ${CMAKE_PROJECT_NAME}) |
43 | +set(GETTEXT_PACKAGE indicator-display) |
44 | +add_definitions( |
45 | + -DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}" |
46 | + -DGNOMELOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}" |
47 | +) |
48 | + |
49 | +set(SERVICE_LIB ${PACKAGE}) |
50 | +set(SERVICE_EXEC "${PACKAGE}-service") |
51 | + |
52 | +option(enable_tests "Build the package's automatic tests." ON) |
53 | +option(enable_coverage "Generate code coverage reports." ON) |
54 | |
55 | ## |
56 | ## GNU standard paths |
57 | ## |
58 | -include (GNUInstallDirs) |
59 | -if (EXISTS "/etc/debian_version") # Workaround for libexecdir on debian |
60 | - set (CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}") |
61 | - set (CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_FULL_LIBDIR}") |
62 | -endif () |
63 | -set (CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}") |
64 | -set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}") |
65 | + |
66 | +include(GNUInstallDirs) |
67 | +if(EXISTS "/etc/debian_version") # Workaround for libexecdir on debian |
68 | + set(CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}") |
69 | + set(CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_FULL_LIBDIR}") |
70 | +endif() |
71 | +set(CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}") |
72 | +set(CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}") |
73 | |
74 | ## |
75 | ## Check for prerequisites |
76 | ## |
77 | |
78 | -find_package (PkgConfig REQUIRED) |
79 | - |
80 | -include (FindPkgConfig) |
81 | -pkg_check_modules (SERVICE_DEPS REQUIRED |
82 | - gio-unix-2.0>=2.36 |
83 | - glib-2.0>=2.36) |
84 | -include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS}) |
85 | - |
86 | -## |
87 | -## |
88 | -## |
89 | - |
90 | -set (CMAKE_INCLUDE_CURRENT_DIR OFF) |
91 | -include_directories (${CMAKE_CURRENT_SOURCE_DIR}) |
92 | +# threads... |
93 | +set(THREADS_PREFER_PTHREAD_FLAG ON) |
94 | +find_package(Threads REQUIRED) |
95 | +if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 3.1) |
96 | + set(THREAD_LINK_LIBRARIES -pthread) |
97 | +else() |
98 | + set(THREAD_LINK_LIBRARIES Threads::Threads) # introduced in cmake 3.1 |
99 | +endif() |
100 | + |
101 | +find_package(PkgConfig REQUIRED) |
102 | + |
103 | +# glib... |
104 | +set(GLIB_MINIMUM 2.36) |
105 | +pkg_check_modules(SERVICE_DEPS REQUIRED |
106 | + gio-unix-2.0>=${GLIB_MINIMUM} |
107 | + glib-2.0>=${GLIB_MINIMUM} |
108 | + gudev-1.0 |
109 | +) |
110 | +include_directories (SYSTEM |
111 | + ${SERVICE_DEPS_INCLUDE_DIRS} |
112 | +) |
113 | + |
114 | +## |
115 | +## Compiler settings |
116 | +## |
117 | + |
118 | +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) |
119 | |
120 | # set the compiler warnings |
121 | -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") |
122 | - set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Weverything -Wno-c++98-compat") |
123 | -else() |
124 | - set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wall -Wextra -Wpedantic") |
125 | -endif() |
126 | -set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-missing-field-initializers") # GActionEntry |
127 | - |
128 | -# testing & coverage |
129 | -if (${enable_tests}) |
130 | - set (GTEST_SOURCE_DIR /usr/src/gtest/src) |
131 | - set (GTEST_INCLUDE_DIR ${GTEST_SOURCE_DIR}/..) |
132 | - set (GTEST_LIBS -lpthread) |
133 | - enable_testing () |
134 | - if (${enable_lcov}) |
135 | - include(GCov) |
136 | - endif () |
137 | -endif () |
138 | - |
139 | -add_subdirectory (src) |
140 | -add_subdirectory (data) |
141 | -add_subdirectory (po) |
142 | -if (${enable_tests}) |
143 | - add_subdirectory (tests) |
144 | +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") |
145 | + list(APPEND CXX_WARNING_ARGS -Weverything -Wno-c++98-compat -Wno-padded) |
146 | +elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") |
147 | + list(APPEND CXX_WARNING_ARGS -Wall -Wextra -Wpedantic) |
148 | +endif() |
149 | + |
150 | +add_compile_options(-std=c++14 -fPIC -g) |
151 | + |
152 | +## |
153 | +## Testing & Coverage |
154 | +## |
155 | + |
156 | +if(${enable_tests}) |
157 | + enable_testing() |
158 | + if(${enable_coverage}) |
159 | + include(EnableCoverageReport) |
160 | + endif() |
161 | +endif() |
162 | + |
163 | +add_subdirectory(src) |
164 | +add_subdirectory(data) |
165 | +add_subdirectory(po) |
166 | +if (${enable_tests}) |
167 | + add_subdirectory(tests) |
168 | endif () |
169 | |
170 | === removed file 'cmake/FindGMock.cmake' |
171 | --- cmake/FindGMock.cmake 2014-08-21 04:03:20 +0000 |
172 | +++ cmake/FindGMock.cmake 1970-01-01 00:00:00 +0000 |
173 | @@ -1,10 +0,0 @@ |
174 | -# Build with system gmock and embedded gtest |
175 | -set (GMOCK_INCLUDE_DIRS "/usr/include/gmock/include" CACHE PATH "gmock source include directory") |
176 | -set (GMOCK_SOURCE_DIR "/usr/src/gmock" CACHE PATH "gmock source directory") |
177 | -set (GTEST_INCLUDE_DIRS "${GMOCK_SOURCE_DIR}/gtest/include" CACHE PATH "gtest source include directory") |
178 | - |
179 | -add_subdirectory(${GMOCK_SOURCE_DIR} "${CMAKE_CURRENT_BINARY_DIR}/gmock") |
180 | - |
181 | -set(GTEST_LIBRARIES gtest) |
182 | -set(GTEST_MAIN_LIBRARIES gtest_main) |
183 | -set(GMOCK_LIBRARIES gmock gmock_main) |
184 | |
185 | === removed file 'cmake/GCov.cmake' |
186 | --- cmake/GCov.cmake 2014-08-21 04:03:20 +0000 |
187 | +++ cmake/GCov.cmake 1970-01-01 00:00:00 +0000 |
188 | @@ -1,51 +0,0 @@ |
189 | -if (CMAKE_BUILD_TYPE MATCHES coverage) |
190 | - set(GCOV_FLAGS "${GCOV_FLAGS} --coverage") |
191 | - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCOV_FLAGS}") |
192 | - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${GCOV_FLAGS}") |
193 | - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GCOV_FLAGS}") |
194 | - set(GCOV_LIBS ${GCOV_LIBS} gcov) |
195 | - |
196 | - find_program(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin") |
197 | - if (NOT GCOVR_EXECUTABLE) |
198 | - message(STATUS "Gcovr binary was not found, can not generate XML coverage info.") |
199 | - else () |
200 | - message(STATUS "Gcovr found, can generate XML coverage info.") |
201 | - add_custom_target (coverage-xml |
202 | - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
203 | - COMMAND "${GCOVR_EXECUTABLE}" --exclude="test.*" -x -r "${CMAKE_SOURCE_DIR}" |
204 | - --object-directory=${CMAKE_BINARY_DIR} -o coverage.xml) |
205 | - endif() |
206 | - |
207 | - find_program(LCOV_EXECUTABLE lcov HINTS ${LCOV_ROOT} "${GCOVR_ROOT}/bin") |
208 | - find_program(GENHTML_EXECUTABLE genhtml HINTS ${GENHTML_ROOT}) |
209 | - if (NOT LCOV_EXECUTABLE) |
210 | - message(STATUS "Lcov binary was not found, can not generate HTML coverage info.") |
211 | - else () |
212 | - if(NOT GENHTML_EXECUTABLE) |
213 | - message(STATUS "Genthml binary not found, can not generate HTML coverage info.") |
214 | - else() |
215 | - message(STATUS "Lcov and genhtml found, can generate HTML coverage info.") |
216 | - add_custom_target (coverage-html |
217 | - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
218 | - COMMAND "${CMAKE_CTEST_COMMAND}" --force-new-ctest-process --verbose |
219 | - COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture | ${CMAKE_SOURCE_DIR}/trim-lcov.py > dconf-lcov.info |
220 | - COMMAND "${LCOV_EXECUTABLE}" -r dconf-lcov.info /usr/include/\\* -o nosys-lcov.info |
221 | - COMMAND LANG=C "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory lcov-html --legend --show-details nosys-lcov.info |
222 | - COMMAND ${CMAKE_COMMAND} -E echo "" |
223 | - COMMAND ${CMAKE_COMMAND} -E echo "file://${CMAKE_BINARY_DIR}/lcov-html/index.html" |
224 | - COMMAND ${CMAKE_COMMAND} -E echo "") |
225 | - #COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture --output-file coverage.info --no-checksum |
226 | - #COMMAND "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info |
227 | - #COMMAND ${CMAKE_COMMAND} -E echo "\\#define foo \\\"bar\\\"" |
228 | - #) |
229 | - endif() |
230 | - endif() |
231 | -endif() |
232 | - |
233 | - |
234 | - #$(MAKE) $(AM_MAKEFLAGS) check |
235 | - #lcov --directory $(top_builddir) --capture --test-name dconf | $(top_srcdir)/trim-lcov.py > dconf-lcov.info |
236 | - #LANG=C genhtml --prefix $(top_builddir) --output-directory lcov-html --legend --show-details dconf-lcov.info |
237 | - #@echo |
238 | - #@echo " file://$(abs_top_builddir)/lcov-html/index.html" |
239 | - #@echo |
240 | |
241 | === removed file 'cmake/GdbusCodegen.cmake' |
242 | --- cmake/GdbusCodegen.cmake 2014-08-21 04:03:20 +0000 |
243 | +++ cmake/GdbusCodegen.cmake 1970-01-01 00:00:00 +0000 |
244 | @@ -1,36 +0,0 @@ |
245 | -cmake_minimum_required(VERSION 2.6) |
246 | -if(POLICY CMP0011) |
247 | - cmake_policy(SET CMP0011 NEW) |
248 | -endif(POLICY CMP0011) |
249 | - |
250 | -find_program(GDBUS_CODEGEN NAMES gdbus-codegen DOC "gdbus-codegen executable") |
251 | -if(NOT GDBUS_CODEGEN) |
252 | - message(FATAL_ERROR "Excutable gdbus-codegen not found") |
253 | -endif() |
254 | - |
255 | -macro(add_gdbus_codegen outfiles name prefix service_xml) |
256 | - add_custom_command( |
257 | - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c" |
258 | - COMMAND "${GDBUS_CODEGEN}" |
259 | - --interface-prefix "${prefix}" |
260 | - --generate-c-code "${name}" |
261 | - "${service_xml}" |
262 | - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} |
263 | - DEPENDS ${ARGN} "${service_xml}" |
264 | - ) |
265 | - list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c") |
266 | -endmacro(add_gdbus_codegen) |
267 | - |
268 | -macro(add_gdbus_codegen_with_namespace outfiles name prefix namespace service_xml) |
269 | - add_custom_command( |
270 | - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c" |
271 | - COMMAND "${GDBUS_CODEGEN}" |
272 | - --interface-prefix "${prefix}" |
273 | - --generate-c-code "${name}" |
274 | - --c-namespace "${namespace}" |
275 | - "${service_xml}" |
276 | - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} |
277 | - DEPENDS ${ARGN} "${service_xml}" |
278 | - ) |
279 | - list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c") |
280 | -endmacro(add_gdbus_codegen_with_namespace) |
281 | |
282 | === modified file 'debian/control' |
283 | --- debian/control 2016-03-17 18:48:26 +0000 |
284 | +++ debian/control 2016-03-24 16:01:39 +0000 |
285 | @@ -4,12 +4,18 @@ |
286 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
287 | XSBC-Original-Maintainer: Charles Kerr <charles.kerr@canonical.com> |
288 | Build-Depends: cmake, |
289 | + cmake-extras (>= 0.4), |
290 | dbus, |
291 | libglib2.0-dev (>= 2.36), |
292 | + libgudev-1.0-dev, |
293 | libproperties-cpp-dev, |
294 | # for coverage reports |
295 | lcov, |
296 | # for tests |
297 | + qt5-default, |
298 | + qtbase5-dev, |
299 | + libqtdbusmock1-dev, |
300 | + libqtdbustest1-dev, |
301 | cppcheck, |
302 | libgtest-dev, |
303 | google-mock (>= 1.6.0+svn437), |
304 | |
305 | === modified file 'po/POTFILES.in' |
306 | --- po/POTFILES.in 2014-08-20 03:13:45 +0000 |
307 | +++ po/POTFILES.in 2016-03-24 16:01:39 +0000 |
308 | @@ -1,1 +1,2 @@ |
309 | src/rotation-lock.cpp |
310 | +src/usb-snap.cpp |
311 | |
312 | === modified file 'src/CMakeLists.txt' |
313 | --- src/CMakeLists.txt 2014-08-21 03:35:16 +0000 |
314 | +++ src/CMakeLists.txt 2016-03-24 16:01:39 +0000 |
315 | @@ -1,29 +1,37 @@ |
316 | -set (SERVICE_LIB "indicatordisplayservice") |
317 | -set (SERVICE_EXEC "indicator-display-service") |
318 | - |
319 | -add_definitions (-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") |
320 | - |
321 | -# handwritten source code... |
322 | -set (SERVICE_LIB_HANDWRITTEN_SOURCES |
323 | - exporter.cpp |
324 | - rotation-lock.cpp) |
325 | - |
326 | -add_library (${SERVICE_LIB} STATIC |
327 | - ${SERVICE_LIB_HANDWRITTEN_SOURCES}) |
328 | - |
329 | -# add the bin dir to the include path so that |
330 | -# the compiler can find the generated header files |
331 | -include_directories (${CMAKE_CURRENT_BINARY_DIR}) |
332 | - |
333 | -link_directories (${SERVICE_DEPS_LIBRARY_DIRS}) |
334 | - |
335 | -set (SERVICE_EXEC_HANDWRITTEN_SOURCES main.cpp) |
336 | -add_executable (${SERVICE_EXEC} ${SERVICE_EXEC_HANDWRITTEN_SOURCES}) |
337 | -target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS}) |
338 | -install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) |
339 | - |
340 | -# add warnings/coverage info on handwritten files |
341 | -# but not the generated ones... |
342 | -set_property (SOURCE ${SERVICE_LIB_HANDWRITTEN_SOURCES} ${SERVICE_EXEC_HANDWRITTEN_SOURCES} |
343 | - APPEND_STRING PROPERTY COMPILE_FLAGS " -std=c++11 -g ${CXX_WARNING_ARGS} ${GCOV_FLAGS}") |
344 | + |
345 | +add_definitions(-DG_LOG_DOMAIN="${CMAKE_PROJECT_NAME}") |
346 | + |
347 | +add_compile_options( |
348 | + ${CXX_WARNING_ARGS} |
349 | +) |
350 | + |
351 | +add_library( |
352 | + ${SERVICE_LIB} |
353 | + STATIC |
354 | + adbd-client.cpp |
355 | + exporter.cpp |
356 | + greeter.cpp |
357 | + indicator.cpp |
358 | + rotation-lock.cpp |
359 | + usb-manager.cpp |
360 | + usb-monitor.cpp |
361 | + usb-snap.cpp |
362 | +) |
363 | + |
364 | +add_executable( |
365 | + ${SERVICE_EXEC} |
366 | + main.cpp |
367 | +) |
368 | + |
369 | +target_link_libraries(${SERVICE_EXEC} |
370 | + ${SERVICE_LIB} |
371 | + ${SERVICE_DEPS_LIBRARIES} |
372 | + ${THREAD_LINK_LIBRARIES} |
373 | +) |
374 | + |
375 | +install( |
376 | + TARGETS |
377 | + ${SERVICE_EXEC} |
378 | + RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR} |
379 | +) |
380 | |
381 | |
382 | === added file 'src/adbd-client.cpp' |
383 | --- src/adbd-client.cpp 1970-01-01 00:00:00 +0000 |
384 | +++ src/adbd-client.cpp 2016-03-24 16:01:39 +0000 |
385 | @@ -0,0 +1,303 @@ |
386 | +/* |
387 | + * Copyright 2016 Canonical Ltd. |
388 | + * |
389 | + * This program is free software: you can redistribute it and/or modify it |
390 | + * under the terms of the GNU General Public License version 3, as published |
391 | + * by the Free Software Foundation. |
392 | + * |
393 | + * This program is distributed in the hope that it will be useful, but |
394 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
395 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
396 | + * PURPOSE. See the GNU General Public License for more details. |
397 | + * |
398 | + * You should have received a copy of the GNU General Public License along |
399 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
400 | + * |
401 | + * Authors: |
402 | + * Charles Kerr <charles.kerr@canonical.com> |
403 | + */ |
404 | + |
405 | +#include <src/adbd-client.h> |
406 | + |
407 | +#include <gio/gio.h> |
408 | +#include <gio/gunixsocketaddress.h> |
409 | + |
410 | +#include <algorithm> |
411 | +#include <cctype> |
412 | +#include <cstring> |
413 | +#include <chrono> |
414 | +#include <condition_variable> |
415 | +#include <mutex> |
416 | +#include <thread> |
417 | + |
418 | +class GAdbdClient::Impl |
419 | +{ |
420 | +public: |
421 | + |
422 | + explicit Impl(const std::string& socket_path): |
423 | + m_socket_path{socket_path}, |
424 | + m_cancellable{g_cancellable_new()}, |
425 | + m_worker_thread{&Impl::worker_func, this} |
426 | + { |
427 | + } |
428 | + |
429 | + ~Impl() |
430 | + { |
431 | + // tell the worker thread to stop whatever it's doing and exit. |
432 | + g_debug("%s Client::Impl dtor, cancelling m_cancellable", G_STRLOC); |
433 | + g_cancellable_cancel(m_cancellable); |
434 | + m_pkresponse_cv.notify_one(); |
435 | + m_sleep_cv.notify_one(); |
436 | + m_worker_thread.join(); |
437 | + g_clear_object(&m_cancellable); |
438 | + } |
439 | + |
440 | + core::Signal<const PKRequest&>& on_pk_request() |
441 | + { |
442 | + return m_on_pk_request; |
443 | + } |
444 | + |
445 | +private: |
446 | + |
447 | + // struct to carry request info from the worker thread to the GMainContext thread |
448 | + struct PKIdleData |
449 | + { |
450 | + Impl* self = nullptr; |
451 | + GCancellable* cancellable = nullptr; |
452 | + const std::string public_key; |
453 | + |
454 | + PKIdleData(Impl* self_, GCancellable* cancellable_, std::string public_key_): |
455 | + self(self_), |
456 | + cancellable(G_CANCELLABLE(g_object_ref(cancellable_))), |
457 | + public_key(public_key_) {} |
458 | + |
459 | + ~PKIdleData() {g_clear_object(&cancellable);} |
460 | + |
461 | + }; |
462 | + |
463 | + void pass_public_key_to_main_thread(const std::string& public_key) |
464 | + { |
465 | + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, |
466 | + on_public_key_request_static, |
467 | + new PKIdleData{this, m_cancellable, public_key}, |
468 | + [](gpointer id){delete static_cast<PKIdleData*>(id);}); |
469 | + } |
470 | + |
471 | + static gboolean on_public_key_request_static (gpointer gdata) // runs in main thread |
472 | + { |
473 | + /* NB: It's possible (though unlikely) that data.self was destroyed |
474 | + while this callback was pending, so we must check is-cancelled FIRST */ |
475 | + auto data = static_cast<PKIdleData*>(gdata); |
476 | + if (!g_cancellable_is_cancelled(data->cancellable)) |
477 | + { |
478 | + // notify our listeners of the request |
479 | + auto self = data->self; |
480 | + struct PKRequest req; |
481 | + req.public_key = data->public_key; |
482 | + req.fingerprint = get_fingerprint(req.public_key); |
483 | + req.respond = [self](PKResponse response){self->on_public_key_response(response);}; |
484 | + self->m_on_pk_request(req); |
485 | + } |
486 | + |
487 | + return G_SOURCE_REMOVE; |
488 | + } |
489 | + |
490 | + void on_public_key_response(PKResponse response) |
491 | + { |
492 | + // set m_pkresponse and wake up the waiting worker thread |
493 | + std::unique_lock<std::mutex> lk(m_pkresponse_mutex); |
494 | + m_pkresponse = response; |
495 | + m_pkresponse_ready = true; |
496 | + m_pkresponse_cv.notify_one(); |
497 | + } |
498 | + |
499 | + /*** |
500 | + **** |
501 | + ***/ |
502 | + |
503 | + void worker_func() // runs in worker thread |
504 | + { |
505 | + const std::string socket_path {m_socket_path}; |
506 | + |
507 | + while (!g_cancellable_is_cancelled(m_cancellable)) |
508 | + { |
509 | + g_debug("%s creating a client socket to '%s'", G_STRLOC, socket_path.c_str()); |
510 | + auto socket = create_client_socket(socket_path); |
511 | + bool got_valid_req = false; |
512 | + |
513 | + g_debug("%s calling read_request", G_STRLOC); |
514 | + std::string reqstr; |
515 | + if (socket != nullptr) |
516 | + reqstr = read_request(socket); |
517 | + if (!reqstr.empty()) |
518 | + g_debug("%s got request [%s]", G_STRLOC, reqstr.c_str()); |
519 | + |
520 | + if (reqstr.substr(0,2) == "PK") { |
521 | + PKResponse response = PKResponse::DENY; |
522 | + const auto public_key = reqstr.substr(2); |
523 | + g_debug("%s got pk [%s]", G_STRLOC, public_key.c_str()); |
524 | + if (!public_key.empty()) { |
525 | + got_valid_req = true; |
526 | + std::unique_lock<std::mutex> lk(m_pkresponse_mutex); |
527 | + m_pkresponse_ready = false; |
528 | + pass_public_key_to_main_thread(public_key); |
529 | + m_pkresponse_cv.wait(lk, [this](){ |
530 | + return m_pkresponse_ready || g_cancellable_is_cancelled(m_cancellable); |
531 | + }); |
532 | + response = m_pkresponse; |
533 | + g_debug("%s got response '%d', is-cancelled %d", G_STRLOC, |
534 | + int(response), |
535 | + int(g_cancellable_is_cancelled(m_cancellable))); |
536 | + } |
537 | + if (!g_cancellable_is_cancelled(m_cancellable)) |
538 | + send_pk_response(socket, response); |
539 | + } else if (!reqstr.empty()) { |
540 | + g_warning("Invalid ADB request: [%s]", reqstr.c_str()); |
541 | + } |
542 | + |
543 | + g_clear_object(&socket); |
544 | + |
545 | + // If nothing interesting's happening, sleep a bit. |
546 | + // (Interval copied from UsbDebuggingManager.java) |
547 | + static constexpr std::chrono::seconds sleep_interval {std::chrono::seconds(1)}; |
548 | + if (!got_valid_req && !g_cancellable_is_cancelled(m_cancellable)) { |
549 | + std::unique_lock<std::mutex> lk(m_sleep_mutex); |
550 | + m_sleep_cv.wait_for(lk, sleep_interval); |
551 | + } |
552 | + } |
553 | + } |
554 | + |
555 | + // connect to a local domain socket |
556 | + GSocket* create_client_socket(const std::string& socket_path) |
557 | + { |
558 | + GError* error {}; |
559 | + auto socket = g_socket_new(G_SOCKET_FAMILY_UNIX, |
560 | + G_SOCKET_TYPE_STREAM, |
561 | + G_SOCKET_PROTOCOL_DEFAULT, |
562 | + &error); |
563 | + if (error != nullptr) { |
564 | + g_warning("Error creating adbd client socket: %s", error->message); |
565 | + g_clear_error(&error); |
566 | + g_clear_object(&socket); |
567 | + return nullptr; |
568 | + } |
569 | + |
570 | + auto address = g_unix_socket_address_new(socket_path.c_str()); |
571 | + const auto connected = g_socket_connect(socket, address, m_cancellable, &error); |
572 | + g_clear_object(&address); |
573 | + if (!connected) { |
574 | + g_debug("unable to connect to '%s': %s", socket_path.c_str(), error->message); |
575 | + g_clear_error(&error); |
576 | + g_clear_object(&socket); |
577 | + return nullptr; |
578 | + } |
579 | + |
580 | + return socket; |
581 | + } |
582 | + |
583 | + std::string read_request(GSocket* socket) |
584 | + { |
585 | + char buf[4096] = {}; |
586 | + g_debug("%s calling g_socket_receive()", G_STRLOC); |
587 | + const auto n_bytes = g_socket_receive (socket, buf, sizeof(buf), m_cancellable, nullptr); |
588 | + std::string ret; |
589 | + if (n_bytes > 0) |
590 | + ret.append(buf, std::string::size_type(n_bytes)); |
591 | + g_debug("%s g_socket_receive got %d bytes: [%s]", G_STRLOC, int(n_bytes), ret.c_str()); |
592 | + return ret; |
593 | + } |
594 | + |
595 | + void send_pk_response(GSocket* socket, PKResponse response) |
596 | + { |
597 | + std::string response_str; |
598 | + switch(response) { |
599 | + case PKResponse::ALLOW: response_str = "OK"; break; |
600 | + case PKResponse::DENY: response_str = "NO"; break; |
601 | + } |
602 | + g_debug("%s sending reply: [%s]", G_STRLOC, response_str.c_str()); |
603 | + |
604 | + GError* error {}; |
605 | + g_socket_send(socket, |
606 | + response_str.c_str(), |
607 | + response_str.size(), |
608 | + m_cancellable, |
609 | + &error); |
610 | + if (error != nullptr) { |
611 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
612 | + g_warning("GAdbdServer: Error accepting socket connection: %s", error->message); |
613 | + g_clear_error(&error); |
614 | + } |
615 | + } |
616 | + |
617 | + static std::string get_fingerprint(const std::string& public_key) |
618 | + { |
619 | + // The first token is base64-encoded data, so cut on the first whitespace |
620 | + const std::string base64 ( |
621 | + public_key.begin(), |
622 | + std::find_if( |
623 | + public_key.begin(), public_key.end(), |
624 | + [](const std::string::value_type& ch){return std::isspace(ch);} |
625 | + ) |
626 | + ); |
627 | + |
628 | + gsize digest_len {}; |
629 | + auto digest = g_base64_decode(base64.c_str(), &digest_len); |
630 | + |
631 | + auto checksum = g_compute_checksum_for_data(G_CHECKSUM_MD5, digest, digest_len); |
632 | + const gsize checksum_len = checksum ? strlen(checksum) : 0; |
633 | + |
634 | + // insert ':' between character pairs; eg "ff27b5f3" --> "ff:27:b5:f3" |
635 | + std::string fingerprint; |
636 | + for (gsize i=0; i<checksum_len; ) { |
637 | + fingerprint.append(checksum+i, checksum+i+2); |
638 | + if (i < checksum_len-2) |
639 | + fingerprint.append(":"); |
640 | + i += 2; |
641 | + } |
642 | + |
643 | + g_clear_pointer(&digest, g_free); |
644 | + g_clear_pointer(&checksum, g_free); |
645 | + return fingerprint; |
646 | + } |
647 | + |
648 | + const std::string m_socket_path; |
649 | + GCancellable* m_cancellable = nullptr; |
650 | + std::thread m_worker_thread; |
651 | + core::Signal<const PKRequest&> m_on_pk_request; |
652 | + |
653 | + std::mutex m_sleep_mutex; |
654 | + std::condition_variable m_sleep_cv; |
655 | + |
656 | + std::mutex m_pkresponse_mutex; |
657 | + std::condition_variable m_pkresponse_cv; |
658 | + bool m_pkresponse_ready = false; |
659 | + PKResponse m_pkresponse = PKResponse::DENY; |
660 | +}; |
661 | + |
662 | +/*** |
663 | +**** |
664 | +***/ |
665 | + |
666 | +AdbdClient::~AdbdClient() |
667 | +{ |
668 | +} |
669 | + |
670 | +/*** |
671 | +**** |
672 | +***/ |
673 | + |
674 | +GAdbdClient::GAdbdClient(const std::string& socket_path): |
675 | + impl{new Impl{socket_path}} |
676 | +{ |
677 | +} |
678 | + |
679 | +GAdbdClient::~GAdbdClient() |
680 | +{ |
681 | +} |
682 | + |
683 | +core::Signal<const AdbdClient::PKRequest&>& |
684 | +GAdbdClient::on_pk_request() |
685 | +{ |
686 | + return impl->on_pk_request(); |
687 | +} |
688 | + |
689 | |
690 | === added file 'src/adbd-client.h' |
691 | --- src/adbd-client.h 1970-01-01 00:00:00 +0000 |
692 | +++ src/adbd-client.h 2016-03-24 16:01:39 +0000 |
693 | @@ -0,0 +1,74 @@ |
694 | +/* |
695 | + * Copyright 2016 Canonical Ltd. |
696 | + * |
697 | + * This program is free software: you can redistribute it and/or modify it |
698 | + * under the terms of the GNU General Public License version 3, as published |
699 | + * by the Free Software Foundation. |
700 | + * |
701 | + * This program is distributed in the hope that it will be useful, but |
702 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
703 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
704 | + * PURPOSE. See the GNU General Public License for more details. |
705 | + * |
706 | + * You should have received a copy of the GNU General Public License along |
707 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
708 | + * |
709 | + * Authors: |
710 | + * Charles Kerr <charles.kerr@canonical.com> |
711 | + */ |
712 | + |
713 | +#pragma once |
714 | + |
715 | +#include <functional> |
716 | +#include <memory> |
717 | +#include <string> |
718 | + |
719 | +#include <core/signal.h> |
720 | + |
721 | +/** |
722 | + * Receives public key requests from ADBD and sends a response back. |
723 | + * |
724 | + * AdbClient only provides a receive/respond mechanism. The decision |
725 | + * of what response gets sent is delegated out to a listener via |
726 | + * the on_pk_request signal. |
727 | + * |
728 | + * The decider should connect to on_pk_request, listen for PKRequests, |
729 | + * and call the request's `respond' method with the desired response. |
730 | + */ |
731 | +class AdbdClient |
732 | +{ |
733 | +public: |
734 | + virtual ~AdbdClient(); |
735 | + |
736 | + enum class PKResponse { DENY, ALLOW }; |
737 | + |
738 | + struct PKRequest { |
739 | + std::string public_key; |
740 | + std::string fingerprint; |
741 | + std::function<void(PKResponse)> respond; |
742 | + }; |
743 | + |
744 | + virtual core::Signal<const PKRequest&>& on_pk_request() =0; |
745 | + |
746 | +protected: |
747 | + AdbdClient() =default; |
748 | +}; |
749 | + |
750 | +/** |
751 | + * An AdbdClient designed to work with GLib's event loop. |
752 | + * |
753 | + * The on_pk_request() signal will be called in global GMainContext's thread; |
754 | + * ie, just like a function passed to g_idle_add() or g_timeout_add(). |
755 | + */ |
756 | +class GAdbdClient: public AdbdClient |
757 | +{ |
758 | +public: |
759 | + explicit GAdbdClient(const std::string& socket_path); |
760 | + ~GAdbdClient(); |
761 | + core::Signal<const PKRequest&>& on_pk_request() override; |
762 | + |
763 | +private: |
764 | + class Impl; |
765 | + std::unique_ptr<Impl> impl; |
766 | +}; |
767 | + |
768 | |
769 | === added file 'src/dbus-names.h' |
770 | --- src/dbus-names.h 1970-01-01 00:00:00 +0000 |
771 | +++ src/dbus-names.h 2016-03-24 16:01:39 +0000 |
772 | @@ -0,0 +1,60 @@ |
773 | +/* |
774 | + * Copyright 2016 Canonical Ltd. |
775 | + * |
776 | + * This program is free software: you can redistribute it and/or modify it |
777 | + * under the terms of the GNU General Public License version 3, as published |
778 | + * by the Free Software Foundation. |
779 | + * |
780 | + * This program is distributed in the hope that it will be useful, but |
781 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
782 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
783 | + * PURPOSE. See the GNU General Public License for more details. |
784 | + * |
785 | + * You should have received a copy of the GNU General Public License along |
786 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
787 | + * |
788 | + * Authors: |
789 | + * Charles Kerr <charles.kerr@canonical.com> |
790 | + */ |
791 | + |
792 | +#pragma once |
793 | + |
794 | +namespace DBusNames |
795 | +{ |
796 | + namespace Notify |
797 | + { |
798 | + static constexpr char const * NAME = "org.freedesktop.Notifications"; |
799 | + static constexpr char const * PATH = "/org/freedesktop/Notifications"; |
800 | + static constexpr char const * INTERFACE = "org.freedesktop.Notifications"; |
801 | + |
802 | + namespace ActionInvoked |
803 | + { |
804 | + static constexpr char const * NAME = "ActionInvoked"; |
805 | + } |
806 | + |
807 | + namespace NotificationClosed |
808 | + { |
809 | + static constexpr char const * NAME = "NotificationClosed"; |
810 | + enum Reason { EXPIRED=1, DISMISSED=2, API=3, UNDEFINED=4 }; |
811 | + } |
812 | + } |
813 | + |
814 | + namespace UnityGreeter |
815 | + { |
816 | + static constexpr char const * NAME = "com.canonical.UnityGreeter"; |
817 | + static constexpr char const * PATH = "/"; |
818 | + static constexpr char const * INTERFACE = "com.canonical.UnityGreeter"; |
819 | + } |
820 | + |
821 | + namespace Properties |
822 | + { |
823 | + static constexpr char const * INTERFACE = "org.freedesktop.DBus.Properties"; |
824 | + |
825 | + namespace PropertiesChanged |
826 | + { |
827 | + static constexpr char const* NAME = "PropertiesChanged"; |
828 | + static constexpr char const* ARGS_VARIANT_TYPE = "(sa{sv}as)"; |
829 | + } |
830 | + } |
831 | +} |
832 | + |
833 | |
834 | === added file 'src/greeter.cpp' |
835 | --- src/greeter.cpp 1970-01-01 00:00:00 +0000 |
836 | +++ src/greeter.cpp 2016-03-24 16:01:39 +0000 |
837 | @@ -0,0 +1,161 @@ |
838 | +/* |
839 | + * Copyright 2016 Canonical Ltd. |
840 | + * |
841 | + * This program is free software: you can redistribute it and/or modify it |
842 | + * under the terms of the GNU General Public License version 3, as published |
843 | + * by the Free Software Foundation. |
844 | + * |
845 | + * This program is distributed in the hope that it will be useful, but |
846 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
847 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
848 | + * PURPOSE. See the GNU General Public License for more details. |
849 | + * |
850 | + * You should have received a copy of the GNU General Public License along |
851 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
852 | + * |
853 | + * Authors: |
854 | + * Charles Kerr <charles.kerr@canonical.com> |
855 | + */ |
856 | + |
857 | +#include <src/dbus-names.h> |
858 | +#include <src/greeter.h> |
859 | + |
860 | +#include <gio/gio.h> |
861 | + |
862 | +class UnityGreeter::Impl |
863 | +{ |
864 | +public: |
865 | + |
866 | + Impl(): |
867 | + m_cancellable{g_cancellable_new()} |
868 | + { |
869 | + g_bus_get(G_BUS_TYPE_SESSION, m_cancellable, on_bus_ready_static, this); |
870 | + } |
871 | + |
872 | + ~Impl() |
873 | + { |
874 | + g_cancellable_cancel(m_cancellable); |
875 | + g_clear_object(&m_cancellable); |
876 | + |
877 | + if (m_subscription_id != 0) |
878 | + g_dbus_connection_signal_unsubscribe (m_bus, m_subscription_id); |
879 | + |
880 | + g_clear_object(&m_bus); |
881 | + } |
882 | + |
883 | + core::Property<bool>& is_active() |
884 | + { |
885 | + return m_is_active; |
886 | + } |
887 | + |
888 | +private: |
889 | + |
890 | + static void on_bus_ready_static(GObject* /*source*/, GAsyncResult* res, gpointer gself) |
891 | + { |
892 | + GError* error {}; |
893 | + auto bus = g_bus_get_finish (res, &error); |
894 | + if (error != nullptr) { |
895 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
896 | + g_warning("UsbSnap: Error getting session bus: %s", error->message); |
897 | + g_clear_error(&error); |
898 | + } else { |
899 | + static_cast<Impl*>(gself)->on_bus_ready(bus); |
900 | + } |
901 | + g_clear_object(&bus); |
902 | + } |
903 | + |
904 | + void on_bus_ready(GDBusConnection* bus) |
905 | + { |
906 | + m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus))); |
907 | + |
908 | + g_dbus_connection_call(m_bus, |
909 | + DBusNames::UnityGreeter::NAME, |
910 | + DBusNames::UnityGreeter::PATH, |
911 | + DBusNames::Properties::INTERFACE, |
912 | + "Get", |
913 | + g_variant_new("(ss)", DBusNames::UnityGreeter::INTERFACE, "IsActive"), |
914 | + G_VARIANT_TYPE("(v)"), |
915 | + G_DBUS_CALL_FLAGS_NONE, |
916 | + -1, |
917 | + m_cancellable, |
918 | + on_get_is_active_ready, |
919 | + this); |
920 | + |
921 | + m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, |
922 | + DBusNames::UnityGreeter::NAME, |
923 | + DBusNames::Properties::INTERFACE, |
924 | + DBusNames::Properties::PropertiesChanged::NAME, |
925 | + DBusNames::UnityGreeter::PATH, |
926 | + DBusNames::UnityGreeter::INTERFACE, |
927 | + G_DBUS_SIGNAL_FLAGS_NONE, |
928 | + on_properties_changed_signal, |
929 | + this, |
930 | + nullptr); |
931 | + } |
932 | + |
933 | + static void on_get_is_active_ready(GObject* source, GAsyncResult* res, gpointer gself) |
934 | + { |
935 | + GError* error {}; |
936 | + auto v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error); |
937 | + if (error != nullptr) { |
938 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
939 | + g_warning("UsbSnap: Error getting session bus: %s", error->message); |
940 | + g_clear_error(&error); |
941 | + } else { |
942 | + GVariant* is_active {}; |
943 | + g_variant_get_child(v, 0, "v", &is_active); |
944 | + static_cast<Impl*>(gself)->m_is_active.set(g_variant_get_boolean(is_active)); |
945 | + g_clear_pointer(&is_active, g_variant_unref); |
946 | + } |
947 | + g_clear_pointer(&v, g_variant_unref); |
948 | + } |
949 | + |
950 | + static void on_properties_changed_signal(GDBusConnection* /*connection*/, |
951 | + const gchar* /*sender_name*/, |
952 | + const gchar* object_path, |
953 | + const gchar* interface_name, |
954 | + const gchar* signal_name, |
955 | + GVariant* parameters, |
956 | + gpointer gself) |
957 | + { |
958 | + g_return_if_fail(!g_strcmp0(object_path, DBusNames::UnityGreeter::PATH)); |
959 | + g_return_if_fail(!g_strcmp0(interface_name, DBusNames::Properties::INTERFACE)); |
960 | + g_return_if_fail(!g_strcmp0(signal_name, DBusNames::Properties::PropertiesChanged::NAME)); |
961 | + g_return_if_fail(g_variant_is_of_type(parameters, G_VARIANT_TYPE(DBusNames::Properties::PropertiesChanged::ARGS_VARIANT_TYPE))); |
962 | + |
963 | + auto v = g_variant_get_child_value (parameters, 1); |
964 | + gboolean is_active {}; |
965 | + if (g_variant_lookup(v, "IsActive", "b", &is_active)) |
966 | + { |
967 | + g_debug("%s is_active changed to %d", G_STRLOC, int(is_active)); |
968 | + static_cast<Impl*>(gself)->m_is_active.set(is_active); |
969 | + } |
970 | + g_clear_pointer(&v, g_variant_unref); |
971 | + } |
972 | + |
973 | + core::Property<bool> m_is_active; |
974 | + GCancellable* m_cancellable {}; |
975 | + GDBusConnection* m_bus {}; |
976 | + unsigned int m_subscription_id {}; |
977 | +}; |
978 | + |
979 | +/*** |
980 | +**** |
981 | +***/ |
982 | + |
983 | +Greeter::Greeter() =default; |
984 | + |
985 | +Greeter::~Greeter() =default; |
986 | + |
987 | +UnityGreeter::UnityGreeter(): |
988 | + impl{new Impl{}} |
989 | +{ |
990 | +} |
991 | + |
992 | +UnityGreeter::~UnityGreeter() =default; |
993 | + |
994 | +core::Property<bool>& |
995 | +UnityGreeter::is_active() |
996 | +{ |
997 | + return impl->is_active(); |
998 | +} |
999 | |
1000 | === added file 'src/greeter.h' |
1001 | --- src/greeter.h 1970-01-01 00:00:00 +0000 |
1002 | +++ src/greeter.h 2016-03-24 16:01:39 +0000 |
1003 | @@ -0,0 +1,47 @@ |
1004 | +/* |
1005 | + * Copyright 2016 Canonical Ltd. |
1006 | + * |
1007 | + * This program is free software: you can redistribute it and/or modify it |
1008 | + * under the terms of the GNU General Public License version 3, as published |
1009 | + * by the Free Software Foundation. |
1010 | + * |
1011 | + * This program is distributed in the hope that it will be useful, but |
1012 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1013 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1014 | + * PURPOSE. See the GNU General Public License for more details. |
1015 | + * |
1016 | + * You should have received a copy of the GNU General Public License along |
1017 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1018 | + * |
1019 | + * Authors: |
1020 | + * Charles Kerr <charles.kerr@canonical.com> |
1021 | + */ |
1022 | + |
1023 | +#pragma once |
1024 | + |
1025 | +#include <core/property.h> |
1026 | + |
1027 | +#include <memory> |
1028 | +#include <string> |
1029 | + |
1030 | +class Greeter |
1031 | +{ |
1032 | +public: |
1033 | + Greeter(); |
1034 | + virtual ~Greeter(); |
1035 | + virtual core::Property<bool>& is_active() =0; |
1036 | +}; |
1037 | + |
1038 | + |
1039 | +class UnityGreeter: public Greeter |
1040 | +{ |
1041 | +public: |
1042 | + UnityGreeter(); |
1043 | + virtual ~UnityGreeter(); |
1044 | + core::Property<bool>& is_active() override; |
1045 | + |
1046 | +protected: |
1047 | + class Impl; |
1048 | + std::unique_ptr<Impl> impl; |
1049 | +}; |
1050 | + |
1051 | |
1052 | === added file 'src/indicator.cpp' |
1053 | --- src/indicator.cpp 1970-01-01 00:00:00 +0000 |
1054 | +++ src/indicator.cpp 2016-03-24 16:01:39 +0000 |
1055 | @@ -0,0 +1,37 @@ |
1056 | +/* |
1057 | + * Copyright 2016 Canonical Ltd. |
1058 | + * |
1059 | + * This program is free software: you can redistribute it and/or modify it |
1060 | + * under the terms of the GNU General Public License version 3, as published |
1061 | + * by the Free Software Foundation. |
1062 | + * |
1063 | + * This program is distributed in the hope that it will be useful, but |
1064 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1065 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1066 | + * PURPOSE. See the GNU General Public License for more details. |
1067 | + * |
1068 | + * You should have received a copy of the GNU General Public License along |
1069 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1070 | + * |
1071 | + * Authors: |
1072 | + * Charles Kerr <charles.kerr@canonical.com> |
1073 | + */ |
1074 | + |
1075 | +#include <src/indicator.h> |
1076 | + |
1077 | +Profile::Profile() |
1078 | +{ |
1079 | +} |
1080 | + |
1081 | +Profile::~Profile() |
1082 | +{ |
1083 | +} |
1084 | + |
1085 | +SimpleProfile::~SimpleProfile() |
1086 | +{ |
1087 | +} |
1088 | + |
1089 | +Indicator::~Indicator() |
1090 | +{ |
1091 | +} |
1092 | + |
1093 | |
1094 | === modified file 'src/indicator.h' |
1095 | --- src/indicator.h 2014-10-06 19:54:15 +0000 |
1096 | +++ src/indicator.h 2016-03-24 16:01:39 +0000 |
1097 | @@ -1,5 +1,5 @@ |
1098 | /* |
1099 | - * Copyright 2014 Canonical Ltd. |
1100 | + * Copyright 2014-2016 Canonical Ltd. |
1101 | * |
1102 | * This program is free software: you can redistribute it and/or modify it |
1103 | * under the terms of the GNU General Public License version 3, as published |
1104 | @@ -17,8 +17,7 @@ |
1105 | * Charles Kerr <charles.kerr@canonical.com> |
1106 | */ |
1107 | |
1108 | -#ifndef INDICATOR_DISPLAY_INDICATOR_H |
1109 | -#define INDICATOR_DISPLAY_INDICATOR_H |
1110 | +#pragma once |
1111 | |
1112 | #include <core/property.h> |
1113 | |
1114 | @@ -52,10 +51,10 @@ |
1115 | virtual std::string name() const =0; |
1116 | virtual const core::Property<Header>& header() const =0; |
1117 | virtual std::shared_ptr<GMenuModel> menu_model() const =0; |
1118 | - virtual ~Profile() =default; |
1119 | + virtual ~Profile(); |
1120 | |
1121 | protected: |
1122 | - Profile() =default; |
1123 | + Profile(); |
1124 | }; |
1125 | |
1126 | |
1127 | @@ -63,6 +62,7 @@ |
1128 | { |
1129 | public: |
1130 | SimpleProfile(const char* name, const std::shared_ptr<GMenuModel>& menu): m_name(name), m_menu(menu) {} |
1131 | + virtual ~SimpleProfile(); |
1132 | |
1133 | std::string name() const {return m_name;} |
1134 | core::Property<Header>& header() {return m_header;} |
1135 | @@ -79,11 +79,10 @@ |
1136 | class Indicator |
1137 | { |
1138 | public: |
1139 | - virtual ~Indicator() =default; |
1140 | + virtual ~Indicator(); |
1141 | |
1142 | virtual const char* name() const =0; |
1143 | virtual GSimpleActionGroup* action_group() const =0; |
1144 | virtual std::vector<std::shared_ptr<Profile>> profiles() const =0; |
1145 | }; |
1146 | |
1147 | -#endif |
1148 | |
1149 | === modified file 'src/main.cpp' |
1150 | --- src/main.cpp 2014-08-21 03:35:16 +0000 |
1151 | +++ src/main.cpp 2016-03-24 16:01:39 +0000 |
1152 | @@ -20,6 +20,10 @@ |
1153 | #include <src/exporter.h> |
1154 | #include <src/rotation-lock.h> |
1155 | |
1156 | +#include <src/greeter.h> |
1157 | +#include <src/usb-manager.h> |
1158 | +#include <src/usb-monitor.h> |
1159 | + |
1160 | #include <glib/gi18n.h> // bindtextdomain() |
1161 | #include <gio/gio.h> |
1162 | |
1163 | @@ -54,6 +58,15 @@ |
1164 | exporters.push_back(exporter); |
1165 | } |
1166 | |
1167 | + // We need the ADBD handler running, |
1168 | + // even though it doesn't have an indicator component yet |
1169 | + static constexpr char const * ADB_SOCKET_PATH {"/dev/socket/adbd"}; |
1170 | + static constexpr char const * PUBLIC_KEYS_FILENAME {"/data/misc/adb/adb_keys"}; |
1171 | + auto usb_monitor = std::make_shared<GUDevUsbMonitor>(); |
1172 | + auto greeter = std::make_shared<UnityGreeter>(); |
1173 | + UsbManager usb_manager {ADB_SOCKET_PATH, PUBLIC_KEYS_FILENAME, usb_monitor, greeter}; |
1174 | + |
1175 | + // let's go! |
1176 | g_main_loop_run(loop); |
1177 | |
1178 | // cleanup |
1179 | |
1180 | === modified file 'src/rotation-lock.cpp' |
1181 | --- src/rotation-lock.cpp 2015-03-01 00:13:47 +0000 |
1182 | +++ src/rotation-lock.cpp 2016-03-24 16:01:39 +0000 |
1183 | @@ -43,6 +43,7 @@ |
1184 | |
1185 | ~Impl() |
1186 | { |
1187 | + g_signal_handlers_disconnect_by_data(m_settings, this); |
1188 | g_clear_object(&m_action_group); |
1189 | g_clear_object(&m_settings); |
1190 | } |
1191 | |
1192 | === added file 'src/usb-manager.cpp' |
1193 | --- src/usb-manager.cpp 1970-01-01 00:00:00 +0000 |
1194 | +++ src/usb-manager.cpp 2016-03-24 16:01:39 +0000 |
1195 | @@ -0,0 +1,180 @@ |
1196 | +/* |
1197 | + * Copyright 2016 Canonical Ltd. |
1198 | + * |
1199 | + * This program is free software: you can redistribute it and/or modify it |
1200 | + * under the terms of the GNU General Public License version 3, as published |
1201 | + * by the Free Software Foundation. |
1202 | + * |
1203 | + * This program is distributed in the hope that it will be useful, but |
1204 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1205 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1206 | + * PURPOSE. See the GNU General Public License for more details. |
1207 | + * |
1208 | + * You should have received a copy of the GNU General Public License along |
1209 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1210 | + * |
1211 | + * Authors: |
1212 | + * Charles Kerr <charles.kerr@canonical.com> |
1213 | + */ |
1214 | + |
1215 | +#include <src/adbd-client.h> |
1216 | +#include <src/usb-manager.h> |
1217 | +#include <src/usb-snap.h> |
1218 | + |
1219 | +#include <glib.h> |
1220 | + |
1221 | +#include <sys/types.h> |
1222 | +#include <sys/stat.h> |
1223 | +#include <fcntl.h> |
1224 | +#include <unistd.h> |
1225 | + |
1226 | +#include <set> |
1227 | + |
1228 | +class UsbManager::Impl |
1229 | +{ |
1230 | +public: |
1231 | + |
1232 | + explicit Impl( |
1233 | + const std::string& socket_path, |
1234 | + const std::string& public_keys_filename, |
1235 | + const std::shared_ptr<UsbMonitor>& usb_monitor, |
1236 | + const std::shared_ptr<Greeter>& greeter |
1237 | + ): |
1238 | + m_socket_path{socket_path}, |
1239 | + m_public_keys_filename{public_keys_filename}, |
1240 | + m_usb_monitor{usb_monitor}, |
1241 | + m_greeter{greeter} |
1242 | + { |
1243 | + m_usb_monitor->on_usb_disconnected().connect([this](const std::string& /*usb_name*/) { |
1244 | + restart(); |
1245 | + }); |
1246 | + |
1247 | + m_greeter->is_active().changed().connect([this](bool /*is_active*/) { |
1248 | + maybe_snap(); |
1249 | + }); |
1250 | + |
1251 | + restart(); |
1252 | + } |
1253 | + |
1254 | + ~Impl() |
1255 | + { |
1256 | + if (m_restart_idle_tag) |
1257 | + g_source_remove(m_restart_idle_tag); |
1258 | + |
1259 | + clear(); |
1260 | + } |
1261 | + |
1262 | +private: |
1263 | + |
1264 | + void clear() |
1265 | + { |
1266 | + // clear out old state |
1267 | + m_snap_connections.clear(); |
1268 | + m_snap.reset(); |
1269 | + m_req = decltype(m_req)(); |
1270 | + m_adbd_client.reset(); |
1271 | + } |
1272 | + |
1273 | + void restart() |
1274 | + { |
1275 | + clear(); |
1276 | + |
1277 | + // set a new client |
1278 | + m_adbd_client.reset(new GAdbdClient{m_socket_path}); |
1279 | + m_adbd_client->on_pk_request().connect( |
1280 | + [this](const AdbdClient::PKRequest& req) { |
1281 | + g_debug("%s got pk request: %s", G_STRLOC, req.fingerprint.c_str()); |
1282 | + m_req = req; |
1283 | + maybe_snap(); |
1284 | + } |
1285 | + ); |
1286 | + } |
1287 | + |
1288 | + void maybe_snap() |
1289 | + { |
1290 | + // don't prompt in the greeter! |
1291 | + if (!m_req.public_key.empty() && !m_greeter->is_active().get()) |
1292 | + snap(); |
1293 | + } |
1294 | + |
1295 | + void snap() |
1296 | + { |
1297 | + m_snap = std::make_shared<UsbSnap>(m_req.fingerprint); |
1298 | + m_snap_connections.insert((*m_snap).on_user_response().connect( |
1299 | + [this](AdbdClient::PKResponse response, bool remember_choice){ |
1300 | + g_debug("%s user responded! response %d, remember %d", G_STRLOC, int(response), int(remember_choice)); |
1301 | + m_req.respond(response); |
1302 | + if (remember_choice && (response == AdbdClient::PKResponse::ALLOW)) |
1303 | + write_public_key(m_req.public_key); |
1304 | + m_restart_idle_tag = g_idle_add([](gpointer gself){ |
1305 | + auto self = static_cast<Impl*>(gself); |
1306 | + self->m_restart_idle_tag = 0; |
1307 | + self->restart(); |
1308 | + return G_SOURCE_REMOVE; |
1309 | + }, this); |
1310 | + } |
1311 | + )); |
1312 | + } |
1313 | + |
1314 | + void write_public_key(const std::string& public_key) |
1315 | + { |
1316 | + g_debug("%s writing public key '%s' to '%s'", G_STRLOC, public_key.c_str(), m_public_keys_filename.c_str()); |
1317 | + |
1318 | + // confirm the directory exists |
1319 | + auto dirname = g_path_get_dirname(m_public_keys_filename.c_str()); |
1320 | + const auto dir_exists = g_file_test(dirname, G_FILE_TEST_IS_DIR); |
1321 | + if (!dir_exists) |
1322 | + g_warning("ADB data directory '%s' does not exist", dirname); |
1323 | + g_clear_pointer(&dirname, g_free); |
1324 | + if (!dir_exists) |
1325 | + return; |
1326 | + |
1327 | + // open the file in append mode, with user rw and group r permissions |
1328 | + const auto fd = open( |
1329 | + m_public_keys_filename.c_str(), |
1330 | + O_APPEND|O_CREAT|O_WRONLY, |
1331 | + S_IRUSR|S_IWUSR|S_IRGRP |
1332 | + ); |
1333 | + if (fd == -1) { |
1334 | + g_warning("Error opening ADB datafile: %s", g_strerror(errno)); |
1335 | + return; |
1336 | + } |
1337 | + |
1338 | + // write the new public key on its own line |
1339 | + std::string buf {public_key + '\n'}; |
1340 | + if (write(fd, buf.c_str(), buf.size()) == -1) |
1341 | + g_warning("Error writing ADB datafile: %d %s", errno, g_strerror(errno)); |
1342 | + close(fd); |
1343 | + } |
1344 | + |
1345 | + const std::string m_socket_path; |
1346 | + const std::string m_public_keys_filename; |
1347 | + const std::shared_ptr<UsbMonitor> m_usb_monitor; |
1348 | + const std::shared_ptr<Greeter> m_greeter; |
1349 | + |
1350 | + unsigned int m_restart_idle_tag {}; |
1351 | + |
1352 | + std::shared_ptr<GAdbdClient> m_adbd_client; |
1353 | + AdbdClient::PKRequest m_req; |
1354 | + std::shared_ptr<UsbSnap> m_snap; |
1355 | + std::set<core::ScopedConnection> m_snap_connections; |
1356 | +}; |
1357 | + |
1358 | +/*** |
1359 | +**** |
1360 | +***/ |
1361 | + |
1362 | +UsbManager::UsbManager( |
1363 | + const std::string& socket_path, |
1364 | + const std::string& public_keys_filename, |
1365 | + const std::shared_ptr<UsbMonitor>& usb_monitor, |
1366 | + const std::shared_ptr<Greeter>& greeter |
1367 | +): |
1368 | + impl{new Impl{socket_path, public_keys_filename, usb_monitor, greeter}} |
1369 | +{ |
1370 | +} |
1371 | + |
1372 | +UsbManager::~UsbManager() |
1373 | +{ |
1374 | +} |
1375 | + |
1376 | |
1377 | === added file 'src/usb-manager.h' |
1378 | --- src/usb-manager.h 1970-01-01 00:00:00 +0000 |
1379 | +++ src/usb-manager.h 2016-03-24 16:01:39 +0000 |
1380 | @@ -0,0 +1,48 @@ |
1381 | +/* |
1382 | + * Copyright 2016 Canonical Ltd. |
1383 | + * |
1384 | + * This program is free software: you can redistribute it and/or modify it |
1385 | + * under the terms of the GNU General Public License version 3, as published |
1386 | + * by the Free Software Foundation. |
1387 | + * |
1388 | + * This program is distributed in the hope that it will be useful, but |
1389 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1390 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1391 | + * PURPOSE. See the GNU General Public License for more details. |
1392 | + * |
1393 | + * You should have received a copy of the GNU General Public License along |
1394 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1395 | + * |
1396 | + * Authors: |
1397 | + * Charles Kerr <charles.kerr@canonical.com> |
1398 | + */ |
1399 | + |
1400 | +#pragma once |
1401 | + |
1402 | +#include <src/greeter.h> |
1403 | +#include <src/usb-monitor.h> |
1404 | + |
1405 | +#include <memory> |
1406 | +#include <string> |
1407 | + |
1408 | +/** |
1409 | + * Manager class that connects the AdbdClient, UsbSnap, and manages the public key file |
1410 | + */ |
1411 | +class UsbManager |
1412 | +{ |
1413 | +public: |
1414 | + |
1415 | + UsbManager( |
1416 | + const std::string& socket_path, |
1417 | + const std::string& public_key_filename, |
1418 | + const std::shared_ptr<UsbMonitor>&, |
1419 | + const std::shared_ptr<Greeter>& |
1420 | + ); |
1421 | + |
1422 | + ~UsbManager(); |
1423 | + |
1424 | +protected: |
1425 | + |
1426 | + class Impl; |
1427 | + std::unique_ptr<Impl> impl; |
1428 | +}; |
1429 | |
1430 | === added file 'src/usb-monitor.cpp' |
1431 | --- src/usb-monitor.cpp 1970-01-01 00:00:00 +0000 |
1432 | +++ src/usb-monitor.cpp 2016-03-24 16:01:39 +0000 |
1433 | @@ -0,0 +1,81 @@ |
1434 | +/* |
1435 | + * Copyright 2016 Canonical Ltd. |
1436 | + * |
1437 | + * This program is free software: you can redistribute it and/or modify it |
1438 | + * under the terms of the GNU General Public License version 3, as published |
1439 | + * by the Free Software Foundation. |
1440 | + * |
1441 | + * This program is distributed in the hope that it will be useful, but |
1442 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1443 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1444 | + * PURPOSE. See the GNU General Public License for more details. |
1445 | + * |
1446 | + * You should have received a copy of the GNU General Public License along |
1447 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1448 | + * |
1449 | + * Authors: |
1450 | + * Charles Kerr <charles.kerr@canonical.com> |
1451 | + */ |
1452 | + |
1453 | +#include <src/usb-monitor.h> |
1454 | + |
1455 | +#include <glib.h> |
1456 | +#include <gudev/gudev.h> |
1457 | + |
1458 | +class GUDevUsbMonitor::Impl |
1459 | +{ |
1460 | +public: |
1461 | + |
1462 | + Impl() |
1463 | + { |
1464 | + const char* subsystems[] = {"android_usb", nullptr}; |
1465 | + m_udev_client = g_udev_client_new(subsystems); |
1466 | + g_signal_connect(m_udev_client, "uevent", G_CALLBACK(on_android_usb_event), this); |
1467 | + } |
1468 | + |
1469 | + ~Impl() |
1470 | + { |
1471 | + g_signal_handlers_disconnect_by_data(m_udev_client, this); |
1472 | + g_clear_object(&m_udev_client); |
1473 | + } |
1474 | + |
1475 | + core::Signal<const std::string&>& on_usb_disconnected() |
1476 | + { |
1477 | + return m_on_usb_disconnected; |
1478 | + } |
1479 | + |
1480 | +private: |
1481 | + |
1482 | + static void on_android_usb_event(GUdevClient*, gchar* action, GUdevDevice* device, gpointer gself) |
1483 | + { |
1484 | + if (!g_strcmp0(action, "change")) |
1485 | + if (!g_strcmp0(g_udev_device_get_property(device, "USB_STATE"), "DISCONNECTED")) |
1486 | + static_cast<Impl*>(gself)->m_on_usb_disconnected(g_udev_device_get_name(device)); |
1487 | + } |
1488 | + |
1489 | + core::Signal<const std::string&> m_on_usb_disconnected; |
1490 | + |
1491 | + GUdevClient* m_udev_client = nullptr; |
1492 | +}; |
1493 | + |
1494 | +/*** |
1495 | +**** |
1496 | +***/ |
1497 | + |
1498 | +UsbMonitor::UsbMonitor() =default; |
1499 | + |
1500 | +UsbMonitor::~UsbMonitor() =default; |
1501 | + |
1502 | +GUDevUsbMonitor::GUDevUsbMonitor(): |
1503 | + impl{new Impl{}} |
1504 | +{ |
1505 | +} |
1506 | + |
1507 | +GUDevUsbMonitor::~GUDevUsbMonitor() =default; |
1508 | + |
1509 | +core::Signal<const std::string&>& |
1510 | +GUDevUsbMonitor::on_usb_disconnected() |
1511 | +{ |
1512 | + return impl->on_usb_disconnected(); |
1513 | +} |
1514 | + |
1515 | |
1516 | === added file 'src/usb-monitor.h' |
1517 | --- src/usb-monitor.h 1970-01-01 00:00:00 +0000 |
1518 | +++ src/usb-monitor.h 2016-03-24 16:01:39 +0000 |
1519 | @@ -0,0 +1,52 @@ |
1520 | +/* |
1521 | + * Copyright 2016 Canonical Ltd. |
1522 | + * |
1523 | + * This program is free software: you can redistribute it and/or modify it |
1524 | + * under the terms of the GNU General Public License version 3, as published |
1525 | + * by the Free Software Foundation. |
1526 | + * |
1527 | + * This program is distributed in the hope that it will be useful, but |
1528 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1529 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1530 | + * PURPOSE. See the GNU General Public License for more details. |
1531 | + * |
1532 | + * You should have received a copy of the GNU General Public License along |
1533 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1534 | + * |
1535 | + * Authors: |
1536 | + * Charles Kerr <charles.kerr@canonical.com> |
1537 | + */ |
1538 | + |
1539 | +#pragma once |
1540 | + |
1541 | +#include <core/signal.h> |
1542 | + |
1543 | +#include <memory> |
1544 | +#include <string> |
1545 | + |
1546 | +/** |
1547 | + * Simple interface that emits signals on USB device state changes |
1548 | + */ |
1549 | +class UsbMonitor |
1550 | +{ |
1551 | +public: |
1552 | + UsbMonitor(); |
1553 | + virtual ~UsbMonitor(); |
1554 | + virtual core::Signal<const std::string&>& on_usb_disconnected() =0; |
1555 | +}; |
1556 | + |
1557 | +/** |
1558 | + * Simple GUDev wrapper that notifies on android_usb device state changes |
1559 | + */ |
1560 | +class GUDevUsbMonitor: public UsbMonitor |
1561 | +{ |
1562 | +public: |
1563 | + GUDevUsbMonitor(); |
1564 | + virtual ~GUDevUsbMonitor(); |
1565 | + core::Signal<const std::string&>& on_usb_disconnected() override; |
1566 | + |
1567 | +protected: |
1568 | + class Impl; |
1569 | + std::unique_ptr<Impl> impl; |
1570 | +}; |
1571 | + |
1572 | |
1573 | === added file 'src/usb-snap.cpp' |
1574 | --- src/usb-snap.cpp 1970-01-01 00:00:00 +0000 |
1575 | +++ src/usb-snap.cpp 2016-03-24 16:01:39 +0000 |
1576 | @@ -0,0 +1,250 @@ |
1577 | +/* |
1578 | + * Copyright 2016 Canonical Ltd. |
1579 | + * |
1580 | + * This program is free software: you can redistribute it and/or modify it |
1581 | + * under the terms of the GNU General Public License version 3, as published |
1582 | + * by the Free Software Foundation. |
1583 | + * |
1584 | + * This program is distributed in the hope that it will be useful, but |
1585 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1586 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1587 | + * PURPOSE. See the GNU General Public License for more details. |
1588 | + * |
1589 | + * You should have received a copy of the GNU General Public License along |
1590 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1591 | + * |
1592 | + * Authors: |
1593 | + * Charles Kerr <charles.kerr@canonical.com> |
1594 | + */ |
1595 | + |
1596 | +#include <src/dbus-names.h> |
1597 | +#include <src/usb-snap.h> |
1598 | + |
1599 | +#include <glib/gi18n.h> |
1600 | +#include <gio/gio.h> |
1601 | + |
1602 | +/*** |
1603 | +**** |
1604 | +***/ |
1605 | + |
1606 | +class UsbSnap::Impl |
1607 | +{ |
1608 | +public: |
1609 | + |
1610 | + explicit Impl(const std::string& fingerprint): |
1611 | + m_fingerprint{fingerprint}, |
1612 | + m_cancellable{g_cancellable_new()} |
1613 | + { |
1614 | + g_bus_get (G_BUS_TYPE_SESSION, m_cancellable, on_bus_ready_static, this); |
1615 | + } |
1616 | + |
1617 | + ~Impl() |
1618 | + { |
1619 | + g_cancellable_cancel(m_cancellable); |
1620 | + g_clear_object(&m_cancellable); |
1621 | + |
1622 | + if (m_subscription_id != 0) |
1623 | + g_dbus_connection_signal_unsubscribe (m_bus, m_subscription_id); |
1624 | + |
1625 | + if (m_notification_id != 0) { |
1626 | + GError* error {}; |
1627 | + g_dbus_connection_call_sync(m_bus, |
1628 | + DBusNames::Notify::NAME, |
1629 | + DBusNames::Notify::PATH, |
1630 | + DBusNames::Notify::INTERFACE, |
1631 | + "CloseNotification", |
1632 | + g_variant_new("(u)", m_notification_id), |
1633 | + nullptr, |
1634 | + G_DBUS_CALL_FLAGS_NONE, |
1635 | + -1, |
1636 | + nullptr, |
1637 | + &error); |
1638 | + if (error != nullptr) { |
1639 | + g_warning("Error closing notification: %s", error->message); |
1640 | + g_clear_error(&error); |
1641 | + } |
1642 | + } |
1643 | + |
1644 | + g_clear_object(&m_bus); |
1645 | + } |
1646 | + |
1647 | + core::Signal<AdbdClient::PKResponse,bool>& on_user_response() |
1648 | + { |
1649 | + return m_on_user_response; |
1650 | + } |
1651 | + |
1652 | +private: |
1653 | + |
1654 | + static void on_bus_ready_static(GObject* /*source*/, GAsyncResult* res, gpointer gself) |
1655 | + { |
1656 | + GError* error {}; |
1657 | + auto bus = g_bus_get_finish (res, &error); |
1658 | + if (error != nullptr) { |
1659 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
1660 | + g_warning("UsbSnap: Error getting session bus: %s", error->message); |
1661 | + g_clear_error(&error); |
1662 | + } else { |
1663 | + static_cast<Impl*>(gself)->on_bus_ready(bus); |
1664 | + } |
1665 | + g_clear_object(&bus); |
1666 | + } |
1667 | + |
1668 | + void on_bus_ready(GDBusConnection* bus) |
1669 | + { |
1670 | + m_bus = G_DBUS_CONNECTION(g_object_ref(G_OBJECT(bus))); |
1671 | + |
1672 | + m_subscription_id = g_dbus_connection_signal_subscribe(m_bus, |
1673 | + DBusNames::Notify::NAME, |
1674 | + DBusNames::Notify::INTERFACE, |
1675 | + nullptr, |
1676 | + DBusNames::Notify::PATH, |
1677 | + nullptr, |
1678 | + G_DBUS_SIGNAL_FLAGS_NONE, |
1679 | + on_notification_signal_static, |
1680 | + this, |
1681 | + nullptr); |
1682 | + |
1683 | + auto body = g_strdup_printf(_("The computer's RSA key fingerprint is: %s"), m_fingerprint.c_str()); |
1684 | + |
1685 | + GVariantBuilder actions_builder; |
1686 | + g_variant_builder_init(&actions_builder, G_VARIANT_TYPE_STRING_ARRAY); |
1687 | + g_variant_builder_add(&actions_builder, "s", ACTION_ALLOW); |
1688 | + g_variant_builder_add(&actions_builder, "s", _("Allow")); |
1689 | + g_variant_builder_add(&actions_builder, "s", ACTION_DENY); |
1690 | + g_variant_builder_add(&actions_builder, "s", _("Don't Allow")); |
1691 | + |
1692 | + GVariantBuilder hints_builder; |
1693 | + g_variant_builder_init(&hints_builder, G_VARIANT_TYPE_VARDICT); |
1694 | + g_variant_builder_add(&hints_builder, "{sv}", "x-canonical-non-shaped-icon", g_variant_new_string("true")); |
1695 | + g_variant_builder_add(&hints_builder, "{sv}", "x-canonical-snap-decisions", g_variant_new_string("true")); |
1696 | + g_variant_builder_add(&hints_builder, "{sv}", "x-canonical-private-affirmative-tint", g_variant_new_string("true")); |
1697 | + |
1698 | + auto args = g_variant_new("(susssasa{sv}i)", |
1699 | + "", |
1700 | + uint32_t(0), |
1701 | + "computer-symbolic", |
1702 | + _("Allow USB Debugging?"), |
1703 | + body, |
1704 | + &actions_builder, |
1705 | + &hints_builder, |
1706 | + -1); |
1707 | + g_dbus_connection_call(m_bus, |
1708 | + DBusNames::Notify::NAME, |
1709 | + DBusNames::Notify::PATH, |
1710 | + DBusNames::Notify::INTERFACE, |
1711 | + "Notify", |
1712 | + args, |
1713 | + G_VARIANT_TYPE("(u)"), |
1714 | + G_DBUS_CALL_FLAGS_NONE, |
1715 | + -1, // timeout |
1716 | + m_cancellable, |
1717 | + on_notify_reply_static, |
1718 | + this); |
1719 | + |
1720 | + g_clear_pointer(&body, g_free); |
1721 | + } |
1722 | + |
1723 | + static void on_notify_reply_static(GObject* obus, GAsyncResult* res, gpointer gself) |
1724 | + { |
1725 | + GError* error {}; |
1726 | + auto reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION(obus), res, &error); |
1727 | + if (error != nullptr) { |
1728 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
1729 | + g_warning("UsbSnap: Error calling Notify: %s", error->message); |
1730 | + g_clear_error(&error); |
1731 | + } else { |
1732 | + uint32_t id {}; |
1733 | + g_variant_get(reply, "(u)", &id); |
1734 | + static_cast<Impl*>(gself)->on_notify_reply(id); |
1735 | + } |
1736 | + g_clear_pointer(&reply, g_variant_unref); |
1737 | + } |
1738 | + |
1739 | + void on_notify_reply(uint32_t id) |
1740 | + { |
1741 | + m_notification_id = id; |
1742 | + } |
1743 | + |
1744 | + static void on_notification_signal_static(GDBusConnection* /*connection*/, |
1745 | + const gchar* /*sender_name*/, |
1746 | + const gchar* object_path, |
1747 | + const gchar* interface_name, |
1748 | + const gchar* signal_name, |
1749 | + GVariant* parameters, |
1750 | + gpointer gself) |
1751 | + { |
1752 | + g_return_if_fail(!g_strcmp0(object_path, DBusNames::Notify::PATH)); |
1753 | + g_return_if_fail(!g_strcmp0(interface_name, DBusNames::Notify::INTERFACE)); |
1754 | + |
1755 | + auto self = static_cast<Impl*>(gself); |
1756 | + |
1757 | + if (!g_strcmp0(signal_name, DBusNames::Notify::ActionInvoked::NAME)) |
1758 | + { |
1759 | + uint32_t id {}; |
1760 | + const char* action_name {}; |
1761 | + g_variant_get(parameters, "(u&s)", &id, &action_name); |
1762 | + if (id == self->m_notification_id) |
1763 | + self->on_action_invoked(action_name); |
1764 | + } |
1765 | + else if (!g_strcmp0(signal_name, DBusNames::Notify::NotificationClosed::NAME)) |
1766 | + { |
1767 | + uint32_t id {}; |
1768 | + uint32_t close_reason {}; |
1769 | + g_variant_get(parameters, "(uu)", &id, &close_reason); |
1770 | + if (id == self->m_notification_id) |
1771 | + self->on_notification_closed(close_reason); |
1772 | + } |
1773 | + } |
1774 | + |
1775 | + void on_action_invoked(const char* action_name) |
1776 | + { |
1777 | + const auto response = !g_strcmp0(action_name, ACTION_ALLOW) |
1778 | + ? AdbdClient::PKResponse::ALLOW |
1779 | + : AdbdClient::PKResponse::DENY; |
1780 | + |
1781 | + // FIXME: the current default is to cover the most common use case. |
1782 | + // We need to get the notification ui's checkbox working ASAP so |
1783 | + // that the user can provide this flag |
1784 | + const bool remember_this_choice = response == AdbdClient::PKResponse::ALLOW; |
1785 | + |
1786 | + m_on_user_response(response, remember_this_choice); |
1787 | + } |
1788 | + |
1789 | + void on_notification_closed(uint32_t close_reason) |
1790 | + { |
1791 | + if (close_reason == DBusNames::Notify::NotificationClosed::Reason::EXPIRED) |
1792 | + m_on_user_response(AdbdClient::PKResponse::DENY, false); |
1793 | + |
1794 | + m_notification_id = 0; |
1795 | + } |
1796 | + |
1797 | + static constexpr char const * ACTION_ALLOW {"allow"}; |
1798 | + static constexpr char const * ACTION_DENY {"deny"}; |
1799 | + |
1800 | + const std::string m_fingerprint; |
1801 | + core::Signal<AdbdClient::PKResponse,bool> m_on_user_response; |
1802 | + GCancellable* m_cancellable {}; |
1803 | + GDBusConnection* m_bus {}; |
1804 | + uint32_t m_notification_id {}; |
1805 | + unsigned int m_subscription_id {}; |
1806 | +}; |
1807 | + |
1808 | +/*** |
1809 | +**** |
1810 | +***/ |
1811 | + |
1812 | +UsbSnap::UsbSnap(const std::string& public_key): |
1813 | + impl{new Impl{public_key}} |
1814 | +{ |
1815 | +} |
1816 | + |
1817 | +UsbSnap::~UsbSnap() |
1818 | +{ |
1819 | +} |
1820 | + |
1821 | +core::Signal<AdbdClient::PKResponse,bool>& |
1822 | +UsbSnap::on_user_response() |
1823 | +{ |
1824 | + return impl->on_user_response(); |
1825 | +} |
1826 | + |
1827 | |
1828 | === added file 'src/usb-snap.h' |
1829 | --- src/usb-snap.h 1970-01-01 00:00:00 +0000 |
1830 | +++ src/usb-snap.h 2016-03-24 16:01:39 +0000 |
1831 | @@ -0,0 +1,42 @@ |
1832 | +/* |
1833 | + * Copyright 2016 Canonical Ltd. |
1834 | + * |
1835 | + * This program is free software: you can redistribute it and/or modify it |
1836 | + * under the terms of the GNU General Public License version 3, as published |
1837 | + * by the Free Software Foundation. |
1838 | + * |
1839 | + * This program is distributed in the hope that it will be useful, but |
1840 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1841 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1842 | + * PURPOSE. See the GNU General Public License for more details. |
1843 | + * |
1844 | + * You should have received a copy of the GNU General Public License along |
1845 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1846 | + * |
1847 | + * Authors: |
1848 | + * Charles Kerr <charles.kerr@canonical.com> |
1849 | + */ |
1850 | + |
1851 | +#pragma once |
1852 | + |
1853 | +#include <src/adbd-client.h> // AdbdClient::PKResponse |
1854 | + |
1855 | +#include <core/signal.h> |
1856 | + |
1857 | +#include <memory> |
1858 | +#include <string> |
1859 | + |
1860 | +/** |
1861 | + * A snap decision prompt for whether or not to allow an ADB connection |
1862 | + */ |
1863 | +class UsbSnap |
1864 | +{ |
1865 | +public: |
1866 | + explicit UsbSnap(const std::string& public_key); |
1867 | + ~UsbSnap(); |
1868 | + core::Signal<AdbdClient::PKResponse,bool>& on_user_response(); |
1869 | + |
1870 | +protected: |
1871 | + class Impl; |
1872 | + std::unique_ptr<Impl> impl; |
1873 | +}; |
1874 | |
1875 | === modified file 'tests/CMakeLists.txt' |
1876 | --- tests/CMakeLists.txt 2014-08-21 03:35:16 +0000 |
1877 | +++ tests/CMakeLists.txt 2016-03-24 16:01:39 +0000 |
1878 | @@ -1,35 +1,35 @@ |
1879 | -include(FindGMock) |
1880 | -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) |
1881 | -include_directories(${GMOCK_INCLUDE_DIRS}) |
1882 | -include_directories(${GTEST_INCLUDE_DIRS}) |
1883 | - |
1884 | -# build libgtest |
1885 | -#add_library (gtest STATIC |
1886 | -# ${GTEST_SOURCE_DIR}/gtest-all.cc |
1887 | -# ${GTEST_SOURCE_DIR}/gtest_main.cc) |
1888 | -#set_target_properties (gtest PROPERTIES INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR}) |
1889 | -#set_target_properties (gtest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -w) |
1890 | - |
1891 | -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") |
1892 | - # turn off the warnings that break Google Test |
1893 | - set (CXX_WARNING_ARGS "${CXX_WARNING_ARGS} -Wno-global-constructors -Wno-weak-vtables -Wno-undef -Wno-c++98-compat-pedantic -Wno-missing-noreturn -Wno-used-but-marked-unused -Wno-padded -Wno-deprecated -Wno-sign-compare -Wno-shift-sign-overflow") |
1894 | +set(CMAKE_AUTOMOC ON) |
1895 | +find_package(GMock REQUIRED) |
1896 | +find_package(Qt5Core REQUIRED) |
1897 | +find_package(Qt5Test REQUIRED) |
1898 | +find_package(Qt5DBus COMPONENTS Qt5DBusMacros REQUIRED) |
1899 | + |
1900 | +pkg_check_modules(TEST_DEPS |
1901 | + libqtdbustest-1 REQUIRED |
1902 | + libqtdbusmock-1 REQUIRED |
1903 | +) |
1904 | + |
1905 | +include_directories(SYSTEM |
1906 | + ${DBUSTEST_INCLUDE_DIRS} |
1907 | + ${TEST_DEPS_INCLUDE_DIRS} |
1908 | + ${GTEST_INCLUDE_DIRS} |
1909 | + ${GMOCK_INCLUDE_DIRS} |
1910 | +) |
1911 | + |
1912 | +list(APPEND CTEST_ENVIRONMENT |
1913 | + G_MESSAGES_DEBUG=all |
1914 | + G_DBUS_DEBUG=call,signal,return,message |
1915 | +) |
1916 | + |
1917 | +# turn off the warnings that break Google Test |
1918 | +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") |
1919 | + list(APPEND CXX_WARNING_ARGS -Wno-global-constructors -Wno-weak-vtables) |
1920 | endif() |
1921 | |
1922 | -SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${CXX_WARNING_ARGS}") |
1923 | - |
1924 | -# look for headers in our src dir, and also in the directories where we autogenerate files... |
1925 | -include_directories (${CMAKE_SOURCE_DIR}/src) |
1926 | -include_directories (${CMAKE_CURRENT_BINARY_DIR}) |
1927 | -include_directories (${DBUSTEST_INCLUDE_DIRS}) |
1928 | - |
1929 | -function(add_test_by_name name) |
1930 | - set (TEST_NAME ${name}) |
1931 | - add_executable (${TEST_NAME} ${TEST_NAME}.cpp) |
1932 | - add_test (${TEST_NAME} ${TEST_NAME}) |
1933 | - add_dependencies (${TEST_NAME} libindicatordisplayservice) |
1934 | - target_link_libraries (${TEST_NAME} indicatordisplayservice ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBRARIES} ${GMOCK_LIBRARIES}) |
1935 | -endfunction() |
1936 | -add_test_by_name(test-rotation-lock) |
1937 | - |
1938 | -add_test (cppcheck cppcheck --enable=all -q --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) |
1939 | - |
1940 | +add_compile_options(${CXX_WARNING_ARGS}) |
1941 | + |
1942 | +add_test(cppcheck cppcheck --enable=all -USCHEMA_DIR --error-exitcode=2 --inline-suppr -I${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/tests) |
1943 | + |
1944 | +add_subdirectory(integration) |
1945 | +add_subdirectory(unit) |
1946 | +add_subdirectory(utils) |
1947 | |
1948 | === added directory 'tests/integration' |
1949 | === added file 'tests/integration/CMakeLists.txt' |
1950 | --- tests/integration/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1951 | +++ tests/integration/CMakeLists.txt 2016-03-24 16:01:39 +0000 |
1952 | @@ -0,0 +1,24 @@ |
1953 | +set(SERVICE_LINK_LIBRARIES |
1954 | + ${SERVICE_LIB} |
1955 | + ${SERVICE_DEPS_LIBRARIES} |
1956 | +) |
1957 | +set(QT_LINK_LIBRARIES |
1958 | + test-utils |
1959 | + Qt5::Core |
1960 | + Qt5::Test |
1961 | + Qt5::DBus |
1962 | +) |
1963 | +set(TEST_LINK_LIBRARIES |
1964 | + ${TEST_DEPS_LIBRARIES} |
1965 | + ${GTEST_LIBRARIES} |
1966 | + ${GMOCK_LIBRARIES} |
1967 | +) |
1968 | + |
1969 | +function(add_qt_test_by_name name) |
1970 | + set(TEST_NAME ${name}) |
1971 | + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) |
1972 | + add_test(${TEST_NAME} ${TEST_NAME}) |
1973 | + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) |
1974 | + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES} ${THREAD_LINK_LIBRARIES}) |
1975 | +endfunction() |
1976 | +add_qt_test_by_name(usb-manager-test) |
1977 | |
1978 | === added file 'tests/integration/usb-manager-test.cpp' |
1979 | --- tests/integration/usb-manager-test.cpp 1970-01-01 00:00:00 +0000 |
1980 | +++ tests/integration/usb-manager-test.cpp 2016-03-24 16:01:39 +0000 |
1981 | @@ -0,0 +1,226 @@ |
1982 | +/* |
1983 | + * Copyright 2016 Canonical Ltd. |
1984 | + * |
1985 | + * This program is free software: you can redistribute it and/or modify it |
1986 | + * under the terms of the GNU General Public License version 3, as published |
1987 | + * by the Free Software Foundation. |
1988 | + * |
1989 | + * This program is distributed in the hope that it will be useful, but |
1990 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1991 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1992 | + * PURPOSE. See the GNU General Public License for more details. |
1993 | + * |
1994 | + * You should have received a copy of the GNU General Public License along |
1995 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1996 | + * |
1997 | + * Authors: |
1998 | + * Charles Kerr <charles.kerr@canonical.com> |
1999 | + */ |
2000 | + |
2001 | +#include <tests/utils/adbd-server.h> |
2002 | +#include <tests/utils/qt-fixture.h> |
2003 | +#include <tests/utils/mock-greeter.h> |
2004 | +#include <tests/utils/mock-usb-monitor.h> |
2005 | + |
2006 | +#include <src/dbus-names.h> |
2007 | +#include <src/usb-manager.h> |
2008 | + |
2009 | +#include <libqtdbustest/DBusTestRunner.h> |
2010 | +#include <libqtdbustest/QProcessDBusService.h> |
2011 | +#include <libqtdbusmock/DBusMock.h> |
2012 | + |
2013 | +#include <fstream> |
2014 | +#include <sstream> |
2015 | +#include <vector> |
2016 | + |
2017 | +/*** |
2018 | +**** |
2019 | +***/ |
2020 | + |
2021 | +class UsbManagerFixture: public QtFixture |
2022 | +{ |
2023 | + using super = QtFixture; |
2024 | + |
2025 | +public: |
2026 | + |
2027 | + UsbManagerFixture(): |
2028 | + dbusMock{dbusTestRunner} |
2029 | + { |
2030 | + dbusTestRunner.startServices(); |
2031 | + } |
2032 | + |
2033 | + ~UsbManagerFixture() =default; |
2034 | + |
2035 | +protected: |
2036 | + |
2037 | + static void file_deleter (std::string* s) |
2038 | + { |
2039 | + fprintf(stderr, "remove \"%s\"\n", s->c_str()); |
2040 | + g_remove(s->c_str()); |
2041 | + delete s; |
2042 | + } |
2043 | + |
2044 | + void SetUp() override |
2045 | + { |
2046 | + super::SetUp(); |
2047 | + |
2048 | + m_usb_monitor.reset(new MockUsbMonitor{}); |
2049 | + m_greeter.reset(new MockGreeter{}); |
2050 | + |
2051 | + char tmpl[] = {"usb-manager-test-XXXXXX"}; |
2052 | + m_tmpdir.reset(new std::string{g_mkdtemp(tmpl)}, file_deleter); |
2053 | + g_message("using tmpdir '%s'", m_tmpdir->c_str()); |
2054 | + |
2055 | + dbusMock.registerNotificationDaemon(); |
2056 | + dbusTestRunner.startServices(); |
2057 | + } |
2058 | + |
2059 | + OrgFreedesktopDBusMockInterface& notificationsMockInterface() |
2060 | + { |
2061 | + return dbusMock.mockInterface(DBusNames::Notify::NAME, |
2062 | + DBusNames::Notify::PATH, |
2063 | + DBusNames::Notify::INTERFACE, |
2064 | + QDBusConnection::SessionBus); |
2065 | + } |
2066 | + |
2067 | + QtDBusTest::DBusTestRunner dbusTestRunner; |
2068 | + QtDBusMock::DBusMock dbusMock; |
2069 | + std::shared_ptr<std::string> m_tmpdir; |
2070 | + std::shared_ptr<MockUsbMonitor> m_usb_monitor; |
2071 | + std::shared_ptr<MockGreeter> m_greeter; |
2072 | +}; |
2073 | + |
2074 | +TEST_F(UsbManagerFixture, Allow) |
2075 | +{ |
2076 | + const std::shared_ptr<std::string> socket_path {new std::string{*m_tmpdir+"/socket"}, file_deleter}; |
2077 | + const std::shared_ptr<std::string> public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter}; |
2078 | + |
2079 | + // add a signal spy to listen to the notification daemon |
2080 | + QSignalSpy notificationsSpy( |
2081 | + ¬ificationsMockInterface(), |
2082 | + SIGNAL(MethodCalled(const QString &, const QVariantList &)) |
2083 | + ); |
2084 | + |
2085 | + // start a mock AdbdServer ready to submit a request |
2086 | + const std::string public_key {"qAAAALUHllFjEZjl5jbS9ivjpQpaTNpibl28Re71D/S8sV3usNJTkbpvZYoVPfxtmHSNdCgLkWN6qcDZsHZqE/4myzmx/8Y/RqBy1oirudugi3YUUcJh7aWkY8lKQe9shCLTcrT7cFLZIJIidTvfmWTm0UcU+xmdPALze11I3lGo1Ty5KpCe9oP+qYM8suHbxhm78LKLlo0QJ2QqM8T5isr1pvoPHDgRb+mSESElG+xDIfPWA2BTu77/xk4EnXmOYfcuCr5akF3N4fRo/ACnYgXWDZFX2XdklBXyDj78lVlinF37xdMk7BMQh166X7UNkpH1uG2y5F6lUzyLg8SsFtRnJkw7eVe/gnJj3feQaFQbF5oVDhWhLMtWLtejhX6umvroVBVA4rynG4xEgs00K4u4ly8DUIIJYDO22Ml4myFR5CUm3lOlyitNdzYGh0utLXPq9oc8EbMVxM3i+O7PRxQw5Ul04X6K8GLiGUDV98DB+xYUqfEveq1BRnXi/ZrdPDhQ8Lfkg5xnLccPTFamAqutPtZXV6s7dXJInBTZf0NtBaWL0RdR2cOJBrpeBYkrc9yIyeqFLFdxr66rjaehjaa4pS4S+CD6PkGiIpPWSQtwNC4RlT10qTQ0/K9lRux2p0D8Z8ubUTFuh4kBScGUkN1OV3Z+7d7B+ghmBtZrrgleXsbehjRuKgEAAQA= foo@bar"}; |
2087 | + const std::string fingerprint {"12:23:5f:2d:8c:40:ae:1d:05:7b:ae:bd:88:8a:f0:80"}; |
2088 | + auto adbd_server = std::make_shared<GAdbdServer>(*socket_path, std::vector<std::string>{"PK"+public_key}); |
2089 | + |
2090 | + // set up a UsbManager to process the request |
2091 | + auto usb_manager = std::make_shared<UsbManager>(*socket_path, *public_keys_path, m_usb_monitor, m_greeter); |
2092 | + |
2093 | + // wait for the notification to show up, confirm it looks right |
2094 | + wait_for_signals(notificationsSpy, 1); |
2095 | + { |
2096 | + QVariantList const& call(notificationsSpy.at(0)); |
2097 | + EXPECT_EQ("Notify", call.at(0)); |
2098 | + |
2099 | + QVariantList const& args(call.at(1).toList()); |
2100 | + ASSERT_EQ(8, args.size()); |
2101 | + EXPECT_EQ("", args.at(0)); // app name |
2102 | + EXPECT_EQ(0, args.at(1)); // replaces-id |
2103 | + EXPECT_EQ("computer-symbolic", args.at(2)); // icon name |
2104 | + EXPECT_EQ("Allow USB Debugging?", args.at(3)); // summary |
2105 | + EXPECT_EQ(QString::fromUtf8("The computer's RSA key fingerprint is: ") + QString::fromUtf8(fingerprint.c_str()), args.at(4)); // body |
2106 | + EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Don't Allow"}), args.at(5)); // actions |
2107 | + EXPECT_EQ(-1, args.at(7)); |
2108 | + |
2109 | + QVariantMap hints; |
2110 | + ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); |
2111 | + ASSERT_EQ(3, hints.size()); |
2112 | + ASSERT_TRUE(hints.contains("x-canonical-private-affirmative-tint")); |
2113 | + ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); |
2114 | + ASSERT_TRUE(hints.contains("x-canonical-snap-decisions")); |
2115 | + } |
2116 | + notificationsSpy.clear(); |
2117 | + |
2118 | + // click on allow in the notification |
2119 | + notificationsMockInterface().EmitSignal( |
2120 | + DBusNames::Notify::INTERFACE, |
2121 | + DBusNames::Notify::ActionInvoked::NAME, |
2122 | + "us", |
2123 | + QVariantList() << uint32_t(1) << "allow" |
2124 | + ); |
2125 | + |
2126 | + // confirm that the AdbdServer got the right response |
2127 | + wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 2000); |
2128 | + ASSERT_EQ(1, adbd_server->m_responses.size()); |
2129 | + EXPECT_EQ("OK", adbd_server->m_responses.front()); |
2130 | + |
2131 | + // confirm that the public_keys file got the public key appended to it |
2132 | + std::ifstream ifkeys {*public_keys_path}; |
2133 | + std::vector<std::string> lines; |
2134 | + std::string line; |
2135 | + while(getline(ifkeys, line)) |
2136 | + lines.emplace_back(std::move(line)); |
2137 | + ASSERT_EQ(1, lines.size()); |
2138 | + EXPECT_EQ(public_key, lines[0]); |
2139 | +} |
2140 | + |
2141 | +TEST_F(UsbManagerFixture, USBDisconnectedDuringPrompt) |
2142 | +{ |
2143 | + const std::shared_ptr<std::string> socket_path {new std::string{*m_tmpdir+"/socket"}, file_deleter}; |
2144 | + const std::shared_ptr<std::string> public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter}; |
2145 | + |
2146 | + // start a mock AdbdServer ready to submit a request |
2147 | + const std::string public_key {"public_key"}; |
2148 | + auto adbd_server = std::make_shared<GAdbdServer>(*socket_path, std::vector<std::string>{"PK"+public_key}); |
2149 | + |
2150 | + // set up a UsbManager to process the request |
2151 | + auto usb_manager = std::make_shared<UsbManager>(*socket_path, *public_keys_path, m_usb_monitor, m_greeter); |
2152 | + |
2153 | + for (int i=0; i<3; i++) |
2154 | + { |
2155 | + // add a signal spy to listen to the notification daemon |
2156 | + QSignalSpy notificationsSpy( |
2157 | + ¬ificationsMockInterface(), |
2158 | + SIGNAL(MethodCalled(const QString &, const QVariantList &)) |
2159 | + ); |
2160 | + |
2161 | + // wait for a notification to show up |
2162 | + wait_for_signals(notificationsSpy, 1); |
2163 | + EXPECT_EQ("Notify", notificationsSpy.at(0).at(0)); |
2164 | + notificationsSpy.clear(); |
2165 | + |
2166 | + // wait for UsbSnap to receive dbusmock's response to the Notify request. |
2167 | + // there's no event to key off of for this, so just wait for a moment |
2168 | + wait_msec(); |
2169 | + |
2170 | + // disconnect the USB before the user has a chance to allow/deny |
2171 | + m_usb_monitor->m_on_usb_disconnected("android0"); |
2172 | + |
2173 | + // confirm that we requested the notification to be pulled down |
2174 | + wait_for_signals(notificationsSpy, 1); |
2175 | + EXPECT_EQ("CloseNotification", notificationsSpy.at(0).at(0)); |
2176 | + notificationsSpy.clear(); |
2177 | + } |
2178 | +} |
2179 | + |
2180 | +TEST_F(UsbManagerFixture, Greeter) |
2181 | +{ |
2182 | + const std::shared_ptr<std::string> socket_path {new std::string{*m_tmpdir+"/socket"}, file_deleter}; |
2183 | + const std::shared_ptr<std::string> public_keys_path {new std::string{*m_tmpdir+"/adb_keys"}, file_deleter}; |
2184 | + |
2185 | + // start a mock AdbdServer ready to submit a request |
2186 | + const std::string public_key {"public_key"}; |
2187 | + auto adbd_server = std::make_shared<GAdbdServer>(*socket_path, std::vector<std::string>{"PK"+public_key}); |
2188 | + |
2189 | + // set up a UsbManager to process the request |
2190 | + m_greeter->m_is_active.set(true); |
2191 | + auto usb_manager = std::make_shared<UsbManager>(*socket_path, *public_keys_path, m_usb_monitor, m_greeter); |
2192 | + |
2193 | + // add a signal spy to listen to the notification daemon |
2194 | + QSignalSpy notificationsSpy( |
2195 | + ¬ificationsMockInterface(), |
2196 | + SIGNAL(MethodCalled(const QString &, const QVariantList &)) |
2197 | + ); |
2198 | + |
2199 | + // the greeter is active, so the notification should not appear |
2200 | + EXPECT_FALSE(notificationsSpy.wait(2000)); |
2201 | + |
2202 | + // disable the greeter, the notification should appear |
2203 | + m_greeter->m_is_active.set(false); |
2204 | + wait_for_signals(notificationsSpy, 1); |
2205 | + EXPECT_EQ("Notify", notificationsSpy.at(0).at(0)); |
2206 | + notificationsSpy.clear(); |
2207 | +} |
2208 | |
2209 | === added directory 'tests/unit' |
2210 | === added file 'tests/unit/CMakeLists.txt' |
2211 | --- tests/unit/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
2212 | +++ tests/unit/CMakeLists.txt 2016-03-24 16:01:39 +0000 |
2213 | @@ -0,0 +1,34 @@ |
2214 | +set(SERVICE_LINK_LIBRARIES |
2215 | + ${SERVICE_LIB} |
2216 | + ${SERVICE_DEPS_LIBRARIES} |
2217 | +) |
2218 | +set(QT_LINK_LIBRARIES |
2219 | + test-utils |
2220 | + Qt5::Core |
2221 | + Qt5::Test |
2222 | + Qt5::DBus |
2223 | +) |
2224 | +set(TEST_LINK_LIBRARIES |
2225 | + ${TEST_DEPS_LIBRARIES} |
2226 | + ${GTEST_LIBRARIES} |
2227 | + ${GMOCK_LIBRARIES} |
2228 | +) |
2229 | + |
2230 | +function(add_test_by_name name) |
2231 | + set(TEST_NAME ${name}) |
2232 | + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) |
2233 | + add_test(${TEST_NAME} ${TEST_NAME}) |
2234 | + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) |
2235 | + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES} ${THREAD_LINK_LIBRARIES}) |
2236 | +endfunction() |
2237 | +add_test_by_name(adbd-client-test) |
2238 | +add_test_by_name(rotation-lock-test) |
2239 | + |
2240 | +function(add_qt_test_by_name name) |
2241 | + set(TEST_NAME ${name}) |
2242 | + add_executable (${TEST_NAME} ${TEST_NAME}.cpp) |
2243 | + add_test(${TEST_NAME} ${TEST_NAME}) |
2244 | + set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT ${CTEST_ENVIRONMENT}) |
2245 | + target_link_libraries(${TEST_NAME} ${SERVICE_LINK_LIBRARIES} ${QT_LINK_LIBRARIES} ${TEST_LINK_LIBRARIES} ${THREAD_LINK_LIBRARIES}) |
2246 | +endfunction() |
2247 | +add_qt_test_by_name(usb-snap-test) |
2248 | |
2249 | === added file 'tests/unit/adbd-client-test.cpp' |
2250 | --- tests/unit/adbd-client-test.cpp 1970-01-01 00:00:00 +0000 |
2251 | +++ tests/unit/adbd-client-test.cpp 2016-03-24 16:01:39 +0000 |
2252 | @@ -0,0 +1,95 @@ |
2253 | +/* |
2254 | + * Copyright 2016 Canonical Ltd. |
2255 | + * |
2256 | + * This program is free software: you can redistribute it and/or modify it |
2257 | + * under the terms of the GNU General Public License version 3, as published |
2258 | + * by the Free Software Foundation. |
2259 | + * |
2260 | + * This program is distributed in the hope that it will be useful, but |
2261 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2262 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2263 | + * PURPOSE. See the GNU General Public License for more details. |
2264 | + * |
2265 | + * You should have received a copy of the GNU General Public License along |
2266 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2267 | + * |
2268 | + * Authors: |
2269 | + * Charles Kerr <charles.kerr@canonical.com> |
2270 | + */ |
2271 | + |
2272 | +#include <tests/utils/test-dbus-fixture.h> |
2273 | +#include <tests/utils/adbd-server.h> |
2274 | + |
2275 | +#include <src/adbd-client.h> |
2276 | + |
2277 | +class AdbdClientFixture: public TestDBusFixture |
2278 | +{ |
2279 | +private: |
2280 | + typedef TestDBusFixture super; |
2281 | + |
2282 | +protected: |
2283 | + |
2284 | + static void file_deleter (std::string* s) |
2285 | + { |
2286 | + fprintf(stderr, "remove \"%s\"\n", s->c_str()); |
2287 | + g_remove(s->c_str()); |
2288 | + delete s; |
2289 | + } |
2290 | + |
2291 | + std::shared_ptr<std::string> m_tmpdir; |
2292 | + |
2293 | + void SetUp() |
2294 | + { |
2295 | + super::SetUp(); |
2296 | + |
2297 | + char tmpl[] = {"adb-client-test-XXXXXX"}; |
2298 | + m_tmpdir.reset(new std::string{g_mkdtemp(tmpl)}, file_deleter); |
2299 | + g_message("using tmpdir '%s'", m_tmpdir->c_str()); |
2300 | + } |
2301 | +}; |
2302 | + |
2303 | + |
2304 | +TEST_F(AdbdClientFixture, SocketPlumbing) |
2305 | +{ |
2306 | + struct { |
2307 | + const std::string request; |
2308 | + const std::string expected_pk; |
2309 | + AdbdClient::PKResponse response; |
2310 | + const std::string expected_response; |
2311 | + } tests[] = { |
2312 | + { "PKHelloWorld", "HelloWorld", AdbdClient::PKResponse::ALLOW, "OK" }, |
2313 | + { "PKHelloWorld", "HelloWorld", AdbdClient::PKResponse::DENY, "NO" }, |
2314 | + { "PKFooBar", "FooBar", AdbdClient::PKResponse::ALLOW, "OK" }, |
2315 | + { "PK", "", AdbdClient::PKResponse::DENY, "NO" } |
2316 | + }; |
2317 | + |
2318 | + const auto main_thread = g_thread_self(); |
2319 | + |
2320 | + const auto socket_path = *m_tmpdir + "/test-socket-plumbing"; |
2321 | + g_message("socket_path is %s", socket_path.c_str()); |
2322 | + |
2323 | + for (const auto& test : tests) |
2324 | + { |
2325 | + // start an AdbdClient that listens for PKRequests |
2326 | + std::string pk; |
2327 | + auto adbd_client = std::make_shared<GAdbdClient>(socket_path); |
2328 | + adbd_client->on_pk_request().connect([&pk, main_thread, test](const AdbdClient::PKRequest& req){ |
2329 | + EXPECT_EQ(main_thread, g_thread_self()); |
2330 | + g_message("in on_pk_request with %s", req.public_key.c_str()); |
2331 | + pk = req.public_key; |
2332 | + req.respond(test.response); |
2333 | + }); |
2334 | + |
2335 | + // start a mock AdbdServer with to fire test key requests and wait for a response |
2336 | + auto adbd_server = std::make_shared<GAdbdServer>(socket_path, std::vector<std::string>{test.request}); |
2337 | + wait_for([adbd_server](){return !adbd_server->m_responses.empty();}, 2000); |
2338 | + EXPECT_EQ(test.expected_pk, pk); |
2339 | + ASSERT_EQ(1, adbd_server->m_responses.size()); |
2340 | + EXPECT_EQ(test.expected_response, adbd_server->m_responses.front()); |
2341 | + |
2342 | + // cleanup |
2343 | + adbd_client.reset(); |
2344 | + adbd_server.reset(); |
2345 | + g_unlink(socket_path.c_str()); |
2346 | + } |
2347 | +} |
2348 | |
2349 | === renamed file 'tests/test-rotation-lock.cpp' => 'tests/unit/rotation-lock-test.cpp' |
2350 | --- tests/test-rotation-lock.cpp 2014-08-21 03:35:16 +0000 |
2351 | +++ tests/unit/rotation-lock-test.cpp 2016-03-24 16:01:39 +0000 |
2352 | @@ -17,14 +17,14 @@ |
2353 | * Charles Kerr <charles.kerr@canonical.com> |
2354 | */ |
2355 | |
2356 | -#include "gtestdbus-fixture.h" |
2357 | +#include <tests/utils/test-dbus-fixture.h> |
2358 | |
2359 | #include <src/rotation-lock.h> |
2360 | |
2361 | -class RotationLockFixture: public GTestDBusFixture |
2362 | +class RotationLockFixture: public TestDBusFixture |
2363 | { |
2364 | private: |
2365 | - typedef GTestDBusFixture super; |
2366 | + typedef TestDBusFixture super; |
2367 | |
2368 | protected: |
2369 | |
2370 | |
2371 | === added file 'tests/unit/usb-snap-test.cpp' |
2372 | --- tests/unit/usb-snap-test.cpp 1970-01-01 00:00:00 +0000 |
2373 | +++ tests/unit/usb-snap-test.cpp 2016-03-24 16:01:39 +0000 |
2374 | @@ -0,0 +1,143 @@ |
2375 | +/* |
2376 | + * Copyright 2016 Canonical Ltd. |
2377 | + * |
2378 | + * This program is free software: you can redistribute it and/or modify it |
2379 | + * under the terms of the GNU General Public License version 3, as published |
2380 | + * by the Free Software Foundation. |
2381 | + * |
2382 | + * This program is distributed in the hope that it will be useful, but |
2383 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2384 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2385 | + * PURPOSE. See the GNU General Public License for more details. |
2386 | + * |
2387 | + * You should have received a copy of the GNU General Public License along |
2388 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2389 | + * |
2390 | + * Authors: |
2391 | + * Charles Kerr <charles.kerr@canonical.com> |
2392 | + */ |
2393 | + |
2394 | +#include <tests/utils/qt-fixture.h> |
2395 | + |
2396 | +#include <src/dbus-names.h> |
2397 | +#include <src/usb-snap.h> |
2398 | + |
2399 | +#include <libqtdbustest/DBusTestRunner.h> |
2400 | +#include <libqtdbustest/QProcessDBusService.h> |
2401 | +#include <libqtdbusmock/DBusMock.h> |
2402 | + |
2403 | +class UsbSnapFixture: public QtFixture |
2404 | +{ |
2405 | + using super = QtFixture; |
2406 | + |
2407 | +public: |
2408 | + |
2409 | + UsbSnapFixture(): |
2410 | + dbusMock{dbusTestRunner} |
2411 | + { |
2412 | + dbusTestRunner.startServices(); |
2413 | + } |
2414 | + |
2415 | + ~UsbSnapFixture() =default; |
2416 | + |
2417 | +protected: |
2418 | + |
2419 | + void SetUp() override |
2420 | + { |
2421 | + super::SetUp(); |
2422 | + |
2423 | + dbusMock.registerNotificationDaemon(); |
2424 | + dbusTestRunner.startServices(); |
2425 | + } |
2426 | + |
2427 | + OrgFreedesktopDBusMockInterface& notificationsMockInterface() |
2428 | + { |
2429 | + return dbusMock.mockInterface(DBusNames::Notify::NAME, |
2430 | + DBusNames::Notify::PATH, |
2431 | + DBusNames::Notify::INTERFACE, |
2432 | + QDBusConnection::SessionBus); |
2433 | + } |
2434 | + |
2435 | + QtDBusTest::DBusTestRunner dbusTestRunner; |
2436 | + QtDBusMock::DBusMock dbusMock; |
2437 | +}; |
2438 | + |
2439 | +TEST_F(UsbSnapFixture, TestRoundTrip) |
2440 | +{ |
2441 | + struct { |
2442 | + const char* fingerprint; |
2443 | + const char* action_to_invoke; |
2444 | + const AdbdClient::PKResponse expected_response; |
2445 | + } tests[] = { |
2446 | + { "Fingerprint", "allow", AdbdClient::PKResponse::ALLOW }, |
2447 | + { "Fingerprint", "deny", AdbdClient::PKResponse::DENY } |
2448 | + }; |
2449 | + |
2450 | + uint32_t next_id = 1; |
2451 | + for(const auto& test : tests) |
2452 | + { |
2453 | + // Minor wart: we don't have a way of getting the fdo notification id |
2454 | + // from dbusmock so instead we copy its (simple) id generation here |
2455 | + const auto id = next_id++; |
2456 | + |
2457 | + QSignalSpy notificationsSpy( |
2458 | + ¬ificationsMockInterface(), |
2459 | + SIGNAL(MethodCalled(const QString &, const QVariantList &))); |
2460 | + |
2461 | + // start up a UsbSnap to ask about a fingerprint |
2462 | + auto snap = std::make_shared<UsbSnap>(test.fingerprint); |
2463 | + AdbdClient::PKResponse user_response {}; |
2464 | + bool user_response_set = false; |
2465 | + snap->on_user_response().connect([&user_response,&user_response_set](AdbdClient::PKResponse response, bool /*remember*/){ |
2466 | + user_response = response; |
2467 | + user_response_set = true; |
2468 | + }); |
2469 | + |
2470 | + // test that UsbSnap creates a fdo notification |
2471 | + wait_for_signals(notificationsSpy, 1); |
2472 | + { |
2473 | + QVariantList const& call(notificationsSpy.at(0)); |
2474 | + EXPECT_EQ("Notify", call.at(0)); |
2475 | + |
2476 | + QVariantList const& args(call.at(1).toList()); |
2477 | + ASSERT_EQ(8, args.size()); |
2478 | + EXPECT_EQ("", args.at(0)); // app name |
2479 | + EXPECT_EQ(0, args.at(1)); // replaces-id |
2480 | + EXPECT_EQ("computer-symbolic", args.at(2)); // icon name |
2481 | + EXPECT_EQ("Allow USB Debugging?", args.at(3)); // summary |
2482 | + EXPECT_EQ(QString::fromUtf8("The computer's RSA key fingerprint is: ") + test.fingerprint, args.at(4)); // body |
2483 | + EXPECT_EQ(QStringList({"allow", "Allow", "deny", "Don't Allow"}), args.at(5)); // actions |
2484 | + EXPECT_EQ(-1, args.at(7)); |
2485 | + |
2486 | + QVariantMap hints; |
2487 | + ASSERT_TRUE(qDBusArgumentToMap(args.at(6), hints)); |
2488 | + ASSERT_EQ(3, hints.size()); |
2489 | + ASSERT_TRUE(hints.contains("x-canonical-private-affirmative-tint")); |
2490 | + ASSERT_TRUE(hints.contains("x-canonical-non-shaped-icon")); |
2491 | + ASSERT_TRUE(hints.contains("x-canonical-snap-decisions")); |
2492 | + } |
2493 | + notificationsSpy.clear(); |
2494 | + |
2495 | + // fake a user interaction with the fdo notification |
2496 | + notificationsMockInterface().EmitSignal( |
2497 | + DBusNames::Notify::INTERFACE, |
2498 | + DBusNames::Notify::ActionInvoked::NAME, |
2499 | + "us", |
2500 | + QVariantList() << id << test.action_to_invoke); |
2501 | + |
2502 | + // test that UsbSnap emits on_user_response() as a result |
2503 | + wait_for([&user_response_set](){return user_response_set;}); |
2504 | + EXPECT_TRUE(user_response_set); |
2505 | + ASSERT_EQ(test.expected_response, user_response); |
2506 | + |
2507 | + // confirm that the snap dtor cleans up the notification |
2508 | + snap.reset(); |
2509 | + wait_for_signals(notificationsSpy, 1); |
2510 | + { |
2511 | + QVariantList const& call(notificationsSpy.at(0)); |
2512 | + EXPECT_EQ("CloseNotification", call.at(0)); |
2513 | + QVariantList const& args(call.at(1).toList()); |
2514 | + EXPECT_EQ(id, args.at(0)); |
2515 | + } |
2516 | + } |
2517 | +} |
2518 | |
2519 | === added directory 'tests/utils' |
2520 | === added file 'tests/utils/CMakeLists.txt' |
2521 | --- tests/utils/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
2522 | +++ tests/utils/CMakeLists.txt 2016-03-24 16:01:39 +0000 |
2523 | @@ -0,0 +1,17 @@ |
2524 | +include_directories( |
2525 | + ${CMAKE_CURRENT_BINARY_DIR} |
2526 | + ${CMAKE_CURRENT_SOURCE_DIR} |
2527 | +) |
2528 | + |
2529 | +add_library( |
2530 | + test-utils |
2531 | + STATIC |
2532 | + qmain.cpp |
2533 | +) |
2534 | + |
2535 | +qt5_use_modules( |
2536 | + test-utils |
2537 | + Core |
2538 | + DBus |
2539 | +) |
2540 | + |
2541 | |
2542 | === added file 'tests/utils/adbd-server.h' |
2543 | --- tests/utils/adbd-server.h 1970-01-01 00:00:00 +0000 |
2544 | +++ tests/utils/adbd-server.h 2016-03-24 16:01:39 +0000 |
2545 | @@ -0,0 +1,150 @@ |
2546 | +/* |
2547 | + * Copyright 2016 Canonical Ltd. |
2548 | + * |
2549 | + * This program is free software: you can redistribute it and/or modify it |
2550 | + * under the terms of the GNU General Public License version 3, as published |
2551 | + * by the Free Software Foundation. |
2552 | + * |
2553 | + * This program is distributed in the hope that it will be useful, but |
2554 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2555 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2556 | + * PURPOSE. See the GNU General Public License for more details. |
2557 | + * |
2558 | + * You should have received a copy of the GNU General Public License along |
2559 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2560 | + * |
2561 | + * Authors: |
2562 | + * Charles Kerr <charles.kerr@canonical.com> |
2563 | + */ |
2564 | + |
2565 | +#include <gio/gio.h> |
2566 | +#include <gio/gunixsocketaddress.h> |
2567 | + |
2568 | +#include <string> |
2569 | +#include <thread> |
2570 | +#include <vector> |
2571 | + |
2572 | + |
2573 | +/** |
2574 | + * A Mock ADBD server. |
2575 | + * |
2576 | + * Binds to a local domain socket, sends public key requests across it, |
2577 | + * and reads back the client's responses. |
2578 | + */ |
2579 | +class GAdbdServer |
2580 | +{ |
2581 | +public: |
2582 | + |
2583 | + GAdbdServer(const std::string& socket_path, |
2584 | + const std::vector<std::string>& requests): |
2585 | + m_requests{requests}, |
2586 | + m_socket_path{socket_path}, |
2587 | + m_cancellable{g_cancellable_new()}, |
2588 | + m_worker_thread{&GAdbdServer::worker_func, this} |
2589 | + { |
2590 | + } |
2591 | + |
2592 | + ~GAdbdServer() |
2593 | + { |
2594 | + // tell the worker thread to stop whatever it's doing and exit. |
2595 | + g_cancellable_cancel(m_cancellable); |
2596 | + m_worker_thread.join(); |
2597 | + g_clear_object(&m_cancellable); |
2598 | + } |
2599 | + |
2600 | + const std::vector<std::string> m_requests; |
2601 | + std::vector<std::string> m_responses; |
2602 | + |
2603 | +private: |
2604 | + |
2605 | + void worker_func() // runs in worker thread |
2606 | + { |
2607 | + auto server_socket = create_server_socket(m_socket_path); |
2608 | + auto requests = m_requests; |
2609 | + |
2610 | + GError* error {}; |
2611 | + g_socket_listen (server_socket, &error); |
2612 | + g_assert_no_error (error); |
2613 | + |
2614 | + while (!g_cancellable_is_cancelled(m_cancellable) && !requests.empty()) |
2615 | + { |
2616 | + // wait for a client connection |
2617 | + g_message("GAdbdServer::Impl::worker_func() calling g_socket_accept()"); |
2618 | + auto client_socket = g_socket_accept(server_socket, m_cancellable, &error); |
2619 | + if (error != nullptr) { |
2620 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
2621 | + g_message("GAdbdServer: Error accepting socket connection: %s", error->message); |
2622 | + g_clear_error(&error); |
2623 | + break; |
2624 | + } |
2625 | + |
2626 | + // pop the next request off the stack |
2627 | + auto request = requests.front(); |
2628 | + |
2629 | + // send the request |
2630 | + g_message("GAdbdServer::Impl::worker_func() sending req [%s]", request.c_str()); |
2631 | + g_socket_send(client_socket, |
2632 | + request.c_str(), |
2633 | + request.size(), |
2634 | + m_cancellable, |
2635 | + &error); |
2636 | + if (error != nullptr) { |
2637 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
2638 | + g_message("GAdbdServer: Error sending request: %s", error->message); |
2639 | + g_clear_error(&error); |
2640 | + g_clear_object(&client_socket); |
2641 | + break; |
2642 | + } |
2643 | + |
2644 | + // read the response |
2645 | + g_message("GAdbdServer::Impl::worker_func() reading response"); |
2646 | + char buf[4096]; |
2647 | + const auto n_bytes = g_socket_receive(client_socket, |
2648 | + buf, |
2649 | + sizeof(buf), |
2650 | + m_cancellable, |
2651 | + &error); |
2652 | + if (error != nullptr) { |
2653 | + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) |
2654 | + g_message("GAdbdServer: Error reading response: %s", error->message); |
2655 | + g_clear_error(&error); |
2656 | + g_clear_object(&client_socket); |
2657 | + continue; |
2658 | + } |
2659 | + const std::string response(buf, std::string::size_type(n_bytes)); |
2660 | + g_message("server read %d bytes, got response: '%s'", int(n_bytes), response.c_str()); |
2661 | + if (!response.empty()) { |
2662 | + m_responses.push_back(response); |
2663 | + requests.erase(requests.begin()); |
2664 | + } |
2665 | + |
2666 | + // cleanup |
2667 | + g_clear_object(&client_socket); |
2668 | + } |
2669 | + |
2670 | + g_clear_object(&server_socket); |
2671 | + } |
2672 | + |
2673 | + // bind to a local domain socket |
2674 | + static GSocket* create_server_socket(const std::string& socket_path) |
2675 | + { |
2676 | + GError* error {}; |
2677 | + auto socket = g_socket_new(G_SOCKET_FAMILY_UNIX, |
2678 | + G_SOCKET_TYPE_STREAM, |
2679 | + G_SOCKET_PROTOCOL_DEFAULT, |
2680 | + &error); |
2681 | + g_assert_no_error(error); |
2682 | + auto address = g_unix_socket_address_new (socket_path.c_str()); |
2683 | + g_socket_bind (socket, address, false, &error); |
2684 | + g_assert_no_error (error); |
2685 | + g_clear_object (&address); |
2686 | + |
2687 | + return socket; |
2688 | + } |
2689 | + |
2690 | + const std::string m_socket_path; |
2691 | + GCancellable* m_cancellable = nullptr; |
2692 | + std::thread m_worker_thread; |
2693 | +}; |
2694 | + |
2695 | + |
2696 | |
2697 | === added file 'tests/utils/dbus-types.h' |
2698 | --- tests/utils/dbus-types.h 1970-01-01 00:00:00 +0000 |
2699 | +++ tests/utils/dbus-types.h 2016-03-24 16:01:39 +0000 |
2700 | @@ -0,0 +1,42 @@ |
2701 | +/* |
2702 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
2703 | + * |
2704 | + * This program is free software: you can redistribute it and/or modify it |
2705 | + * under the terms of the GNU General Public License version 3, as published |
2706 | + * by the Free Software Foundation. |
2707 | + * |
2708 | + * This program is distributed in the hope that it will be useful, but |
2709 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2710 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2711 | + * PURPOSE. See the GNU General Public License for more details. |
2712 | + * |
2713 | + * You should have received a copy of the GNU General Public License along |
2714 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2715 | + * |
2716 | + * Author: Pete Woods <pete.woods@canonical.com> |
2717 | + */ |
2718 | + |
2719 | +#pragma once |
2720 | + |
2721 | +#include <QDBusMetaType> |
2722 | +#include <QtCore> |
2723 | +#include <QString> |
2724 | +#include <QVariantMap> |
2725 | + |
2726 | +typedef QMap<QString, QVariantMap> QVariantDictMap; |
2727 | +Q_DECLARE_METATYPE(QVariantDictMap) |
2728 | + |
2729 | +typedef QMap<QString, QString> QStringMap; |
2730 | +Q_DECLARE_METATYPE(QStringMap) |
2731 | + |
2732 | +namespace DBusTypes |
2733 | +{ |
2734 | + inline void registerMetaTypes() |
2735 | + { |
2736 | + qRegisterMetaType<QVariantDictMap>("QVariantDictMap"); |
2737 | + qRegisterMetaType<QStringMap>("QStringMap"); |
2738 | + |
2739 | + qDBusRegisterMetaType<QVariantDictMap>(); |
2740 | + qDBusRegisterMetaType<QStringMap>(); |
2741 | + } |
2742 | +} |
2743 | |
2744 | === renamed file 'tests/glib-fixture.h' => 'tests/utils/glib-fixture.h' |
2745 | --- tests/glib-fixture.h 2014-10-06 20:57:13 +0000 |
2746 | +++ tests/utils/glib-fixture.h 2016-03-24 16:01:39 +0000 |
2747 | @@ -1,5 +1,8 @@ |
2748 | /* |
2749 | - * Copyright 2014 Canonical Ltd. |
2750 | + * Copyright 2013 Canonical Ltd. |
2751 | + * |
2752 | + * Authors: |
2753 | + * Charles Kerr <charles.kerr@canonical.com> |
2754 | * |
2755 | * This program is free software: you can redistribute it and/or modify it |
2756 | * under the terms of the GNU General Public License version 3, as published |
2757 | @@ -12,15 +15,13 @@ |
2758 | * |
2759 | * You should have received a copy of the GNU General Public License along |
2760 | * with this program. If not, see <http://www.gnu.org/licenses/>. |
2761 | - * |
2762 | - * Authors: |
2763 | - * Charles Kerr <charles.kerr@canonical.com> |
2764 | */ |
2765 | |
2766 | -#ifndef INDICATOR_TESTS_GLIB_FIXTURE_H |
2767 | -#define INDICATOR_TESTS_GLIB_FIXTURE_H |
2768 | +#pragma once |
2769 | |
2770 | +#include <functional> // std::function |
2771 | #include <map> |
2772 | +#include <memory> // std::shared_ptr |
2773 | |
2774 | #include <glib.h> |
2775 | #include <glib/gstdio.h> |
2776 | @@ -32,81 +33,50 @@ |
2777 | |
2778 | class GlibFixture : public ::testing::Test |
2779 | { |
2780 | - private: |
2781 | - |
2782 | - GLogFunc realLogHandler; |
2783 | - |
2784 | - std::map<GLogLevelFlags,size_t> expected_log; |
2785 | - std::map<GLogLevelFlags,std::vector<std::string>> log; |
2786 | - |
2787 | - void test_log_counts() |
2788 | - { |
2789 | - const GLogLevelFlags levels_to_test[] = { G_LOG_LEVEL_ERROR, |
2790 | - G_LOG_LEVEL_CRITICAL, |
2791 | - G_LOG_LEVEL_MESSAGE, |
2792 | - G_LOG_LEVEL_WARNING }; |
2793 | - |
2794 | - for(const auto& level : levels_to_test) |
2795 | - { |
2796 | - const auto& v = log[level]; |
2797 | - const auto n = v.size(); |
2798 | - |
2799 | - EXPECT_EQ(expected_log[level], n); |
2800 | - |
2801 | - if (expected_log[level] != n) |
2802 | - for (size_t i=0; i<n; ++i) |
2803 | - g_print("%d %s\n", (n+1), v[i].c_str()); |
2804 | - } |
2805 | - |
2806 | - expected_log.clear(); |
2807 | - log.clear(); |
2808 | - } |
2809 | - |
2810 | - static void default_log_handler(const gchar * log_domain, |
2811 | - GLogLevelFlags log_level, |
2812 | - const gchar * message, |
2813 | - gpointer self) |
2814 | - { |
2815 | - char* tmp = g_strdup_printf ("%s:%d \"%s\"", log_domain, (int)log_level, message); |
2816 | - static_cast<GlibFixture*>(self)->log[log_level].push_back(tmp); |
2817 | - g_free(tmp); |
2818 | - } |
2819 | + public: |
2820 | + |
2821 | + virtual ~GlibFixture() =default; |
2822 | |
2823 | protected: |
2824 | |
2825 | - void increment_expected_errors(GLogLevelFlags level, size_t n=1) |
2826 | - { |
2827 | - expected_log[level] += n; |
2828 | - } |
2829 | - |
2830 | - virtual void SetUp() |
2831 | + virtual void SetUp() override |
2832 | { |
2833 | setlocale(LC_ALL, "C.UTF-8"); |
2834 | |
2835 | loop = g_main_loop_new(nullptr, false); |
2836 | |
2837 | - g_log_set_default_handler(default_log_handler, this); |
2838 | - |
2839 | +#ifdef SCHEMA_DIR |
2840 | + // only use local, temporary settings |
2841 | + g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true)); |
2842 | g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true)); |
2843 | + g_debug("SCHEMA_DIR is %s", SCHEMA_DIR); |
2844 | +#endif |
2845 | + |
2846 | + // fail on unexpected messages from this domain |
2847 | + g_log_set_fatal_mask(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING); |
2848 | |
2849 | g_unsetenv("DISPLAY"); |
2850 | + |
2851 | } |
2852 | |
2853 | - virtual void TearDown() |
2854 | + virtual void TearDown() override |
2855 | { |
2856 | - test_log_counts(); |
2857 | - |
2858 | - g_log_set_default_handler(realLogHandler, this); |
2859 | + g_test_assert_expected_messages (); |
2860 | |
2861 | g_clear_pointer(&loop, g_main_loop_unref); |
2862 | } |
2863 | |
2864 | + void expectLogMessage (const gchar *domain, GLogLevelFlags level, const gchar *pattern) |
2865 | + { |
2866 | + g_test_expect_message (domain, level, pattern); |
2867 | + } |
2868 | + |
2869 | private: |
2870 | |
2871 | static gboolean |
2872 | wait_for_signal__timeout(gpointer name) |
2873 | { |
2874 | - g_error("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name); |
2875 | + g_error("%s: timed out waiting for signal '%s'", G_STRLOC, static_cast<const char*>(name)); |
2876 | return G_SOURCE_REMOVE; |
2877 | } |
2878 | |
2879 | @@ -120,7 +90,7 @@ |
2880 | protected: |
2881 | |
2882 | /* convenience func to loop while waiting for a GObject's signal */ |
2883 | - void wait_for_signal(gpointer o, const gchar * signal, const guint timeout_seconds=5) |
2884 | + void wait_for_signal(gpointer o, const gchar * signal, guint timeout_seconds=5) |
2885 | { |
2886 | // wait for the signal or for timeout, whichever comes first |
2887 | const auto handler_id = g_signal_connect_swapped(o, signal, |
2888 | @@ -142,11 +112,92 @@ |
2889 | g_source_remove(id); |
2890 | } |
2891 | |
2892 | + bool wait_for(std::function<bool()> test_function, guint timeout_msec=1000) |
2893 | + { |
2894 | + auto timer = std::shared_ptr<GTimer>(g_timer_new(), [](GTimer* t){g_timer_destroy(t);}); |
2895 | + const auto timeout_sec = timeout_msec / 1000.0; |
2896 | + for (;;) { |
2897 | + if (test_function()) |
2898 | + return true; |
2899 | + //g_message("%f ... %f", g_timer_elapsed(timer.get(), nullptr), timeout_sec); |
2900 | + if (g_timer_elapsed(timer.get(), nullptr) >= timeout_sec) |
2901 | + return false; |
2902 | + wait_msec(); |
2903 | + } |
2904 | + } |
2905 | + |
2906 | + bool wait_for_name_owned(GDBusConnection* connection, |
2907 | + const gchar* name, |
2908 | + guint timeout_msec=1000, |
2909 | + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) |
2910 | + { |
2911 | + struct Data { |
2912 | + GMainLoop* loop = nullptr; |
2913 | + bool owned = false; |
2914 | + }; |
2915 | + Data data; |
2916 | + |
2917 | + auto on_name_appeared = [](GDBusConnection* /*connection*/, |
2918 | + const gchar* /*name_*/, |
2919 | + const gchar* name_owner, |
2920 | + gpointer gdata) |
2921 | + { |
2922 | + if (name_owner == nullptr) |
2923 | + return; |
2924 | + auto tmp = static_cast<Data*>(gdata); |
2925 | + tmp->owned = true; |
2926 | + g_main_loop_quit(tmp->loop); |
2927 | + }; |
2928 | + |
2929 | + const auto timeout_id = g_timeout_add(timeout_msec, wait_msec__timeout, loop); |
2930 | + data.loop = loop; |
2931 | + const auto watch_id = g_bus_watch_name_on_connection(connection, |
2932 | + name, |
2933 | + flags, |
2934 | + on_name_appeared, |
2935 | + nullptr, /* name_vanished */ |
2936 | + &data, |
2937 | + nullptr); /* user_data_free_func */ |
2938 | + g_main_loop_run(loop); |
2939 | + |
2940 | + g_bus_unwatch_name(watch_id); |
2941 | + g_source_remove(timeout_id); |
2942 | + |
2943 | + return data.owned; |
2944 | + } |
2945 | + |
2946 | + void EXPECT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection, |
2947 | + const gchar* name, |
2948 | + guint timeout_msec=1000, |
2949 | + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) |
2950 | + { |
2951 | + EXPECT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; |
2952 | + } |
2953 | + |
2954 | + void EXPECT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection, |
2955 | + const gchar* name, |
2956 | + guint timeout_msec=1000, |
2957 | + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) |
2958 | + { |
2959 | + EXPECT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; |
2960 | + } |
2961 | + |
2962 | + void ASSERT_NAME_OWNED_EVENTUALLY(GDBusConnection* connection, |
2963 | + const gchar* name, |
2964 | + guint timeout_msec=1000, |
2965 | + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) |
2966 | + { |
2967 | + ASSERT_TRUE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; |
2968 | + } |
2969 | + |
2970 | + void ASSERT_NAME_NOT_OWNED_EVENTUALLY(GDBusConnection* connection, |
2971 | + const gchar* name, |
2972 | + guint timeout_msec=1000, |
2973 | + GBusNameWatcherFlags flags=G_BUS_NAME_WATCHER_FLAGS_AUTO_START) |
2974 | + { |
2975 | + ASSERT_FALSE(wait_for_name_owned(connection, name, timeout_msec, flags)) << "name: " << name; |
2976 | + } |
2977 | + |
2978 | GMainLoop * loop; |
2979 | - |
2980 | - public: |
2981 | - |
2982 | - virtual ~GlibFixture() =default; |
2983 | }; |
2984 | |
2985 | -#endif /* INDICATOR_TESTS_GLIB_FIXTURE_H */ |
2986 | |
2987 | === added file 'tests/utils/gtest-qt-print-helpers.h' |
2988 | --- tests/utils/gtest-qt-print-helpers.h 1970-01-01 00:00:00 +0000 |
2989 | +++ tests/utils/gtest-qt-print-helpers.h 2016-03-24 16:01:39 +0000 |
2990 | @@ -0,0 +1,45 @@ |
2991 | + |
2992 | +#pragma once |
2993 | + |
2994 | +#include <QDBusObjectPath> |
2995 | +#include <QString> |
2996 | +#include <QStringList> |
2997 | +#include <QVariant> |
2998 | + |
2999 | +inline QString qVariantToString(const QVariant& variant) { |
3000 | + QString output; |
3001 | + QDebug dbg(&output); |
3002 | + dbg << variant; |
3003 | + return output; |
3004 | +} |
3005 | + |
3006 | +inline void PrintTo(const QVariant& variant, std::ostream* os) { |
3007 | + QString output; |
3008 | + QDebug dbg(&output); |
3009 | + dbg << variant; |
3010 | + |
3011 | + *os << "QVariant(" << output.toStdString() << ")"; |
3012 | +} |
3013 | + |
3014 | +inline void PrintTo(const QString& s, std::ostream* os) { |
3015 | + *os << "\"" << s.toStdString() << "\""; |
3016 | +} |
3017 | + |
3018 | +inline void PrintTo(const QStringList& list, std::ostream* os) { |
3019 | + QString output; |
3020 | + QDebug dbg(&output); |
3021 | + dbg << list; |
3022 | + |
3023 | + *os << "QStringList(" << output.toStdString() << ")"; |
3024 | +} |
3025 | + |
3026 | +inline void PrintTo(const QList<QDBusObjectPath>& list, std::ostream* os) { |
3027 | + QString output; |
3028 | + for (const auto& path: list) |
3029 | + { |
3030 | + output.append("\"" + path.path() + "\","); |
3031 | + } |
3032 | + |
3033 | + *os << "QList<QDBusObjectPath>(" << output.toStdString() << ")"; |
3034 | +} |
3035 | + |
3036 | |
3037 | === added file 'tests/utils/mock-greeter.h' |
3038 | --- tests/utils/mock-greeter.h 1970-01-01 00:00:00 +0000 |
3039 | +++ tests/utils/mock-greeter.h 2016-03-24 16:01:39 +0000 |
3040 | @@ -0,0 +1,32 @@ |
3041 | +/* |
3042 | + * Copyright 2016 Canonical Ltd. |
3043 | + * |
3044 | + * This program is free software: you can redistribute it and/or modify it |
3045 | + * under the terms of the GNU General Public License version 3, as published |
3046 | + * by the Free Software Foundation. |
3047 | + * |
3048 | + * This program is distributed in the hope that it will be useful, but |
3049 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3050 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3051 | + * PURPOSE. See the GNU General Public License for more details. |
3052 | + * |
3053 | + * You should have received a copy of the GNU General Public License along |
3054 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
3055 | + * |
3056 | + * Authors: |
3057 | + * Charles Kerr <charles.kerr@canonical.com> |
3058 | + */ |
3059 | + |
3060 | +#pragma once |
3061 | + |
3062 | +#include <src/greeter.h> |
3063 | + |
3064 | +class MockGreeter: public Greeter |
3065 | +{ |
3066 | +public: |
3067 | + MockGreeter() =default; |
3068 | + virtual ~MockGreeter() =default; |
3069 | + core::Property<bool>& is_active() override {return m_is_active;} |
3070 | + core::Property<bool> m_is_active {false}; |
3071 | +}; |
3072 | + |
3073 | |
3074 | === added file 'tests/utils/mock-usb-monitor.h' |
3075 | --- tests/utils/mock-usb-monitor.h 1970-01-01 00:00:00 +0000 |
3076 | +++ tests/utils/mock-usb-monitor.h 2016-03-24 16:01:39 +0000 |
3077 | @@ -0,0 +1,32 @@ |
3078 | +/* |
3079 | + * Copyright 2016 Canonical Ltd. |
3080 | + * |
3081 | + * This program is free software: you can redistribute it and/or modify it |
3082 | + * under the terms of the GNU General Public License version 3, as published |
3083 | + * by the Free Software Foundation. |
3084 | + * |
3085 | + * This program is distributed in the hope that it will be useful, but |
3086 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3087 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3088 | + * PURPOSE. See the GNU General Public License for more details. |
3089 | + * |
3090 | + * You should have received a copy of the GNU General Public License along |
3091 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
3092 | + * |
3093 | + * Authors: |
3094 | + * Charles Kerr <charles.kerr@canonical.com> |
3095 | + */ |
3096 | + |
3097 | +#pragma once |
3098 | + |
3099 | +#include <src/usb-monitor.h> |
3100 | + |
3101 | +class MockUsbMonitor: public UsbMonitor |
3102 | +{ |
3103 | +public: |
3104 | + MockUsbMonitor() =default; |
3105 | + virtual ~MockUsbMonitor() =default; |
3106 | + core::Signal<const std::string&>& on_usb_disconnected() override {return m_on_usb_disconnected;} |
3107 | + core::Signal<const std::string&> m_on_usb_disconnected; |
3108 | +}; |
3109 | + |
3110 | |
3111 | === added file 'tests/utils/qmain.cpp' |
3112 | --- tests/utils/qmain.cpp 1970-01-01 00:00:00 +0000 |
3113 | +++ tests/utils/qmain.cpp 2016-03-24 16:01:39 +0000 |
3114 | @@ -0,0 +1,60 @@ |
3115 | +/* |
3116 | + * Copyright © 2014 Canonical Ltd. |
3117 | + * |
3118 | + * This program is free software: you can redistribute it and/or modify it |
3119 | + * under the terms of the GNU Lesser General Public License version 3, |
3120 | + * as published by the Free Software Foundation. |
3121 | + * |
3122 | + * This program is distributed in the hope that it will be useful, |
3123 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3124 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3125 | + * GNU Lesser General Public License for more details. |
3126 | + * |
3127 | + * You should have received a copy of the GNU Lesser General Public License |
3128 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3129 | + * |
3130 | + * Authors: |
3131 | + * Pete Woods <pete.woods@canonical.com> |
3132 | + */ |
3133 | + |
3134 | +//#include <config.h> |
3135 | + |
3136 | +#include <QCoreApplication> |
3137 | +#include <QTimer> |
3138 | +#include <gtest/gtest.h> |
3139 | + |
3140 | +#include <libqtdbusmock/DBusMock.h> |
3141 | + |
3142 | +using namespace QtDBusMock; |
3143 | + |
3144 | +class Runner: public QObject |
3145 | +{ |
3146 | + Q_OBJECT |
3147 | +public Q_SLOTS: |
3148 | + void run() |
3149 | + { |
3150 | + QCoreApplication::exit(RUN_ALL_TESTS()); |
3151 | + } |
3152 | +}; |
3153 | + |
3154 | +int main(int argc, char **argv) |
3155 | +{ |
3156 | + qputenv("LANG", "C.UTF-8"); |
3157 | + unsetenv("LC_ALL"); |
3158 | + |
3159 | + // boilerplate i18n |
3160 | + setlocale(LC_ALL, ""); |
3161 | + bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); |
3162 | + textdomain(GETTEXT_PACKAGE); |
3163 | + |
3164 | + QCoreApplication application(argc, argv); |
3165 | + DBusMock::registerMetaTypes(); |
3166 | + ::testing::InitGoogleTest(&argc, argv); |
3167 | + |
3168 | + Runner runner; |
3169 | + QTimer::singleShot(0, &runner, SLOT(run())); |
3170 | + |
3171 | + return application.exec(); |
3172 | +} |
3173 | + |
3174 | +#include "qmain.moc" |
3175 | |
3176 | === added file 'tests/utils/qt-fixture.h' |
3177 | --- tests/utils/qt-fixture.h 1970-01-01 00:00:00 +0000 |
3178 | +++ tests/utils/qt-fixture.h 2016-03-24 16:01:39 +0000 |
3179 | @@ -0,0 +1,74 @@ |
3180 | +/* |
3181 | + * Copyright 2016 Canonical Ltd. |
3182 | + * |
3183 | + * This program is free software: you can redistribute it and/or modify it |
3184 | + * under the terms of the GNU General Public License version 3, as published |
3185 | + * by the Free Software Foundation. |
3186 | + * |
3187 | + * This program is distributed in the hope that it will be useful, but |
3188 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
3189 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3190 | + * PURPOSE. See the GNU General Public License for more details. |
3191 | + * |
3192 | + * You should have received a copy of the GNU General Public License along |
3193 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
3194 | + * |
3195 | + * Authors: |
3196 | + * Charles Kerr <charles.kerr@canonical.com> |
3197 | + */ |
3198 | + |
3199 | +#pragma once |
3200 | + |
3201 | +#define QT_NO_KEYWORDS |
3202 | + |
3203 | +#include <tests/utils/dbus-types.h> |
3204 | +#include <tests/utils/glib-fixture.h> |
3205 | +#include <tests/utils/gtest-qt-print-helpers.h> |
3206 | + |
3207 | +#include <gtest/gtest.h> |
3208 | + |
3209 | +#include <QDBusArgument> |
3210 | +#include <QVariant> |
3211 | +#include <QSignalSpy> |
3212 | + |
3213 | +#define wait_for_signals(signalSpy,signalsExpected) \ |
3214 | +{ \ |
3215 | + while (signalSpy.size() < signalsExpected) \ |
3216 | + { \ |
3217 | + ASSERT_TRUE(signalSpy.wait()); \ |
3218 | + } \ |
3219 | + \ |
3220 | + ASSERT_EQ(signalsExpected, signalSpy.size()); \ |
3221 | +} |
3222 | + |
3223 | +class QtFixture: public GlibFixture |
3224 | +{ |
3225 | + using super = GlibFixture; |
3226 | + |
3227 | +public: |
3228 | + |
3229 | + QtFixture() |
3230 | + { |
3231 | + DBusTypes::registerMetaTypes(); |
3232 | + } |
3233 | + |
3234 | + ~QtFixture() =default; |
3235 | + |
3236 | +protected: |
3237 | + |
3238 | + bool qDBusArgumentToMap(QVariant const& variant, QVariantMap& map) |
3239 | + { |
3240 | + if (variant.canConvert<QDBusArgument>()) |
3241 | + { |
3242 | + QDBusArgument value(variant.value<QDBusArgument>()); |
3243 | + if (value.currentType() == QDBusArgument::MapType) |
3244 | + { |
3245 | + value >> map; |
3246 | + return true; |
3247 | + } |
3248 | + } |
3249 | + |
3250 | + return false; |
3251 | + } |
3252 | +}; |
3253 | + |
3254 | |
3255 | === renamed file 'tests/gtestdbus-fixture.h' => 'tests/utils/test-dbus-fixture.h' |
3256 | --- tests/gtestdbus-fixture.h 2014-10-06 19:54:15 +0000 |
3257 | +++ tests/utils/test-dbus-fixture.h 2016-03-24 16:01:39 +0000 |
3258 | @@ -17,8 +17,7 @@ |
3259 | * Charles Kerr <charles.kerr@canonical.com> |
3260 | */ |
3261 | |
3262 | -#ifndef INDICATOR_TESTS_GTESTDBUS_FIXTURE_H |
3263 | -#define INDICATOR_TESTS_GTESTDBUS_FIXTURE_H |
3264 | +#pragma once |
3265 | |
3266 | #include "glib-fixture.h" |
3267 | |
3268 | @@ -26,14 +25,14 @@ |
3269 | **** |
3270 | ***/ |
3271 | |
3272 | -class GTestDBusFixture: public GlibFixture |
3273 | +class TestDBusFixture: public GlibFixture |
3274 | { |
3275 | public: |
3276 | |
3277 | - GTestDBusFixture() =default; |
3278 | - virtual ~GTestDBusFixture() =default; |
3279 | + TestDBusFixture() =default; |
3280 | + virtual ~TestDBusFixture() =default; |
3281 | |
3282 | - explicit GTestDBusFixture(const std::vector<std::string>& service_dirs_in): service_dirs(service_dirs_in) {} |
3283 | + explicit TestDBusFixture(const std::vector<std::string>& service_dirs_in): service_dirs(service_dirs_in) {} |
3284 | |
3285 | private: |
3286 | |
3287 | @@ -42,10 +41,10 @@ |
3288 | static void |
3289 | on_bus_opened (GObject* /*object*/, GAsyncResult * res, gpointer gself) |
3290 | { |
3291 | - auto self = static_cast<GTestDBusFixture*>(gself); |
3292 | + auto self = static_cast<TestDBusFixture*>(gself); |
3293 | |
3294 | GError * err = 0; |
3295 | - self->bus = g_bus_get_finish (res, &err); |
3296 | + self->system_bus = g_bus_get_finish (res, &err); |
3297 | g_assert_no_error (err); |
3298 | |
3299 | g_main_loop_quit (self->loop); |
3300 | @@ -54,10 +53,10 @@ |
3301 | static void |
3302 | on_bus_closed (GObject* /*object*/, GAsyncResult * res, gpointer gself) |
3303 | { |
3304 | - auto self = static_cast<GTestDBusFixture*>(gself); |
3305 | + auto self = static_cast<TestDBusFixture*>(gself); |
3306 | |
3307 | GError * err = 0; |
3308 | - g_dbus_connection_close_finish (self->bus, res, &err); |
3309 | + g_dbus_connection_close_finish (self->system_bus, res, &err); |
3310 | g_assert_no_error (err); |
3311 | |
3312 | g_main_loop_quit (self->loop); |
3313 | @@ -66,10 +65,10 @@ |
3314 | protected: |
3315 | |
3316 | GTestDBus * test_dbus = nullptr; |
3317 | - GDBusConnection * bus = nullptr; |
3318 | + GDBusConnection * system_bus = nullptr; |
3319 | const std::vector<std::string> service_dirs; |
3320 | |
3321 | - virtual void SetUp () |
3322 | + virtual void SetUp() override |
3323 | { |
3324 | super::SetUp (); |
3325 | |
3326 | @@ -88,14 +87,14 @@ |
3327 | g_main_loop_run (loop); |
3328 | } |
3329 | |
3330 | - virtual void TearDown () |
3331 | + virtual void TearDown() override |
3332 | { |
3333 | wait_msec(); |
3334 | |
3335 | // close the system bus |
3336 | - g_dbus_connection_close(bus, nullptr, on_bus_closed, this); |
3337 | + g_dbus_connection_close(system_bus, nullptr, on_bus_closed, this); |
3338 | g_main_loop_run(loop); |
3339 | - g_clear_object(&bus); |
3340 | + g_clear_object(&system_bus); |
3341 | |
3342 | // tear down the test dbus |
3343 | g_test_dbus_down(test_dbus); |
3344 | @@ -105,4 +104,3 @@ |
3345 | } |
3346 | }; |
3347 | |
3348 | -#endif /* INDICATOR_TESTS_GTESTDBUS_FIXTURE_H */ |
FAILED: Continuous integration, rev:37 jenkins. qa.ubuntu. com/job/ indicator- display- ci/22/ jenkins. qa.ubuntu. com/job/ indicator- display- wily-amd64- ci/5/console jenkins. qa.ubuntu. com/job/ indicator- display- wily-armhf- ci/5/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/indicator- display- ci/22/rebuild
http://