OLD | NEW |
| (Empty) |
1 // Copyright 2010 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "omaha/goopdate/app_bundle_state_initialized.h" | |
17 #include "omaha/base/debug.h" | |
18 #include "omaha/base/error.h" | |
19 #include "omaha/base/logging.h" | |
20 #include "omaha/common/app_registry_utils.h" | |
21 #include "omaha/common/config_manager.h" | |
22 #include "omaha/common/web_services_client.h" | |
23 #include "omaha/goopdate/app_bundle_state_busy.h" | |
24 #include "omaha/goopdate/app_bundle_state_paused.h" | |
25 #include "omaha/goopdate/app_bundle_state_stopped.h" | |
26 #include "omaha/goopdate/app_manager.h" | |
27 #include "omaha/goopdate/model.h" | |
28 | |
29 namespace omaha { | |
30 | |
31 namespace fsm { | |
32 | |
33 HRESULT AppBundleStateInitialized::Pause(AppBundle* app_bundle) { | |
34 CORE_LOG(L3, (_T("[AppBundleStateInitialized::Pause][0x%p]"), app_bundle)); | |
35 ASSERT1(app_bundle); | |
36 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
37 | |
38 ChangeState(app_bundle, new AppBundleStatePaused); | |
39 return S_OK; | |
40 } | |
41 | |
42 HRESULT AppBundleStateInitialized::Stop(AppBundle* app_bundle) { | |
43 CORE_LOG(L3, (_T("[AppBundleStateInitialized::Stop][0x%p]"), app_bundle)); | |
44 ASSERT1(app_bundle); | |
45 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
46 | |
47 ChangeState(app_bundle, new AppBundleStateStopped); | |
48 return S_OK; | |
49 } | |
50 | |
51 // Remains in this state. | |
52 HRESULT AppBundleStateInitialized::CreateApp(AppBundle* app_bundle, | |
53 const CString& app_id, | |
54 App** app) { | |
55 CORE_LOG(L3, (_T("[AppBundleStateInitialized::CreateApp][0x%p]"), | |
56 app_bundle)); | |
57 ASSERT1(app_bundle); | |
58 ASSERT1(app); | |
59 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
60 | |
61 // TODO(omaha): consider enabling this runtime test. Currently, there are | |
62 // a few unit tests that break this assumption mostly during the setup of | |
63 // the unit test itself. | |
64 #if 0 | |
65 if (app_id.CompareNoCase(kGoogleUpdateAppId) == 0) { | |
66 CORE_LOG(LE, (_T("[Omaha itself can't be created as a new app]"))); | |
67 return E_INVALIDARG; | |
68 } | |
69 #endif | |
70 | |
71 if (has_installed_app_) { | |
72 CORE_LOG(LE, (_T("[CreateApp][Installed app already in bundle]"))); | |
73 return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__)); | |
74 } | |
75 | |
76 GUID app_guid = {0}; | |
77 HRESULT hr = StringToGuidSafe(app_id, &app_guid); | |
78 if (FAILED(hr)) { | |
79 CORE_LOG(LE, (_T("[invalid app id][%s]"), app_id)); | |
80 return hr; | |
81 } | |
82 | |
83 scoped_ptr<App> local_app(new App(app_guid, false, app_bundle)); | |
84 hr = AddApp(app_bundle, local_app.get()); | |
85 if (FAILED(hr)) { | |
86 return hr; | |
87 } | |
88 | |
89 // When overinstalling, we want the install age for the existing install, so | |
90 // explicitly get it here. This is the only value read from the registry for | |
91 // installs. | |
92 AppManager::Instance()->ReadAppInstallTimeDiff(local_app.get()); | |
93 | |
94 *app = local_app.release(); | |
95 has_new_app_ = true; | |
96 return S_OK; | |
97 } | |
98 | |
99 // Remains in this state. | |
100 HRESULT AppBundleStateInitialized::CreateInstalledApp(AppBundle* app_bundle, | |
101 const CString& app_id, | |
102 App** app) { | |
103 CORE_LOG(L3, (_T("[AppBundleStateInitialized::CreateInstalledApp][0x%p]"), | |
104 app_bundle)); | |
105 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
106 | |
107 if (has_new_app_) { | |
108 CORE_LOG(LE, (_T("[CreateInstalledApp][New app already in bundle]"))); | |
109 return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__)); | |
110 } | |
111 | |
112 // Make sure that the application registration is up to date. | |
113 HRESULT hr = AppManager::Instance()->RunRegistrationUpdateHook(app_id); | |
114 if (FAILED(hr)) { | |
115 CORE_LOG(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr ? L3 : LW, | |
116 (_T("[RunRegistrationUpdateHook failed][%s][0x%x]"), | |
117 app_id, hr)); | |
118 } | |
119 | |
120 hr = AddInstalledApp(app_bundle, app_id, app); | |
121 if (FAILED(hr)) { | |
122 return hr; | |
123 } | |
124 | |
125 return S_OK; | |
126 } | |
127 | |
128 // Remains in this state. | |
129 // This function must explicitly check to ensure duplicate apps are not added | |
130 // because AddInstalledApp errors are ignored. The check for an empty bundle | |
131 // also covers the has_new_app_ case. | |
132 HRESULT AppBundleStateInitialized::CreateAllInstalledApps( | |
133 AppBundle* app_bundle) { | |
134 CORE_LOG(L3, (_T("[AppBundleStateInitialized::CreateAllInstalledApps][0x%p]"), | |
135 app_bundle)); | |
136 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
137 | |
138 if (app_bundle->GetNumberOfApps() > 0) { | |
139 CORE_LOG(LE, (_T("[CreateAllInstalledApps][Bundle already has apps]"))); | |
140 return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__)); | |
141 } | |
142 ASSERT1(!has_new_app_); | |
143 | |
144 // Make sure the list of installed applications is up to date. This is | |
145 // primarily important for Google Pack, which supports updating third-party | |
146 // applications that are not aware of Omaha registration, and hence will not | |
147 // update the registration during an install or uninstall outside of Pack. | |
148 AppManager& app_manager = *AppManager::Instance(); | |
149 HRESULT hr = app_manager.RunAllRegistrationUpdateHooks(); | |
150 if (FAILED(hr)) { | |
151 CORE_LOG(LW, (_T("[RunAllRegistrationUpdateHooks failed][0x%x]"), hr)); | |
152 } | |
153 | |
154 AppIdVector registered_app_ids; | |
155 hr = app_manager.GetRegisteredApps(®istered_app_ids); | |
156 if (FAILED(hr)) { | |
157 CORE_LOG(LE, (_T("[GetRegisteredApps failed][0x%08x]"), hr)); | |
158 return hr; | |
159 } | |
160 | |
161 for (size_t i = 0; i != registered_app_ids.size(); ++i) { | |
162 const CString& app_id = registered_app_ids[i]; | |
163 | |
164 ASSERT(RegKey::HasKey(app_registry_utils::GetAppClientStateKey( | |
165 app_bundle->is_machine(), app_id)), | |
166 (_T("[Clients key without matching ClientState][%s]"), app_id)); | |
167 | |
168 App* app = NULL; | |
169 HRESULT hr = AddInstalledApp(app_bundle, app_id, &app); | |
170 if (FAILED(hr)) { | |
171 CORE_LOG(LW, (_T("[AddInstalledApp failed processing app][%s]"), app_id)); | |
172 } | |
173 } | |
174 | |
175 return S_OK; | |
176 } | |
177 | |
178 // It is important that the lock is held for the entirety of this and similar | |
179 // methods with asynchronous callbacks because CompleteAsyncCall() must not be | |
180 // called before the state has been changed to busy. | |
181 HRESULT AppBundleStateInitialized::CheckForUpdate(AppBundle* app_bundle) { | |
182 CORE_LOG(L3, (_T("[AppBundleStateInitialized::CheckForUpdate][0x%p]"), | |
183 app_bundle)); | |
184 ASSERT1(app_bundle); | |
185 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
186 ASSERT1(!IsPendingNonBlockingCall(app_bundle)); | |
187 | |
188 if (app_bundle->GetNumberOfApps() == 0) { | |
189 CORE_LOG(LE, (_T("[CheckForUpdate][No apps in bundle]"))); | |
190 return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__)); | |
191 } | |
192 | |
193 ASSERT1(has_new_app_ != has_installed_app_); | |
194 | |
195 HRESULT hr = app_bundle->model()->CheckForUpdate(app_bundle); | |
196 if (FAILED(hr)) { | |
197 CORE_LOG(LE, (_T("[CheckForUpdates failed][0x%x][0x%p]"), hr, app_bundle)); | |
198 return hr; | |
199 } | |
200 | |
201 ChangeState(app_bundle, new AppBundleStateBusy); | |
202 return S_OK; | |
203 } | |
204 | |
205 HRESULT AppBundleStateInitialized::UpdateAllApps(AppBundle* app_bundle) { | |
206 CORE_LOG(L3, (_T("[AppBundleStateInitialized::UpdateAllApps][0x%p]"), | |
207 app_bundle)); | |
208 ASSERT1(app_bundle); | |
209 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
210 ASSERT1(!IsPendingNonBlockingCall(app_bundle)); | |
211 | |
212 if (app_bundle->GetNumberOfApps() != 0) { | |
213 CORE_LOG(LE, (_T("[UpdateAllApps][Apps already in bundle]"))); | |
214 return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__)); | |
215 } | |
216 | |
217 app_bundle->set_is_auto_update(true); | |
218 | |
219 HRESULT hr = app_bundle->createAllInstalledApps(); | |
220 if (FAILED(hr)) { | |
221 return hr; | |
222 } | |
223 ASSERT1(app_bundle->GetNumberOfApps() > 0); | |
224 | |
225 hr = app_bundle->model()->UpdateAllApps(app_bundle); | |
226 if (FAILED(hr)) { | |
227 CORE_LOG(LE, (_T("[UpdateAllApps failed][0x%08x][0x%p]"), hr, app_bundle)); | |
228 return hr; | |
229 } | |
230 | |
231 ChangeState(app_bundle, new AppBundleStateBusy); | |
232 return S_OK; | |
233 } | |
234 | |
235 HRESULT AppBundleStateInitialized::DownloadPackage( | |
236 AppBundle* app_bundle, | |
237 const CString& app_id, | |
238 const CString& package_name) { | |
239 CORE_LOG(L3, (_T("[AppBundleStateInitialized::DownloadPackage][0x%p]"), | |
240 app_bundle)); | |
241 ASSERT1(app_bundle); | |
242 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
243 | |
244 if (app_bundle->GetNumberOfApps() == 0 || has_new_app_) { | |
245 CORE_LOG(LE, (_T("[DownloadPackage][No existing apps in bundle]"))); | |
246 return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__)); | |
247 } | |
248 | |
249 return DoDownloadPackage(app_bundle, app_id, package_name); | |
250 } | |
251 | |
252 // App is created with is_update=true because using an installed app's | |
253 // information, including a non-zero version, is an update. | |
254 HRESULT AppBundleStateInitialized::AddInstalledApp(AppBundle* app_bundle, | |
255 const CString& app_id, | |
256 App** app) { | |
257 ASSERT1(app_bundle); | |
258 ASSERT1(app); | |
259 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
260 | |
261 GUID app_guid = {0}; | |
262 HRESULT hr = StringToGuidSafe(app_id, &app_guid); | |
263 if (FAILED(hr)) { | |
264 CORE_LOG(LE, (_T("[invalid app id][%s]"), app_id)); | |
265 return hr; | |
266 } | |
267 | |
268 scoped_ptr<App> local_app(new App(app_guid, true, app_bundle)); | |
269 | |
270 hr = AppManager::Instance()->ReadAppPersistentData(local_app.get()); | |
271 if (FAILED(hr)) { | |
272 CORE_LOG(LE, (_T("[ReadAppPersistentData failed][0x%x][%s]"), hr, app_id)); | |
273 return hr; | |
274 } | |
275 | |
276 hr = AddApp(app_bundle, local_app.get()); | |
277 if (FAILED(hr)) { | |
278 return hr; | |
279 } | |
280 | |
281 has_installed_app_ = true; | |
282 *app = local_app.release(); | |
283 return S_OK; | |
284 } | |
285 | |
286 // Fails if the app already exists in the bundle. | |
287 HRESULT AppBundleStateInitialized::AddApp(AppBundle* app_bundle, App* app) { | |
288 ASSERT1(app_bundle); | |
289 ASSERT1(app); | |
290 ASSERT1(app_bundle->model()->IsLockedByCaller()); | |
291 | |
292 for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) { | |
293 App* existing_app = app_bundle->GetApp(i); | |
294 if (::IsEqualGUID(existing_app->app_guid(), app->app_guid())) { | |
295 CORE_LOG(LE, (_T("[App already in bundle][%s]"), app->app_guid_string())); | |
296 return GOOPDATE_E_CALL_UNEXPECTED; | |
297 } | |
298 } | |
299 | |
300 AddAppToBundle(app_bundle, app); | |
301 return S_OK; | |
302 } | |
303 | |
304 } // namespace fsm | |
305 | |
306 } // namespace omaha | |
OLD | NEW |