| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-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_manager.h" | |
| 17 #include <algorithm> | |
| 18 #include <cstdlib> | |
| 19 #include <functional> | |
| 20 #include <map> | |
| 21 #include "base/scoped_ptr.h" | |
| 22 #include "omaha/base/const_object_names.h" | |
| 23 #include "omaha/base/error.h" | |
| 24 #include "omaha/base/reg_key.h" | |
| 25 #include "omaha/base/scoped_ptr_address.h" | |
| 26 #include "omaha/base/time.h" | |
| 27 #include "omaha/base/utils.h" | |
| 28 #include "omaha/base/vistautil.h" | |
| 29 #include "omaha/common/app_registry_utils.h" | |
| 30 #include "omaha/common/config_manager.h" | |
| 31 #include "omaha/common/const_goopdate.h" | |
| 32 #include "omaha/common/oem_install_utils.h" | |
| 33 #include "omaha/goopdate/application_usage_data.h" | |
| 34 #include "omaha/goopdate/model.h" | |
| 35 #include "omaha/goopdate/server_resource.h" | |
| 36 #include "omaha/goopdate/string_formatter.h" | |
| 37 | |
| 38 namespace omaha { | |
| 39 | |
| 40 namespace { | |
| 41 | |
| 42 const uint32 kInitialInstallTimeDiff = static_cast<uint32>(-1 * kSecondsPerDay); | |
| 43 | |
| 44 // Returns the number of days haven been passed since the given time. | |
| 45 // The parameter time is in the same format as C time() returns. | |
| 46 int GetNumberOfDaysSince(int time) { | |
| 47 ASSERT1(time >= 0); | |
| 48 const int now = Time64ToInt32(GetCurrent100NSTime()); | |
| 49 ASSERT1(now >= time); | |
| 50 | |
| 51 if (now < time) { | |
| 52 // In case the client computer clock is adjusted in between. | |
| 53 return 0; | |
| 54 } | |
| 55 return (now - time) / kSecondsPerDay; | |
| 56 } | |
| 57 | |
| 58 // Determines if an application is registered with Omaha. | |
| 59 class IsAppRegisteredFunc | |
| 60 : public std::unary_function<const CString&, HRESULT> { | |
| 61 public: | |
| 62 explicit IsAppRegisteredFunc(const CString& guid) | |
| 63 : is_registered_(false), | |
| 64 guid_(guid) {} | |
| 65 | |
| 66 bool is_registered() const { return is_registered_; } | |
| 67 | |
| 68 HRESULT operator() (const CString& guid) { | |
| 69 if (guid.CompareNoCase(guid_) == 0) { | |
| 70 is_registered_ = true; | |
| 71 } | |
| 72 return S_OK; | |
| 73 } | |
| 74 private: | |
| 75 CString guid_; | |
| 76 bool is_registered_; | |
| 77 }; | |
| 78 | |
| 79 // Enumerates all sub keys of the key and calls the functor for each of them, | |
| 80 // ignoring errors to ensure all keys are processed. | |
| 81 template <typename T> | |
| 82 HRESULT EnumerateSubKeys(const TCHAR* key_name, T* functor) { | |
| 83 RegKey client_key; | |
| 84 HRESULT hr = client_key.Open(key_name, KEY_READ); | |
| 85 if (FAILED(hr)) { | |
| 86 return hr; | |
| 87 } | |
| 88 | |
| 89 int num_sub_keys = client_key.GetSubkeyCount(); | |
| 90 for (int i = 0; i < num_sub_keys; ++i) { | |
| 91 CString sub_key_name; | |
| 92 hr = client_key.GetSubkeyNameAt(i, &sub_key_name); | |
| 93 if (SUCCEEDED(hr)) { | |
| 94 (*functor)(sub_key_name); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 return S_OK; | |
| 99 } | |
| 100 | |
| 101 } // namespace | |
| 102 | |
| 103 typedef bool (*AppPredictFunc)(const AppManager& app_manager, | |
| 104 const CString& app_id); | |
| 105 | |
| 106 bool IsUninstalledAppPredicate(const AppManager& app_manager, | |
| 107 const CString& app_id) { | |
| 108 return app_manager.IsAppUninstalled(app_id); | |
| 109 } | |
| 110 | |
| 111 bool IsAppOemInstalledAndEulaAcceptedPredicate(const AppManager& app_manager, | |
| 112 const CString& app_id) { | |
| 113 return app_manager.IsAppOemInstalledAndEulaAccepted(app_id); | |
| 114 } | |
| 115 | |
| 116 bool IsRegisteredAppPredicate(const AppManager& app_manager, | |
| 117 const CString& app_id) { | |
| 118 return app_manager.IsAppRegistered(app_id); | |
| 119 } | |
| 120 | |
| 121 // Accumulates app IDs for apps that satisfies the predicate. | |
| 122 class CollectProductsFunc | |
| 123 : public std::unary_function<const CString&, HRESULT> { | |
| 124 public: | |
| 125 CollectProductsFunc(const AppPredictFunc predicate, | |
| 126 const AppManager& app_manager, | |
| 127 AppIdVector* app_ids) | |
| 128 : predicate_(predicate), | |
| 129 app_manager_(app_manager), | |
| 130 app_ids_(app_ids) { | |
| 131 ASSERT1(app_ids); | |
| 132 } | |
| 133 | |
| 134 // Ignores errors and accumulates as many applications as possible. | |
| 135 HRESULT operator() (const CString& app_id) const { | |
| 136 if ((*predicate_)(app_manager_, app_id)) { | |
| 137 app_ids_->push_back(app_id); | |
| 138 } | |
| 139 | |
| 140 return S_OK; | |
| 141 } | |
| 142 | |
| 143 private: | |
| 144 const AppPredictFunc predicate_; | |
| 145 const AppManager& app_manager_; | |
| 146 AppIdVector* const app_ids_; | |
| 147 | |
| 148 DISALLOW_IMPLICIT_CONSTRUCTORS(CollectProductsFunc); | |
| 149 }; | |
| 150 | |
| 151 // Runs application registration hooks registered under Omaha AppIds. | |
| 152 // Reads the Hook Clsid entry under Clients\{AppID}. CoCreates the CLSID. Calls | |
| 153 // IRegistrationUpdateHook::UpdateRegistry(). | |
| 154 class RunRegistrationUpdateHooksFunc | |
| 155 : public std::unary_function<const CString&, HRESULT> { | |
| 156 public: | |
| 157 explicit RunRegistrationUpdateHooksFunc(const AppManager& app_manager) | |
| 158 : app_manager_(app_manager) { | |
| 159 } | |
| 160 | |
| 161 HRESULT operator() (const CString& app_id) { | |
| 162 GUID app_guid = GUID_NULL; | |
| 163 HRESULT hr = StringToGuidSafe(app_id, &app_guid); | |
| 164 if (FAILED(hr)) { | |
| 165 return hr; | |
| 166 } | |
| 167 RegKey client_key; | |
| 168 hr = app_manager_.OpenClientKey(app_guid, &client_key); | |
| 169 if (FAILED(hr)) { | |
| 170 return hr; | |
| 171 } | |
| 172 | |
| 173 CString hook_clsid_str; | |
| 174 hr = client_key.GetValue(kRegValueUpdateHookClsid, &hook_clsid_str); | |
| 175 if (FAILED(hr)) { | |
| 176 return hr; | |
| 177 } | |
| 178 GUID hook_clsid = GUID_NULL; | |
| 179 hr = StringToGuidSafe(hook_clsid_str, &hook_clsid); | |
| 180 if (FAILED(hr)) { | |
| 181 return hr; | |
| 182 } | |
| 183 | |
| 184 CORE_LOG(L3, (_T("[Update Hook Clsid][%s][%s]"), app_id, hook_clsid_str)); | |
| 185 | |
| 186 CComPtr<IRegistrationUpdateHook> registration_hook; | |
| 187 hr = registration_hook.CoCreateInstance(hook_clsid); | |
| 188 if (FAILED(hr)) { | |
| 189 CORE_LOG(LE, (_T("[IRegistrationUpdateHook CoCreate failed][0x%x]"), hr)); | |
| 190 return hr; | |
| 191 } | |
| 192 | |
| 193 hr = registration_hook->UpdateRegistry(CComBSTR(app_id), | |
| 194 app_manager_.is_machine_); | |
| 195 if (FAILED(hr)) { | |
| 196 CORE_LOG(LE, (_T("[registration_hook UpdateRegistry failed][0x%x]"), hr)); | |
| 197 return hr; | |
| 198 } | |
| 199 | |
| 200 return S_OK; | |
| 201 } | |
| 202 | |
| 203 private: | |
| 204 const AppManager& app_manager_; | |
| 205 }; | |
| 206 | |
| 207 AppManager* AppManager::instance_ = NULL; | |
| 208 | |
| 209 // We do not worry about contention on creation because only the Worker should | |
| 210 // create AppManager during its initialization. | |
| 211 HRESULT AppManager::CreateInstance(bool is_machine) { | |
| 212 ASSERT1(!instance_); | |
| 213 if (instance_) { | |
| 214 return S_OK; | |
| 215 } | |
| 216 | |
| 217 AppManager* instance(new AppManager(is_machine)); | |
| 218 if (!instance->InitializeRegistryLock()) { | |
| 219 HRESULT hr(HRESULTFromLastError()); | |
| 220 delete instance; | |
| 221 return hr; | |
| 222 } | |
| 223 | |
| 224 instance_ = instance; | |
| 225 return S_OK; | |
| 226 } | |
| 227 | |
| 228 void AppManager::DeleteInstance() { | |
| 229 delete instance_; | |
| 230 instance_ = NULL; | |
| 231 } | |
| 232 | |
| 233 AppManager* AppManager::Instance() { | |
| 234 ASSERT1(instance_); | |
| 235 return instance_; | |
| 236 } | |
| 237 | |
| 238 HRESULT AppManager::ReadAppVersionNoLock(bool is_machine, const GUID& app_guid, | |
| 239 CString* version) { | |
| 240 ASSERT1(version); | |
| 241 CORE_LOG(L2, (_T("[ReadAppVersionNoLock][%s]"), GuidToString(app_guid))); | |
| 242 | |
| 243 AppManager app_manager(is_machine); | |
| 244 RegKey client_key; | |
| 245 HRESULT hr = app_manager.OpenClientKey(app_guid, &client_key); | |
| 246 if (FAILED(hr)) { | |
| 247 return hr; | |
| 248 } | |
| 249 | |
| 250 hr = client_key.GetValue(kRegValueProductVersion, version); | |
| 251 if (FAILED(hr)) { | |
| 252 return hr; | |
| 253 } | |
| 254 | |
| 255 CORE_LOG(L3, (_T("[kRegValueProductVersion][%s]"), *version)); | |
| 256 return S_OK; | |
| 257 } | |
| 258 | |
| 259 AppManager::AppManager(bool is_machine) | |
| 260 : is_machine_(is_machine) { | |
| 261 CORE_LOG(L3, (_T("[AppManager::AppManager][is_machine=%d]"), is_machine)); | |
| 262 } | |
| 263 | |
| 264 // App installers should use similar code to create a lock to acquire while | |
| 265 // modifying Omaha registry. | |
| 266 bool AppManager::InitializeRegistryLock() { | |
| 267 NamedObjectAttributes lock_attr; | |
| 268 GetNamedObjectAttributes(kRegistryAccessMutex, is_machine_, &lock_attr); | |
| 269 return registry_access_lock_.InitializeWithSecAttr(lock_attr.name, | |
| 270 &lock_attr.sa); | |
| 271 } | |
| 272 | |
| 273 // Vulnerable to a race condition with installers. To prevent this, acquire | |
| 274 // GetRegistryStableStateLock(). | |
| 275 bool AppManager::IsAppRegistered(const GUID& app_guid) const { | |
| 276 return IsAppRegistered(GuidToString(app_guid)); | |
| 277 } | |
| 278 | |
| 279 // Vulnerable to a race condition with installers. To prevent this, acquire | |
| 280 // GetRegistryStableStateLock(). | |
| 281 bool AppManager::IsAppRegistered(const CString& app_id) const { | |
| 282 IsAppRegisteredFunc func(app_id); | |
| 283 HRESULT hr = EnumerateSubKeys( | |
| 284 ConfigManager::Instance()->registry_clients(is_machine_), | |
| 285 &func); | |
| 286 if (FAILED(hr)) { | |
| 287 return false; | |
| 288 } | |
| 289 | |
| 290 return func.is_registered(); | |
| 291 } | |
| 292 | |
| 293 bool AppManager::IsAppUninstalled(const CString& app_id) const { | |
| 294 GUID app_guid = {0}; | |
| 295 if (FAILED(StringToGuidSafe(app_id, &app_guid))) { | |
| 296 ASSERT1(false); | |
| 297 return false; | |
| 298 } | |
| 299 return IsAppUninstalled(app_guid); | |
| 300 } | |
| 301 | |
| 302 // An app is considered uninstalled if: | |
| 303 // * The app's Clients key does not exist AND | |
| 304 // * The app's ClientState key exists and contains the pv value. | |
| 305 // We check for the pv key value in the ClientState to prevent Omaha from | |
| 306 // detecting the key created in the following scenarios as an uninstalled app. | |
| 307 // * Per-machine apps may write dr to per-user Omaha's key. Per-user Omaha | |
| 308 // must not detect this as an uninstalled app. | |
| 309 // * Omaha may create the app's ClientState key and write values from the | |
| 310 // metainstaller tag before running the installer, which creates the | |
| 311 // Clients key. | |
| 312 bool AppManager::IsAppUninstalled(const GUID& app_guid) const { | |
| 313 if (IsAppRegistered(app_guid)) { | |
| 314 return false; | |
| 315 } | |
| 316 | |
| 317 return RegKey::HasValue(GetClientStateKeyName(app_guid), | |
| 318 kRegValueProductVersion); | |
| 319 } | |
| 320 | |
| 321 bool AppManager::IsAppOemInstalledAndEulaAccepted(const CString& app_id) const { | |
| 322 GUID app_guid = GUID_NULL; | |
| 323 if (FAILED(StringToGuidSafe(app_id, &app_guid))) { | |
| 324 ASSERT1(false); | |
| 325 return false; | |
| 326 } | |
| 327 | |
| 328 if (IsAppUninstalled(app_guid)) { | |
| 329 return false; | |
| 330 } | |
| 331 | |
| 332 if (!app_registry_utils::IsAppEulaAccepted(is_machine_, app_id, false)) { | |
| 333 CORE_LOG(L3, (_T("[EULA not accepted for app %s, its OEM ping not sent.]"), | |
| 334 app_id.GetString())); | |
| 335 return false; | |
| 336 } | |
| 337 | |
| 338 return RegKey::HasValue(GetClientStateKeyName(app_guid), kRegValueOemInstall); | |
| 339 } | |
| 340 | |
| 341 // Vulnerable to a race condition with installers. To prevent this, hold | |
| 342 // GetRegistryStableStateLock() while calling this function and related | |
| 343 // functions, such as ReadAppPersistentData(). | |
| 344 HRESULT AppManager::GetRegisteredApps(AppIdVector* app_ids) const { | |
| 345 ASSERT1(app_ids); | |
| 346 | |
| 347 CollectProductsFunc func(IsRegisteredAppPredicate, *this, app_ids); | |
| 348 | |
| 349 return EnumerateSubKeys( | |
| 350 ConfigManager::Instance()->registry_clients(is_machine_), | |
| 351 &func); | |
| 352 } | |
| 353 | |
| 354 // Vulnerable to a race condition with installers. To prevent this, acquire | |
| 355 // GetRegistryStableStateLock(). | |
| 356 HRESULT AppManager::GetUninstalledApps(AppIdVector* app_ids) const { | |
| 357 ASSERT1(app_ids); | |
| 358 | |
| 359 CollectProductsFunc func(IsUninstalledAppPredicate, *this, app_ids); | |
| 360 | |
| 361 return EnumerateSubKeys( | |
| 362 ConfigManager::Instance()->registry_client_state(is_machine_), | |
| 363 &func); | |
| 364 } | |
| 365 | |
| 366 HRESULT AppManager::GetOemInstalledAndEulaAcceptedApps( | |
| 367 AppIdVector* app_ids) const { | |
| 368 ASSERT1(app_ids); | |
| 369 | |
| 370 CollectProductsFunc func(IsAppOemInstalledAndEulaAcceptedPredicate, | |
| 371 *this, | |
| 372 app_ids); | |
| 373 | |
| 374 return EnumerateSubKeys( | |
| 375 ConfigManager::Instance()->registry_client_state(is_machine_), | |
| 376 &func); | |
| 377 } | |
| 378 | |
| 379 HRESULT AppManager::RunRegistrationUpdateHook(const CString& app_id) const { | |
| 380 return RunRegistrationUpdateHooksFunc(*this)(app_id); | |
| 381 } | |
| 382 | |
| 383 // Vulnerable to a race condition with installers. We think this is acceptable. | |
| 384 // If there is a future requirement for greater consistency, acquire | |
| 385 // GetRegistryStableStateLock(). | |
| 386 HRESULT AppManager::RunAllRegistrationUpdateHooks() const { | |
| 387 RunRegistrationUpdateHooksFunc func(*this); | |
| 388 const TCHAR* key(ConfigManager::Instance()->registry_clients(is_machine_)); | |
| 389 return EnumerateSubKeys(key, &func); | |
| 390 } | |
| 391 | |
| 392 CString AppManager::GetClientKeyName(const GUID& app_guid) const { | |
| 393 return app_registry_utils::GetAppClientsKey(is_machine_, | |
| 394 GuidToString(app_guid)); | |
| 395 } | |
| 396 | |
| 397 CString AppManager::GetClientStateKeyName(const GUID& app_guid) const { | |
| 398 return app_registry_utils::GetAppClientStateKey(is_machine_, | |
| 399 GuidToString(app_guid)); | |
| 400 } | |
| 401 | |
| 402 CString AppManager::GetClientStateMediumKeyName(const GUID& app_guid) const { | |
| 403 ASSERT1(is_machine_); | |
| 404 return app_registry_utils::GetAppClientStateMediumKey(is_machine_, | |
| 405 GuidToString(app_guid)); | |
| 406 } | |
| 407 | |
| 408 // Assumes the registry access lock is held. | |
| 409 HRESULT AppManager::OpenClientKey(const GUID& app_guid, | |
| 410 RegKey* client_key) const { | |
| 411 ASSERT1(client_key); | |
| 412 return client_key->Open(GetClientKeyName(app_guid), KEY_READ); | |
| 413 } | |
| 414 | |
| 415 // Assumes the registry access lock is held. | |
| 416 HRESULT AppManager::OpenClientStateKey(const GUID& app_guid, | |
| 417 REGSAM sam_desired, | |
| 418 RegKey* client_state_key) const { | |
| 419 ASSERT1(client_state_key); | |
| 420 CString key_name = GetClientStateKeyName(app_guid); | |
| 421 return client_state_key->Open(key_name, sam_desired); | |
| 422 } | |
| 423 | |
| 424 // Also creates the ClientStateMedium key for machine apps, ensuring it exists | |
| 425 // whenever ClientState exists. Does not create ClientStateMedium for Omaha. | |
| 426 // This function is called for self-updates, so it must explicitly avoid this. | |
| 427 // Assumes the registry access lock is held. | |
| 428 HRESULT AppManager::CreateClientStateKey(const GUID& app_guid, | |
| 429 RegKey* client_state_key) { | |
| 430 ASSERT1(client_state_key); | |
| 431 // TODO(omaha3): Add GetOwner() to GLock & add this to Open() functions too. | |
| 432 // ASSERT1(::GetCurrentThreadId() == registry_access_lock_.GetOwner()); | |
| 433 | |
| 434 const CString key_name = GetClientStateKeyName(app_guid); | |
| 435 HRESULT hr = client_state_key->Create(key_name); | |
| 436 if (FAILED(hr)) { | |
| 437 CORE_LOG(L3, (_T("[RegKey::Create failed][0x%08x]"), hr)); | |
| 438 return hr; | |
| 439 } | |
| 440 | |
| 441 if (!is_machine_) { | |
| 442 return S_OK; | |
| 443 } | |
| 444 | |
| 445 if (::IsEqualGUID(kGoopdateGuid, app_guid)) { | |
| 446 return S_OK; | |
| 447 } | |
| 448 | |
| 449 const CString medium_key_name = GetClientStateMediumKeyName(app_guid); | |
| 450 hr = RegKey::CreateKey(medium_key_name); | |
| 451 if (FAILED(hr)) { | |
| 452 CORE_LOG(L3, (_T("[RegKey::Create ClientStateMedium failed][0x%08x]"), hr)); | |
| 453 return hr; | |
| 454 } | |
| 455 | |
| 456 return S_OK; | |
| 457 } | |
| 458 | |
| 459 // Reads the following values from the registry: | |
| 460 // Clients key | |
| 461 // pv | |
| 462 // lang | |
| 463 // name | |
| 464 // ClientState key | |
| 465 // lang (if not present in Clients) | |
| 466 // ap | |
| 467 // tttoken | |
| 468 // iid | |
| 469 // brand | |
| 470 // client | |
| 471 // experiment | |
| 472 // (referral is intentionally not read) | |
| 473 // InstallTime (converted to diff) | |
| 474 // oeminstall | |
| 475 // ClientState and ClientStateMedium key | |
| 476 // eulaaccepted | |
| 477 // ClientState key in HKCU/HKLM/Low integrity | |
| 478 // did run | |
| 479 // | |
| 480 // app_guid_ is set to the app_guid argument. | |
| 481 // Note: pv is not read from ClientState into app_data. It's | |
| 482 // presence is checked for an uninstall | |
| 483 // TODO(omaha3): We will need to get ClientState's pv when reporting uninstalls. | |
| 484 // Note: If the application is uninstalled, the Clients key may not exist. | |
| 485 HRESULT AppManager::ReadAppPersistentData(App* app) { | |
| 486 ASSERT1(app); | |
| 487 | |
| 488 const GUID& app_guid = app->app_guid(); | |
| 489 const CString& app_guid_string = app->app_guid_string(); | |
| 490 | |
| 491 CORE_LOG(L2, (_T("[AppManager::ReadAppPersistentData][%s]"), | |
| 492 app_guid_string)); | |
| 493 | |
| 494 ASSERT1(app->model()->IsLockedByCaller()); | |
| 495 | |
| 496 __mutexScope(registry_access_lock_); | |
| 497 | |
| 498 const bool is_eula_accepted = | |
| 499 app_registry_utils::IsAppEulaAccepted(is_machine_, | |
| 500 app_guid_string, | |
| 501 false); | |
| 502 app->is_eula_accepted_ = is_eula_accepted ? TRISTATE_TRUE : TRISTATE_FALSE; | |
| 503 | |
| 504 bool client_key_exists = false; | |
| 505 RegKey client_key; | |
| 506 HRESULT hr = OpenClientKey(app_guid, &client_key); | |
| 507 if (SUCCEEDED(hr)) { | |
| 508 client_key_exists = true; | |
| 509 | |
| 510 CString version; | |
| 511 hr = client_key.GetValue(kRegValueProductVersion, &version); | |
| 512 CORE_LOG(L3, (_T("[AppManager::ReadAppPersistentData]") | |
| 513 _T("[%s][version=%s]"), app_guid_string, version)); | |
| 514 if (FAILED(hr)) { | |
| 515 return hr; | |
| 516 } | |
| 517 | |
| 518 app->current_version()->set_version(version); | |
| 519 | |
| 520 // Language and name might not be written by installer, so ignore failures. | |
| 521 client_key.GetValue(kRegValueLanguage, &app->language_); | |
| 522 client_key.GetValue(kRegValueAppName, &app->display_name_); | |
| 523 } | |
| 524 | |
| 525 // Ensure there is a valid display name. | |
| 526 if (app->display_name_.IsEmpty()) { | |
| 527 StringFormatter formatter(app->app_bundle()->display_language()); | |
| 528 | |
| 529 CString company_name; | |
| 530 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_FRIENDLY_COMPANY_NAME, | |
| 531 &company_name))); | |
| 532 | |
| 533 VERIFY1(SUCCEEDED(formatter.FormatMessage(&app->display_name_, | |
| 534 IDS_DEFAULT_APP_DISPLAY_NAME, | |
| 535 company_name))); | |
| 536 } | |
| 537 | |
| 538 // If ClientState registry key doesn't exist, the function could return. | |
| 539 // Before opening the key, set days_since_last* to -1, which is the | |
| 540 // default value if reg key doesn't exist. If later we find that the values | |
| 541 // are readable, new values will overwrite current ones. | |
| 542 app->set_days_since_last_active_ping(-1); | |
| 543 app->set_days_since_last_roll_call(-1); | |
| 544 | |
| 545 // The following do not rely on client_state_key, so check them before | |
| 546 // possibly returning if OpenClientStateKey fails. | |
| 547 | |
| 548 // Reads the did run value. | |
| 549 ApplicationUsageData app_usage(is_machine_, vista_util::IsVistaOrLater()); | |
| 550 app_usage.ReadDidRun(app_guid_string); | |
| 551 | |
| 552 // Sets did_run regardless of the return value of ReadDidRun above. If read | |
| 553 // fails, active_state() should return ACTIVE_UNKNOWN which is intented. | |
| 554 app->did_run_ = app_usage.active_state(); | |
| 555 | |
| 556 // TODO(omaha3): Consider moving GetInstallTimeDiffSec() up here. Be careful | |
| 557 // that the results when ClientState does not exist are desirable. See the | |
| 558 // comments near that function and above set_days_since_last_active_ping call. | |
| 559 | |
| 560 RegKey client_state_key; | |
| 561 hr = OpenClientStateKey(app_guid, KEY_READ, &client_state_key); | |
| 562 if (FAILED(hr)) { | |
| 563 // It is possible that the client state key has not yet been populated. | |
| 564 // In this case just return the information that we have gathered thus far. | |
| 565 // However if both keys do not exist, then we are doing something wrong. | |
| 566 CORE_LOG(LW, (_T("[AppManager::ReadAppPersistentData - No ClientState]"))); | |
| 567 if (client_key_exists) { | |
| 568 return S_OK; | |
| 569 } else { | |
| 570 return hr; | |
| 571 } | |
| 572 } | |
| 573 | |
| 574 // Read language from ClientState key if it was not found in the Clients key. | |
| 575 if (app->language().IsEmpty()) { | |
| 576 client_state_key.GetValue(kRegValueLanguage, &app->language_); | |
| 577 } | |
| 578 | |
| 579 client_state_key.GetValue(kRegValueAdditionalParams, &app->ap_); | |
| 580 client_state_key.GetValue(kRegValueTTToken, &app->tt_token_); | |
| 581 | |
| 582 CString iid; | |
| 583 client_state_key.GetValue(kRegValueInstallationId, &iid); | |
| 584 GUID iid_guid; | |
| 585 if (SUCCEEDED(StringToGuidSafe(iid, &iid_guid))) { | |
| 586 app->iid_ = iid_guid; | |
| 587 } | |
| 588 | |
| 589 client_state_key.GetValue(kRegValueBrandCode, &app->brand_code_); | |
| 590 ASSERT1(app->brand_code_.GetLength() <= kBrandIdLength); | |
| 591 client_state_key.GetValue(kRegValueClientId, &app->client_id_); | |
| 592 | |
| 593 // We do not need the referral_id. | |
| 594 | |
| 595 DWORD last_active_ping_sec(0); | |
| 596 if (SUCCEEDED(client_state_key.GetValue(kRegValueActivePingDayStartSec, | |
| 597 &last_active_ping_sec))) { | |
| 598 int days_since_last_active_ping = | |
| 599 GetNumberOfDaysSince(static_cast<int32>(last_active_ping_sec)); | |
| 600 app->set_days_since_last_active_ping(days_since_last_active_ping); | |
| 601 } | |
| 602 | |
| 603 DWORD last_roll_call_sec(0); | |
| 604 if (SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec, | |
| 605 &last_roll_call_sec))) { | |
| 606 int days_since_last_roll_call = | |
| 607 GetNumberOfDaysSince(static_cast<int32>(last_roll_call_sec)); | |
| 608 app->set_days_since_last_roll_call(days_since_last_roll_call); | |
| 609 } | |
| 610 | |
| 611 app->install_time_diff_sec_ = GetInstallTimeDiffSec(app_guid); | |
| 612 // Generally GetInstallTimeDiffSec() shouldn't return kInitialInstallTimeDiff | |
| 613 // here. The only exception is in the unexpected case when ClientState exists | |
| 614 // without a pv. | |
| 615 ASSERT1((app->install_time_diff_sec_ != kInitialInstallTimeDiff) || | |
| 616 !RegKey::HasValue(GetClientStateKeyName(app_guid), | |
| 617 kRegValueProductVersion)); | |
| 618 | |
| 619 return S_OK; | |
| 620 } | |
| 621 | |
| 622 void AppManager::ReadAppInstallTimeDiff(App* app) { | |
| 623 ASSERT1(app); | |
| 624 app->install_time_diff_sec_ = GetInstallTimeDiffSec(app->app_guid()); | |
| 625 } | |
| 626 | |
| 627 // Calls ReadAppPersistentData() to populate app and adds the following values | |
| 628 // specific to uninstalled apps: | |
| 629 // ClientState key | |
| 630 // pv: set as current_version()->version | |
| 631 // | |
| 632 // Since this is an uninstalled app, values from the Clients key should not be | |
| 633 // populated. | |
| 634 HRESULT AppManager::ReadUninstalledAppPersistentData(App* app) { | |
| 635 ASSERT1(app); | |
| 636 ASSERT1(!IsAppRegistered(app->app_guid_string())); | |
| 637 | |
| 638 HRESULT hr = ReadAppPersistentData(app); | |
| 639 if (FAILED(hr)) { | |
| 640 return hr; | |
| 641 } | |
| 642 | |
| 643 ASSERT1(app->current_version()->version().IsEmpty()); | |
| 644 | |
| 645 RegKey client_state_key; | |
| 646 hr = OpenClientStateKey(app->app_guid(), KEY_READ, &client_state_key); | |
| 647 ASSERT(SUCCEEDED(hr), (_T("Uninstalled apps have a ClientState key."))); | |
| 648 | |
| 649 CString version; | |
| 650 hr = client_state_key.GetValue(kRegValueProductVersion, &version); | |
| 651 CORE_LOG(L3, (_T("[AppManager::ReadAppPersistentData]") | |
| 652 _T("[%s][uninstalled version=%s]"), | |
| 653 app->app_guid_string(), version)); | |
| 654 ASSERT(SUCCEEDED(hr), (_T("Uninstalled apps have a pv."))); | |
| 655 app->current_version()->set_version(version); | |
| 656 | |
| 657 return S_OK; | |
| 658 } | |
| 659 | |
| 660 // Sets the following values in the app's ClientState, to make them available to | |
| 661 // the installer: | |
| 662 // lang | |
| 663 // ap | |
| 664 // brand (in SetAppBranding) | |
| 665 // client (in SetAppBranding) | |
| 666 // experiment | |
| 667 // referral (in SetAppBranding) | |
| 668 // InstallTime (in SetAppBranding; converted from diff) | |
| 669 // oeminstall (if appropriate) | |
| 670 // eulaaccepted (set/deleted) | |
| 671 // browser | |
| 672 // usagestats | |
| 673 // Sets eulaaccepted=0 if the app is not already registered and the app's EULA | |
| 674 // has not been accepted. Deletes eulaaccepted if the EULA has been accepted. | |
| 675 // Only call for initial or over-installs. Do not call for updates to avoid | |
| 676 // mistakenly replacing data, such as the application's language, and causing | |
| 677 // unexpected changes to the app during a silent update. | |
| 678 HRESULT AppManager::WritePreInstallData(const App& app) { | |
| 679 CORE_LOG(L2, (_T("[AppManager::WritePreInstallData][%s]"), | |
| 680 app.app_guid_string())); | |
| 681 | |
| 682 ASSERT1(app.app_bundle()->is_machine() == is_machine_); | |
| 683 | |
| 684 ASSERT1(IsRegistryStableStateLockedByCaller()); | |
| 685 __mutexScope(registry_access_lock_); | |
| 686 | |
| 687 RegKey client_state_key; | |
| 688 HRESULT hr = CreateClientStateKey(app.app_guid(), &client_state_key); | |
| 689 if (FAILED(hr)) { | |
| 690 return hr; | |
| 691 } | |
| 692 | |
| 693 if (app.is_eula_accepted()) { | |
| 694 hr = app_registry_utils::ClearAppEulaNotAccepted(is_machine_, | |
| 695 app.app_guid_string()); | |
| 696 } else { | |
| 697 if (!IsAppRegistered(app.app_guid())) { | |
| 698 hr = app_registry_utils::SetAppEulaNotAccepted(is_machine_, | |
| 699 app.app_guid_string()); | |
| 700 } | |
| 701 } | |
| 702 if (FAILED(hr)) { | |
| 703 return hr; | |
| 704 } | |
| 705 | |
| 706 if (!app.language().IsEmpty()) { | |
| 707 VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueLanguage, | |
| 708 app.language()))); | |
| 709 } | |
| 710 | |
| 711 if (app.ap().IsEmpty()) { | |
| 712 VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueAdditionalParams))); | |
| 713 } else { | |
| 714 VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueAdditionalParams, | |
| 715 app.ap()))); | |
| 716 } | |
| 717 | |
| 718 CString state_key_path = GetClientStateKeyName(app.app_guid()); | |
| 719 VERIFY1(SUCCEEDED(app_registry_utils::SetAppBranding(state_key_path, | |
| 720 app.brand_code(), | |
| 721 app.client_id(), | |
| 722 app.referral_id()))); | |
| 723 | |
| 724 if (app.GetExperimentLabels().IsEmpty()) { | |
| 725 VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueExperimentLabels))); | |
| 726 } else { | |
| 727 VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueExperimentLabels, | |
| 728 app.GetExperimentLabels()))); | |
| 729 } | |
| 730 | |
| 731 if (oem_install_utils::IsOemInstalling(is_machine_)) { | |
| 732 ASSERT1(is_machine_); | |
| 733 VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueOemInstall, _T("1")))); | |
| 734 } | |
| 735 | |
| 736 if (BROWSER_UNKNOWN == app.browser_type()) { | |
| 737 VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueBrowser))); | |
| 738 } else { | |
| 739 DWORD browser_type = app.browser_type(); | |
| 740 VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueBrowser, | |
| 741 browser_type))); | |
| 742 } | |
| 743 | |
| 744 if (TRISTATE_NONE != app.usage_stats_enable()) { | |
| 745 VERIFY1(SUCCEEDED(app_registry_utils::SetUsageStatsEnable( | |
| 746 is_machine_, | |
| 747 app.app_guid_string(), | |
| 748 app.usage_stats_enable()))); | |
| 749 } | |
| 750 | |
| 751 return S_OK; | |
| 752 } | |
| 753 | |
| 754 // All values are optional. | |
| 755 void AppManager::ReadInstallerResultApiValues( | |
| 756 const GUID& app_guid, | |
| 757 InstallerResult* installer_result, | |
| 758 DWORD* installer_error, | |
| 759 DWORD* installer_extra_code1, | |
| 760 CString* installer_result_uistring, | |
| 761 CString* installer_success_launch_cmd) { | |
| 762 ASSERT1(installer_result); | |
| 763 ASSERT1(installer_error); | |
| 764 ASSERT1(installer_extra_code1); | |
| 765 ASSERT1(installer_result_uistring); | |
| 766 ASSERT1(installer_success_launch_cmd); | |
| 767 | |
| 768 __mutexScope(registry_access_lock_); | |
| 769 | |
| 770 RegKey client_state_key; | |
| 771 HRESULT hr = OpenClientStateKey(app_guid, KEY_READ, &client_state_key); | |
| 772 if (FAILED(hr)) { | |
| 773 return; | |
| 774 } | |
| 775 | |
| 776 if (SUCCEEDED(client_state_key.GetValue( | |
| 777 kRegValueInstallerResult, | |
| 778 reinterpret_cast<DWORD*>(installer_result)))) { | |
| 779 CORE_LOG(L1, (_T("[InstallerResult in registry][%u]"), *installer_result)); | |
| 780 } | |
| 781 if (*installer_result >= INSTALLER_RESULT_MAX) { | |
| 782 CORE_LOG(LW, (_T("[Unsupported InstallerResult value]"))); | |
| 783 *installer_result = INSTALLER_RESULT_DEFAULT; | |
| 784 } | |
| 785 | |
| 786 if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallerError, | |
| 787 installer_error))) { | |
| 788 CORE_LOG(L1, (_T("[InstallerError in registry][%u]"), *installer_error)); | |
| 789 } | |
| 790 | |
| 791 if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallerExtraCode1, | |
| 792 installer_extra_code1))) { | |
| 793 CORE_LOG(L1, (_T("[InstallerExtraCode1 in registry][%u]"), | |
| 794 *installer_extra_code1)); | |
| 795 } | |
| 796 | |
| 797 if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallerResultUIString, | |
| 798 installer_result_uistring))) { | |
| 799 CORE_LOG(L1, (_T("[InstallerResultUIString in registry][%s]"), | |
| 800 *installer_result_uistring)); | |
| 801 } | |
| 802 | |
| 803 if (SUCCEEDED(client_state_key.GetValue( | |
| 804 kRegValueInstallerSuccessLaunchCmdLine, | |
| 805 installer_success_launch_cmd))) { | |
| 806 CORE_LOG(L1, (_T("[InstallerSuccessLaunchCmdLine in registry][%s]"), | |
| 807 *installer_success_launch_cmd)); | |
| 808 } | |
| 809 | |
| 810 ClearInstallerResultApiValues(app_guid); | |
| 811 } | |
| 812 | |
| 813 void AppManager::ClearInstallerResultApiValues(const GUID& app_guid) { | |
| 814 const CString client_state_key_name = GetClientStateKeyName(app_guid); | |
| 815 const CString update_key_name = | |
| 816 ConfigManager::Instance()->registry_update(is_machine_); | |
| 817 | |
| 818 ASSERT1(IsRegistryStableStateLockedByCaller()); | |
| 819 __mutexScope(registry_access_lock_); | |
| 820 | |
| 821 // Delete the old LastXXX values. These may not exist, so don't care if they | |
| 822 // fail. | |
| 823 RegKey::DeleteValue(client_state_key_name, | |
| 824 kRegValueLastInstallerResult); | |
| 825 RegKey::DeleteValue(client_state_key_name, | |
| 826 kRegValueLastInstallerResultUIString); | |
| 827 RegKey::DeleteValue(client_state_key_name, | |
| 828 kRegValueLastInstallerError); | |
| 829 RegKey::DeleteValue(client_state_key_name, | |
| 830 kRegValueLastInstallerExtraCode1); | |
| 831 RegKey::DeleteValue(client_state_key_name, | |
| 832 kRegValueLastInstallerSuccessLaunchCmdLine); | |
| 833 | |
| 834 // Also delete any values from Google\Update. | |
| 835 // TODO(Omaha): This is a temporary fix for bug 1539293. See TODO below. | |
| 836 RegKey::DeleteValue(update_key_name, | |
| 837 kRegValueLastInstallerResult); | |
| 838 RegKey::DeleteValue(update_key_name, | |
| 839 kRegValueLastInstallerResultUIString); | |
| 840 RegKey::DeleteValue(update_key_name, | |
| 841 kRegValueLastInstallerError); | |
| 842 RegKey::DeleteValue(update_key_name, | |
| 843 kRegValueLastInstallerExtraCode1); | |
| 844 RegKey::DeleteValue(update_key_name, | |
| 845 kRegValueLastInstallerSuccessLaunchCmdLine); | |
| 846 | |
| 847 // Rename current InstallerResultXXX values to LastXXX. | |
| 848 RegKey::RenameValue(client_state_key_name, | |
| 849 kRegValueInstallerResult, | |
| 850 kRegValueLastInstallerResult); | |
| 851 RegKey::RenameValue(client_state_key_name, | |
| 852 kRegValueInstallerError, | |
| 853 kRegValueLastInstallerError); | |
| 854 RegKey::RenameValue(client_state_key_name, | |
| 855 kRegValueInstallerExtraCode1, | |
| 856 kRegValueLastInstallerExtraCode1); | |
| 857 RegKey::RenameValue(client_state_key_name, | |
| 858 kRegValueInstallerResultUIString, | |
| 859 kRegValueLastInstallerResultUIString); | |
| 860 RegKey::RenameValue(client_state_key_name, | |
| 861 kRegValueInstallerSuccessLaunchCmdLine, | |
| 862 kRegValueLastInstallerSuccessLaunchCmdLine); | |
| 863 | |
| 864 // Copy over to the Google\Update key. | |
| 865 // TODO(Omaha3): This is a temporary fix for bug 1539293. Once Pack V2 is | |
| 866 // deprecated (Pack stops taking offline installers for new versions of | |
| 867 // Omaha apps), remove this. (It might be useful to leave the CopyValue calls | |
| 868 // in DEBUG builds only.) | |
| 869 RegKey::CopyValue(client_state_key_name, | |
| 870 update_key_name, | |
| 871 kRegValueLastInstallerResult); | |
| 872 RegKey::CopyValue(client_state_key_name, | |
| 873 update_key_name, | |
| 874 kRegValueLastInstallerError); | |
| 875 RegKey::CopyValue(client_state_key_name, | |
| 876 update_key_name, | |
| 877 kRegValueLastInstallerExtraCode1); | |
| 878 RegKey::CopyValue(client_state_key_name, | |
| 879 update_key_name, | |
| 880 kRegValueLastInstallerResultUIString); | |
| 881 RegKey::CopyValue(client_state_key_name, | |
| 882 update_key_name, | |
| 883 kRegValueLastInstallerSuccessLaunchCmdLine); | |
| 884 } | |
| 885 | |
| 886 // Reads the following values from Clients: | |
| 887 // pv | |
| 888 // lang (if present) | |
| 889 // name is not read. TODO(omaha3): May change if we persist name in registry. | |
| 890 HRESULT AppManager::ReadInstallerRegistrationValues(App* app) { | |
| 891 ASSERT1(app); | |
| 892 | |
| 893 const CString& app_guid_string = app->app_guid_string(); | |
| 894 | |
| 895 CORE_LOG(L2, (_T("[AppManager::ReadInstallerRegistrationValues][%s]"), | |
| 896 app_guid_string)); | |
| 897 | |
| 898 ASSERT1(app->model()->IsLockedByCaller()); | |
| 899 | |
| 900 __mutexScope(registry_access_lock_); | |
| 901 | |
| 902 RegKey client_key; | |
| 903 if (FAILED(OpenClientKey(app->app_guid(), &client_key))) { | |
| 904 OPT_LOG(LE, (_T("[Installer did not create key][%s]"), app_guid_string)); | |
| 905 return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY; | |
| 906 } | |
| 907 | |
| 908 CString version; | |
| 909 if (FAILED(client_key.GetValue(kRegValueProductVersion, &version))) { | |
| 910 OPT_LOG(LE, (_T("[Installer did not register][%s]"), app_guid_string)); | |
| 911 return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY; | |
| 912 } | |
| 913 | |
| 914 if (version.IsEmpty()) { | |
| 915 OPT_LOG(LE, (_T("[Installer did not write version][%s]"), app_guid_string)); | |
| 916 return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY; | |
| 917 } | |
| 918 | |
| 919 app->next_version()->set_version(version); | |
| 920 | |
| 921 CString language; | |
| 922 if (SUCCEEDED(client_key.GetValue(kRegValueLanguage, &language))) { | |
| 923 app->language_ = language; | |
| 924 } | |
| 925 | |
| 926 return S_OK; | |
| 927 } | |
| 928 | |
| 929 // Writes tttoken and updates relevant stats. | |
| 930 void AppManager::PersistSuccessfulUpdateCheckResponse( | |
| 931 const App& app, | |
| 932 bool is_update_available) { | |
| 933 CORE_LOG(L2, (_T("[AppManager::PersistSuccessfulUpdateCheckResponse]") | |
| 934 _T("[%s][%d]"), app.app_guid_string(), is_update_available)); | |
| 935 __mutexScope(registry_access_lock_); | |
| 936 | |
| 937 VERIFY1(SUCCEEDED(SetTTToken(app))); | |
| 938 | |
| 939 const CString client_state_key = GetClientStateKeyName(app.app_guid()); | |
| 940 | |
| 941 if (is_update_available) { | |
| 942 if (app.error_code() == GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY) { | |
| 943 // The error indicates is_update and updates are disabled by policy. | |
| 944 ASSERT1(app.is_update()); | |
| 945 app_registry_utils::ClearUpdateAvailableStats(client_state_key); | |
| 946 } else if (app.is_update()) { | |
| 947 // Only record an update available event for updates. | |
| 948 // We have other mechanisms, including IID, to track install success. | |
| 949 UpdateUpdateAvailableStats(app.app_guid()); | |
| 950 } | |
| 951 } else { | |
| 952 app_registry_utils::ClearUpdateAvailableStats(client_state_key); | |
| 953 app_registry_utils::PersistSuccessfulUpdateCheck(client_state_key); | |
| 954 } | |
| 955 } | |
| 956 | |
| 957 // Writes the following values to the ClientState key: | |
| 958 // pv (should be value written by installer in Clients key) | |
| 959 // lang (should be value written by installer in Clients key) | |
| 960 // iid (set/deleted) | |
| 961 // | |
| 962 // Does not write the following values because they were set by | |
| 963 // WritePreInstallData() and would not have changed during installation unless | |
| 964 // modified directly by the app installer. | |
| 965 // ap | |
| 966 // brand | |
| 967 // client | |
| 968 // experiment | |
| 969 // referral | |
| 970 // InstallTime (converted from diff) | |
| 971 // oeminstall | |
| 972 // eulaaccepted | |
| 973 // browser | |
| 974 // usagestats | |
| 975 // TODO(omaha3): Maybe we should delete referral at this point. Ask Chrome. | |
| 976 // | |
| 977 // Other values, such as tttoken were set after the update check. | |
| 978 // | |
| 979 // The caller is responsible for modifying the values in app_data as | |
| 980 // appropriate, including: | |
| 981 // * Updating values in app_data to reflect installer's values (pv and lang) | |
| 982 // * Clearing iid if appropriate. | |
| 983 // * Clearing the did run value. TODO(omaha3): Depends on TODO below. | |
| 984 void AppManager::PersistSuccessfulInstall(const App& app) { | |
| 985 CORE_LOG(L2, (_T("[AppManager::PersistSuccessfulInstall][%s]"), | |
| 986 app.app_guid_string())); | |
| 987 | |
| 988 ASSERT1(IsRegistryStableStateLockedByCaller()); | |
| 989 __mutexScope(registry_access_lock_); | |
| 990 | |
| 991 ASSERT1(!::IsEqualGUID(kGoopdateGuid, app.app_guid())); | |
| 992 | |
| 993 RegKey client_state_key; | |
| 994 VERIFY1(SUCCEEDED(CreateClientStateKey(app.app_guid(), &client_state_key))); | |
| 995 | |
| 996 VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueProductVersion, | |
| 997 app.next_version()->version()))); | |
| 998 | |
| 999 if (!app.language().IsEmpty()) { | |
| 1000 VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueLanguage, | |
| 1001 app.language()))); | |
| 1002 } | |
| 1003 | |
| 1004 if (::IsEqualGUID(app.iid(), GUID_NULL)) { | |
| 1005 VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueInstallationId))); | |
| 1006 } else { | |
| 1007 VERIFY1(SUCCEEDED(client_state_key.SetValue( | |
| 1008 kRegValueInstallationId, | |
| 1009 GuidToString(app.iid())))); | |
| 1010 } | |
| 1011 | |
| 1012 const CString client_state_key_path = GetClientStateKeyName(app.app_guid()); | |
| 1013 app_registry_utils::PersistSuccessfulInstall(client_state_key_path, | |
| 1014 app.is_update(), | |
| 1015 false); // TODO(omaha3): offline | |
| 1016 } | |
| 1017 | |
| 1018 HRESULT AppManager::SynchronizeClientState(const GUID& app_guid) { | |
| 1019 __mutexScope(registry_access_lock_); | |
| 1020 | |
| 1021 RegKey client_key; | |
| 1022 HRESULT hr = OpenClientKey(app_guid, &client_key); | |
| 1023 if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { | |
| 1024 return S_OK; | |
| 1025 } | |
| 1026 if (FAILED(hr)) { | |
| 1027 return hr; | |
| 1028 } | |
| 1029 | |
| 1030 RegKey client_state_key; | |
| 1031 hr = CreateClientStateKey(app_guid, &client_state_key); | |
| 1032 if (FAILED(hr)) { | |
| 1033 return hr; | |
| 1034 } | |
| 1035 | |
| 1036 CString version; | |
| 1037 client_key.GetValue(kRegValueProductVersion, &version); | |
| 1038 if (FAILED(hr)) { | |
| 1039 return hr; | |
| 1040 } | |
| 1041 hr = client_state_key.SetValue(kRegValueProductVersion, version); | |
| 1042 if (FAILED(hr)) { | |
| 1043 return hr; | |
| 1044 } | |
| 1045 | |
| 1046 CString language; | |
| 1047 client_key.GetValue(kRegValueLanguage, &language); | |
| 1048 if (!language.IsEmpty()) { | |
| 1049 return client_state_key.SetValue(kRegValueLanguage, language); | |
| 1050 } | |
| 1051 | |
| 1052 return S_OK; | |
| 1053 } | |
| 1054 | |
| 1055 // TODO(omaha3): tttoken is not currently read from the server response. | |
| 1056 // TODO(omaha3): When implementing offline, we must make sure that the tttoken | |
| 1057 // is not deleted by the offline response processing. | |
| 1058 // TODO(omaha3): Having the parser write the server's token to the same member | |
| 1059 // that is used for the value from the tag exposes this value to the COM setter. | |
| 1060 // It would be nice to avoid that, possibly by only allowing that setter to work | |
| 1061 // in certain states. | |
| 1062 HRESULT AppManager::SetTTToken(const App& app) { | |
| 1063 CORE_LOG(L3, (_T("[AppManager::SetTTToken][token=%s]"), app.tt_token())); | |
| 1064 | |
| 1065 __mutexScope(registry_access_lock_); | |
| 1066 | |
| 1067 RegKey client_state_key; | |
| 1068 HRESULT hr = CreateClientStateKey(app.app_guid(), &client_state_key); | |
| 1069 if (FAILED(hr)) { | |
| 1070 return hr; | |
| 1071 } | |
| 1072 | |
| 1073 if (app.tt_token().IsEmpty()) { | |
| 1074 return client_state_key.DeleteValue(kRegValueTTToken); | |
| 1075 } else { | |
| 1076 return client_state_key.SetValue(kRegValueTTToken, app.tt_token()); | |
| 1077 } | |
| 1078 } | |
| 1079 | |
| 1080 void AppManager::ClearOemInstalled(const AppIdVector& app_ids) { | |
| 1081 __mutexScope(registry_access_lock_); | |
| 1082 | |
| 1083 AppIdVector::const_iterator it; | |
| 1084 for (it = app_ids.begin(); it != app_ids.end(); ++it) { | |
| 1085 ASSERT1(IsAppOemInstalledAndEulaAccepted(*it)); | |
| 1086 RegKey state_key; | |
| 1087 | |
| 1088 GUID app_guid = GUID_NULL; | |
| 1089 HRESULT hr = StringToGuidSafe(*it, &app_guid); | |
| 1090 if (FAILED(hr)) { | |
| 1091 continue; | |
| 1092 } | |
| 1093 | |
| 1094 hr = OpenClientStateKey(app_guid, KEY_ALL_ACCESS, &state_key); | |
| 1095 if (FAILED(hr)) { | |
| 1096 continue; | |
| 1097 } | |
| 1098 | |
| 1099 VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueOemInstall))); | |
| 1100 } | |
| 1101 } | |
| 1102 | |
| 1103 void AppManager::UpdateUpdateAvailableStats(const GUID& app_guid) { | |
| 1104 __mutexScope(registry_access_lock_); | |
| 1105 | |
| 1106 RegKey state_key; | |
| 1107 HRESULT hr = CreateClientStateKey(app_guid, &state_key); | |
| 1108 if (FAILED(hr)) { | |
| 1109 ASSERT1(false); | |
| 1110 return; | |
| 1111 } | |
| 1112 | |
| 1113 DWORD update_available_count(0); | |
| 1114 hr = state_key.GetValue(kRegValueUpdateAvailableCount, | |
| 1115 &update_available_count); | |
| 1116 if (FAILED(hr)) { | |
| 1117 update_available_count = 0; | |
| 1118 } | |
| 1119 ++update_available_count; | |
| 1120 VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableCount, | |
| 1121 update_available_count))); | |
| 1122 | |
| 1123 DWORD64 update_available_since_time(0); | |
| 1124 hr = state_key.GetValue(kRegValueUpdateAvailableSince, | |
| 1125 &update_available_since_time); | |
| 1126 if (FAILED(hr)) { | |
| 1127 // There is no existing value, so this must be the first update notice. | |
| 1128 VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableSince, | |
| 1129 GetCurrent100NSTime()))); | |
| 1130 | |
| 1131 // TODO(omaha): It would be nice to report the version that we were first | |
| 1132 // told to update to. This is available in UpdateResponse but we do not | |
| 1133 // currently send it down in update responses. If we start using it, add | |
| 1134 // kRegValueFirstUpdateResponseVersion. | |
| 1135 } | |
| 1136 } | |
| 1137 | |
| 1138 // Returns 0 for any values that are not found. | |
| 1139 void AppManager::ReadUpdateAvailableStats( | |
| 1140 const GUID& app_guid, | |
| 1141 DWORD* update_responses, | |
| 1142 DWORD64* time_since_first_response_ms) { | |
| 1143 ASSERT1(update_responses); | |
| 1144 ASSERT1(time_since_first_response_ms); | |
| 1145 *update_responses = 0; | |
| 1146 *time_since_first_response_ms = 0; | |
| 1147 | |
| 1148 __mutexScope(registry_access_lock_); | |
| 1149 | |
| 1150 RegKey state_key; | |
| 1151 HRESULT hr = OpenClientStateKey(app_guid, KEY_READ, &state_key); | |
| 1152 if (FAILED(hr)) { | |
| 1153 CORE_LOG(LW, (_T("[App ClientState key does not exist][%s]"), | |
| 1154 GuidToString(app_guid))); | |
| 1155 return; | |
| 1156 } | |
| 1157 | |
| 1158 DWORD update_responses_in_reg(0); | |
| 1159 hr = state_key.GetValue(kRegValueUpdateAvailableCount, | |
| 1160 &update_responses_in_reg); | |
| 1161 if (SUCCEEDED(hr)) { | |
| 1162 *update_responses = update_responses_in_reg; | |
| 1163 } | |
| 1164 | |
| 1165 DWORD64 update_available_since_time(0); | |
| 1166 hr = state_key.GetValue(kRegValueUpdateAvailableSince, | |
| 1167 &update_available_since_time); | |
| 1168 if (SUCCEEDED(hr)) { | |
| 1169 const DWORD64 current_time = GetCurrent100NSTime(); | |
| 1170 ASSERT1(update_available_since_time <= current_time); | |
| 1171 const DWORD64 time_since_first_response_in_100ns = | |
| 1172 current_time - update_available_since_time; | |
| 1173 *time_since_first_response_ms = | |
| 1174 time_since_first_response_in_100ns / kMillisecsTo100ns; | |
| 1175 } | |
| 1176 } | |
| 1177 | |
| 1178 uint32 AppManager::GetInstallTimeDiffSec(const GUID& app_guid) const { | |
| 1179 if (!IsAppRegistered(app_guid) && !IsAppUninstalled(app_guid)) { | |
| 1180 return kInitialInstallTimeDiff; | |
| 1181 } | |
| 1182 | |
| 1183 RegKey client_state_key; | |
| 1184 HRESULT hr = OpenClientStateKey(app_guid, KEY_READ, &client_state_key); | |
| 1185 if (FAILED(hr)) { | |
| 1186 return 0; | |
| 1187 } | |
| 1188 | |
| 1189 DWORD install_time(0); | |
| 1190 DWORD install_time_diff_sec(0); | |
| 1191 if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallTimeSec, | |
| 1192 &install_time))) { | |
| 1193 const uint32 now = Time64ToInt32(GetCurrent100NSTime()); | |
| 1194 if (0 != install_time && now >= install_time) { | |
| 1195 install_time_diff_sec = now - install_time; | |
| 1196 // TODO(omaha3): Restore this assert. In Omaha 2, this function gets | |
| 1197 // called as part of installation verification and Job::UpdateJob(), so | |
| 1198 // the value can be 0. This will not be the case in Omaha 3. | |
| 1199 // ASSERT1(install_time_diff_sec != 0); | |
| 1200 } | |
| 1201 } | |
| 1202 | |
| 1203 return install_time_diff_sec; | |
| 1204 } | |
| 1205 | |
| 1206 // Clear the Installation ID if at least one of the conditions is true: | |
| 1207 // 1) DidRun==yes. First run is the last time we want to use the Installation | |
| 1208 // ID. So delete Installation ID if it is present. | |
| 1209 // 2) kMaxLifeOfInstallationIDSec has passed since the app was installed. This | |
| 1210 // is to ensure that Installation ID is cleared even if DidRun is never set. | |
| 1211 // 3) The app is Omaha. Always delete Installation ID if it is present | |
| 1212 // because DidRun does not apply. | |
| 1213 HRESULT AppManager::ClearInstallationId(const App& app) { | |
| 1214 ASSERT1(app.model()->IsLockedByCaller()); | |
| 1215 __mutexScope(registry_access_lock_); | |
| 1216 | |
| 1217 if (::IsEqualGUID(app.iid(), GUID_NULL)) { | |
| 1218 return S_OK; | |
| 1219 } | |
| 1220 | |
| 1221 if ((ACTIVE_RUN == app.did_run()) || | |
| 1222 (kMaxLifeOfInstallationIDSec <= app.install_time_diff_sec()) || | |
| 1223 (::IsEqualGUID(kGoopdateGuid, app.app_guid()))) { | |
| 1224 CORE_LOG(L1, (_T("[Deleting iid for app][%s]"), app.app_guid_string())); | |
| 1225 | |
| 1226 RegKey client_state_key; | |
| 1227 HRESULT hr = CreateClientStateKey(app.app_guid(), &client_state_key); | |
| 1228 if (FAILED(hr)) { | |
| 1229 return hr; | |
| 1230 } | |
| 1231 | |
| 1232 return client_state_key.DeleteValue(kRegValueInstallationId); | |
| 1233 } | |
| 1234 | |
| 1235 return S_OK; | |
| 1236 } | |
| 1237 | |
| 1238 void AppManager::SetLastPingDayStartTime(const App& app, | |
| 1239 int elapsed_seconds_since_day_start) { | |
| 1240 ASSERT1(elapsed_seconds_since_day_start >= 0); | |
| 1241 ASSERT1(elapsed_seconds_since_day_start < kMaxTimeSinceMidnightSec); | |
| 1242 ASSERT1(app.model()->IsLockedByCaller()); | |
| 1243 | |
| 1244 __mutexScope(registry_access_lock_); | |
| 1245 | |
| 1246 int now = Time64ToInt32(GetCurrent100NSTime()); | |
| 1247 | |
| 1248 RegKey client_state_key; | |
| 1249 if (FAILED(CreateClientStateKey(app.app_guid(), &client_state_key))) { | |
| 1250 return; | |
| 1251 } | |
| 1252 | |
| 1253 bool did_send_active_ping = (app.did_run() == ACTIVE_RUN && | |
| 1254 app.days_since_last_active_ping() != 0); | |
| 1255 if (did_send_active_ping) { | |
| 1256 VERIFY1(SUCCEEDED(client_state_key.SetValue( | |
| 1257 kRegValueActivePingDayStartSec, | |
| 1258 static_cast<DWORD>(now - elapsed_seconds_since_day_start)))); | |
| 1259 } | |
| 1260 | |
| 1261 bool did_send_roll_call = (app.days_since_last_roll_call() != 0); | |
| 1262 if (did_send_roll_call) { | |
| 1263 VERIFY1(SUCCEEDED(client_state_key.SetValue( | |
| 1264 kRegValueRollCallDayStartSec, | |
| 1265 static_cast<DWORD>(now - elapsed_seconds_since_day_start)))); | |
| 1266 } | |
| 1267 } | |
| 1268 | |
| 1269 // Writes the day start time when last active ping/roll call happened to | |
| 1270 // registry if the corresponding ping has been sent. | |
| 1271 // Removes installation id, if did run = true or if goopdate. | |
| 1272 // Clears did run. | |
| 1273 HRESULT AppManager::PersistUpdateCheckSuccessfullySent( | |
| 1274 const App& app, | |
| 1275 int elapsed_seconds_since_day_start) { | |
| 1276 ASSERT1(app.model()->IsLockedByCaller()); | |
| 1277 | |
| 1278 ApplicationUsageData app_usage(app.app_bundle()->is_machine(), | |
| 1279 vista_util::IsVistaOrLater()); | |
| 1280 VERIFY1(SUCCEEDED(app_usage.ResetDidRun(app.app_guid_string()))); | |
| 1281 | |
| 1282 SetLastPingDayStartTime(app, elapsed_seconds_since_day_start); | |
| 1283 | |
| 1284 // Handle the installation id. | |
| 1285 VERIFY1(SUCCEEDED(ClearInstallationId(app))); | |
| 1286 | |
| 1287 return S_OK; | |
| 1288 } | |
| 1289 | |
| 1290 HRESULT AppManager::RemoveClientState(const GUID& app_guid) { | |
| 1291 CORE_LOG(L2, (_T("[AppManager::RemoveClientState][%s]"), | |
| 1292 GuidToString(app_guid))); | |
| 1293 ASSERT1(IsRegistryStableStateLockedByCaller()); | |
| 1294 __mutexScope(registry_access_lock_); | |
| 1295 | |
| 1296 ASSERT1(!IsAppRegistered(app_guid)); | |
| 1297 | |
| 1298 return app_registry_utils::RemoveClientState(is_machine_, | |
| 1299 GuidToString(app_guid)); | |
| 1300 } | |
| 1301 | |
| 1302 // TODO(omaha3): May not need these | |
| 1303 #if 0 | |
| 1304 // Writes 0.0.0.1 to pv. This value avoids any special cases, such as initial | |
| 1305 // install rules, for 0.0.0.0, while being unlikely to be higher than the | |
| 1306 // product's actual current version. | |
| 1307 HRESULT AppManager::RegisterProduct(const GUID& product_guid, | |
| 1308 const CString& product_name) { | |
| 1309 const TCHAR* const kRegisterProductVersion = _T("0.0.0.1"); | |
| 1310 | |
| 1311 __mutexScope(GetRegistryStableStateLock()); | |
| 1312 RegKey client_key; | |
| 1313 HRESULT hr = client_key.Create(GetClientKeyName(GUID_NULL, product_guid)); | |
| 1314 if (FAILED(hr)) { | |
| 1315 return hr; | |
| 1316 } | |
| 1317 | |
| 1318 hr = client_key.SetValue(kRegValueProductVersion, kRegisterProductVersion); | |
| 1319 if (FAILED(hr)) { | |
| 1320 return hr; | |
| 1321 } | |
| 1322 | |
| 1323 // AppName is not a required parameter since it's only used for being able to | |
| 1324 // easily tell what application is there when reading the registry. | |
| 1325 VERIFY1(SUCCEEDED(client_key.SetValue(kRegValueAppName, product_name))); | |
| 1326 | |
| 1327 return S_OK; | |
| 1328 } | |
| 1329 | |
| 1330 HRESULT AppManager::UnregisterProduct(const GUID& product_guid) { | |
| 1331 __mutexScope(GetRegistryStableStateLock()); | |
| 1332 return RegKey::DeleteKey(GetClientKeyName(GUID_NULL, product_guid), true); | |
| 1333 } | |
| 1334 #endif | |
| 1335 | |
| 1336 } // namespace omaha | |
| OLD | NEW |