| 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 |