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 |