OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/extensions/app_host/binaries_installer.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/threading/platform_thread.h" | |
9 #include "base/win/scoped_bstr.h" | |
10 #include "base/win/scoped_com_initializer.h" | |
11 #include "base/win/scoped_comptr.h" | |
12 #include "google_update/google_update_idl.h" | |
13 | |
14 | |
15 namespace app_host { | |
16 | |
17 // Helpers -------------------------------------------------------------------- | |
18 | |
19 namespace { | |
20 | |
21 const wchar_t kAppHostAppId[] = L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}"; | |
22 const wchar_t kBinariesAppId[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; | |
23 const int kInstallationPollingIntervalMs = 50; | |
24 | |
25 HRESULT CreateInstalledApp(IAppBundle* app_bundle, | |
26 const wchar_t* app_guid, | |
27 IApp** app) { | |
28 base::win::ScopedComPtr<IDispatch> idispatch; | |
29 HRESULT hr = app_bundle->createInstalledApp(base::win::ScopedBstr(app_guid), | |
30 idispatch.Receive()); | |
31 if (FAILED(hr)) { | |
32 LOG(ERROR) << "Failed to configure App Bundle: " << hr; | |
33 return hr; | |
34 } | |
35 | |
36 base::win::ScopedComPtr<IApp> temp_app; | |
37 hr = temp_app.QueryFrom(idispatch); | |
38 if (FAILED(hr)) { | |
39 LOG(ERROR) << "Unexpected error querying IApp from " | |
40 << "IAppBundle->createInstalledApp return value: " << hr; | |
41 } else { | |
42 *app = temp_app.Detach(); | |
43 } | |
44 return hr; | |
45 } | |
46 | |
47 HRESULT GetAppHostApValue(IGoogleUpdate3* update3, | |
48 IAppBundle* app_bundle, | |
49 BSTR* ap_value) { | |
50 base::win::ScopedComPtr<IApp> app; | |
51 HRESULT hr = CreateInstalledApp(app_bundle, kAppHostAppId, app.Receive()); | |
52 if (FAILED(hr)) | |
53 return hr; | |
54 | |
55 hr = app->get_ap(ap_value); | |
56 if (FAILED(hr)) | |
57 LOG(ERROR) << "Failed to get the App Launcher AP value."; | |
58 return hr; | |
59 } | |
60 | |
61 HRESULT GetCurrentState(IApp* app, | |
62 ICurrentState** current_state, | |
63 CurrentState* state_value) { | |
64 base::win::ScopedComPtr<IDispatch> idispatch; | |
65 HRESULT hr = app->get_currentState(idispatch.Receive()); | |
66 if (FAILED(hr)) { | |
67 LOG(ERROR) << "Failed to get App Bundle state: " << hr; | |
68 return hr; | |
69 } | |
70 | |
71 base::win::ScopedComPtr<ICurrentState> temp_current_state; | |
72 hr = temp_current_state.QueryFrom(idispatch); | |
73 if (FAILED(hr)) { | |
74 LOG(ERROR) << "Unexpected error querying ICurrentState from " | |
75 << "IApp::get_currentState return value: " << hr; | |
76 return hr; | |
77 } | |
78 | |
79 LONG long_state_value; | |
80 hr = temp_current_state->get_stateValue(&long_state_value); | |
81 if (SUCCEEDED(hr)) { | |
82 *state_value = static_cast<CurrentState>(long_state_value); | |
83 *current_state = temp_current_state.Detach(); | |
84 } else { | |
85 LOG(ERROR) << "Failed to get App Bundle state value: " << hr; | |
86 } | |
87 return hr; | |
88 } | |
89 | |
90 bool CheckIsBusy(IAppBundle* app_bundle, HRESULT* hr) { | |
91 VARIANT_BOOL variant_is_busy = VARIANT_TRUE; | |
92 *hr = app_bundle->isBusy(&variant_is_busy); | |
93 if (FAILED(*hr)) | |
94 LOG(ERROR) << "Failed to check app_bundle->isBusy: " << *hr; | |
95 return (variant_is_busy == VARIANT_TRUE); | |
96 } | |
97 | |
98 void OnUpdateAvailable(IAppBundle* app_bundle, HRESULT* hr) { | |
99 // If the app bundle is busy we will just wait some more. | |
100 if (CheckIsBusy(app_bundle, hr) || FAILED(*hr)) | |
101 return; | |
102 *hr = app_bundle->download(); | |
103 if (FAILED(*hr)) | |
104 LOG(ERROR) << "Failed to initiate bundle download: " << *hr; | |
105 } | |
106 | |
107 void OnReadyToInstall(IAppBundle* app_bundle, HRESULT* hr) { | |
108 // If the app bundle is busy we will just wait some more. | |
109 if (CheckIsBusy(app_bundle, hr) || FAILED(*hr)) | |
110 return; | |
111 *hr = app_bundle->install(); | |
112 if (FAILED(*hr)) | |
113 LOG(ERROR) << "Failed to initiate bundle install: " << *hr; | |
114 } | |
115 | |
116 HRESULT OnError(ICurrentState* current_state) { | |
117 LONG error_code; | |
118 HRESULT hr = current_state->get_errorCode(&error_code); | |
119 if (FAILED(hr)) { | |
120 LOG(ERROR) << "Failed to retrieve bundle error code: " << hr; | |
121 return hr; | |
122 } | |
123 | |
124 base::win::ScopedBstr completion_message; | |
125 HRESULT completion_message_hr = | |
126 current_state->get_completionMessage(completion_message.Receive()); | |
127 if (FAILED(completion_message_hr)) { | |
128 LOG(ERROR) << "Bundle installation failed with error " << error_code | |
129 << ". Error message retrieval failed with error: " | |
130 << completion_message_hr; | |
131 } else { | |
132 LOG(ERROR) << "Bundle installation failed with error " << error_code << ": " | |
133 << completion_message; | |
134 } | |
135 return error_code; | |
136 } | |
137 | |
138 HRESULT CreateGoogleUpdate3(IGoogleUpdate3** update3) { | |
139 base::win::ScopedComPtr<IGoogleUpdate3> temp_update3; | |
140 HRESULT hr = temp_update3.CreateInstance(CLSID_GoogleUpdate3UserClass); | |
141 if (SUCCEEDED(hr)) { | |
142 *update3 = temp_update3.Detach(); | |
143 } else { | |
144 // TODO(erikwright): Try in-proc to support running elevated? According | |
145 // to update3_utils.cc (CreateGoogleUpdate3UserClass): | |
146 // The primary reason for the LocalServer activation failing on Vista/Win7 | |
147 // is that COM does not look at HKCU registration when the code is running | |
148 // elevated. We fall back to an in-proc mode. The in-proc mode is limited to | |
149 // one install at a time, so we use it only as a backup mechanism. | |
150 LOG(ERROR) << "Failed to instantiate GoogleUpdate3: " << hr; | |
151 } | |
152 return hr; | |
153 } | |
154 | |
155 HRESULT CreateAppBundle(IGoogleUpdate3* update3, IAppBundle** app_bundle) { | |
156 base::win::ScopedComPtr<IDispatch> idispatch; | |
157 HRESULT hr = update3->createAppBundle(idispatch.Receive()); | |
158 if (FAILED(hr)) { | |
159 LOG(ERROR) << "Failed to createAppBundle: " << hr; | |
160 return hr; | |
161 } | |
162 | |
163 base::win::ScopedComPtr<IAppBundle> temp_app_bundle; | |
164 hr = temp_app_bundle.QueryFrom(idispatch); | |
165 if (FAILED(hr)) { | |
166 LOG(ERROR) << "Unexpected error querying IAppBundle from " | |
167 << "IGoogleUpdate3->createAppBundle return value: " << hr; | |
168 return hr; | |
169 } | |
170 | |
171 hr = temp_app_bundle->initialize(); | |
172 if (FAILED(hr)) | |
173 LOG(ERROR) << "Failed to initialize App Bundle: " << hr; | |
174 else | |
175 *app_bundle = temp_app_bundle.Detach(); | |
176 return hr; | |
177 } | |
178 | |
179 HRESULT SelectBinariesApValue(IGoogleUpdate3* update3, | |
180 IAppBundle* app_bundle, | |
181 BSTR* ap_value) { | |
182 HRESULT hr = GetAppHostApValue(update3, app_bundle, ap_value); | |
183 if (SUCCEEDED(hr)) | |
184 return hr; | |
185 | |
186 // TODO(erikwright): distinguish between AppHost not installed and an | |
187 // error in GetAppHostApValue. | |
188 // TODO(erikwright): Use stable by default when App Host support is in | |
189 // stable. | |
190 base::win::ScopedBstr temp_ap_value; | |
191 if (temp_ap_value.Allocate(L"2.0-dev-multi-apphost") == NULL) { | |
192 LOG(ERROR) << "Unexpected error in ScopedBstr::Allocate."; | |
193 return E_FAIL; | |
194 } | |
195 *ap_value = temp_ap_value.Release(); | |
196 return S_OK; | |
197 } | |
198 | |
199 HRESULT CreateBinariesIApp(IAppBundle* app_bundle, BSTR ap, IApp** app) { | |
200 base::win::ScopedComPtr<IDispatch> idispatch; | |
201 HRESULT hr = app_bundle->createApp(base::win::ScopedBstr(kBinariesAppId), | |
202 idispatch.Receive()); | |
203 if (FAILED(hr)) { | |
204 LOG(ERROR) << "Failed to configure App Bundle: " << hr; | |
205 return hr; | |
206 } | |
207 | |
208 base::win::ScopedComPtr<IApp> temp_app; | |
209 hr = temp_app.QueryFrom(idispatch); | |
210 if (FAILED(hr)) { | |
211 LOG(ERROR) << "Unexpected error querying IApp from " | |
212 << "IAppBundle->createApp return value: " << hr; | |
213 return hr; | |
214 } | |
215 | |
216 hr = temp_app->put_isEulaAccepted(VARIANT_TRUE); | |
217 if (FAILED(hr)) { | |
218 LOG(ERROR) << "Failed to set 'EULA Accepted': " << hr; | |
219 return hr; | |
220 } | |
221 | |
222 hr = temp_app->put_ap(ap); | |
223 if (FAILED(hr)) | |
224 LOG(ERROR) << "Failed to set AP value: " << hr; | |
225 else | |
226 *app = temp_app.Detach(); | |
227 return hr; | |
228 } | |
229 | |
230 bool CheckIfDone(IAppBundle* app_bundle, IApp* app, HRESULT* hr) { | |
231 base::win::ScopedComPtr<ICurrentState> current_state; | |
232 CurrentState state_value; | |
233 *hr = GetCurrentState(app, current_state.Receive(), &state_value); | |
234 if (FAILED(*hr)) | |
235 return true; | |
236 | |
237 switch (state_value) { | |
238 case STATE_WAITING_TO_CHECK_FOR_UPDATE: | |
239 case STATE_CHECKING_FOR_UPDATE: | |
240 case STATE_WAITING_TO_DOWNLOAD: | |
241 case STATE_RETRYING_DOWNLOAD: | |
242 case STATE_DOWNLOADING: | |
243 case STATE_WAITING_TO_INSTALL: | |
244 case STATE_INSTALLING: | |
245 case STATE_DOWNLOAD_COMPLETE: | |
246 case STATE_EXTRACTING: | |
247 case STATE_APPLYING_DIFFERENTIAL_PATCH: | |
248 // These states will all transition on their own. | |
249 return false; | |
250 | |
251 case STATE_UPDATE_AVAILABLE: | |
252 OnUpdateAvailable(app_bundle, hr); | |
253 return FAILED(*hr); | |
254 | |
255 case STATE_READY_TO_INSTALL: | |
256 OnReadyToInstall(app_bundle, hr); | |
257 return FAILED(*hr); | |
258 | |
259 case STATE_NO_UPDATE: | |
260 LOG(INFO) << "Google Update reports that the binaries are already " | |
261 << "installed and up-to-date."; | |
262 return true; | |
263 | |
264 case STATE_INSTALL_COMPLETE: | |
265 return true; | |
266 | |
267 case STATE_ERROR: | |
268 *hr = OnError(current_state); | |
269 return FAILED(*hr); | |
270 | |
271 case STATE_INIT: | |
272 case STATE_PAUSED: | |
273 default: | |
274 LOG(ERROR) << "Unexpected bundle state: " << state_value << "."; | |
275 *hr = E_FAIL; | |
276 return true; | |
277 } | |
278 } | |
279 | |
280 } // namespace | |
281 | |
282 | |
283 // Globals -------------------------------------------------------------------- | |
284 | |
285 HRESULT InstallBinaries() { | |
286 base::win::ScopedCOMInitializer initialize_com; | |
287 if (!initialize_com.succeeded()) { | |
288 LOG(ERROR) << "COM initialization failed"; | |
289 return E_FAIL; | |
290 } | |
291 | |
292 base::win::ScopedComPtr<IGoogleUpdate3> update3; | |
293 HRESULT hr = CreateGoogleUpdate3(update3.Receive()); | |
294 if (FAILED(hr)) | |
295 return hr; | |
296 | |
297 base::win::ScopedComPtr<IAppBundle> app_bundle; | |
298 hr = CreateAppBundle(update3, app_bundle.Receive()); | |
299 if (FAILED(hr)) | |
300 return hr; | |
301 | |
302 base::win::ScopedBstr ap_value; | |
303 hr = SelectBinariesApValue(update3, app_bundle, ap_value.Receive()); | |
304 if (FAILED(hr)) | |
305 return hr; | |
306 | |
307 base::win::ScopedComPtr<IApp> app; | |
308 hr = CreateBinariesIApp(app_bundle, ap_value, app.Receive()); | |
309 if (FAILED(hr)) | |
310 return hr; | |
311 | |
312 hr = app_bundle->checkForUpdate(); | |
313 if (FAILED(hr)) { | |
314 LOG(ERROR) << "Failed to initiate update check: " << hr; | |
315 return hr; | |
316 } | |
317 | |
318 // We rely upon Omaha to eventually time out and transition to a failure | |
319 // state. | |
320 while (!CheckIfDone(app_bundle, app, &hr)) { | |
321 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( | |
322 kInstallationPollingIntervalMs)); | |
323 } | |
324 return hr; | |
325 } | |
326 | |
327 } // namespace app_host | |
OLD | NEW |