Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(535)

Unified Diff: rlz/chromeos/lib/rlz_value_store_chromeos.cc

Issue 11365107: [cros] RlzValueStore implementation for ChromeOS. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Revert to Chrome-only Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « rlz/chromeos/lib/rlz_value_store_chromeos.h ('k') | rlz/lib/financial_ping.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: rlz/chromeos/lib/rlz_value_store_chromeos.cc
diff --git a/rlz/win/lib/rlz_value_store_registry.cc b/rlz/chromeos/lib/rlz_value_store_chromeos.cc
similarity index 10%
copy from rlz/win/lib/rlz_value_store_registry.cc
copy to rlz/chromeos/lib/rlz_value_store_chromeos.cc
index 36c53d578a9f7ff6e9447c6e18602abcbea0ce7a..2f75887643a29e89ddb2ea7367bff28453468e29 100644
--- a/rlz/win/lib/rlz_value_store_registry.cc
+++ b/rlz/chromeos/lib/rlz_value_store_chromeos.cc
@@ -2,383 +2,283 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "rlz/win/lib/rlz_value_store_registry.h"
-
-#include "base/win/registry.h"
-#include "base/stringprintf.h"
-#include "base/utf_string_conversions.h"
-#include "rlz/lib/assert.h"
+#include "rlz/chromeos/lib/rlz_value_store_chromeos.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/prefs/json_pref_store.h"
+#include "base/sequenced_task_runner.h"
+#include "base/string_number_conversions.h"
+#include "base/values.h"
#include "rlz/lib/lib_values.h"
+#include "rlz/lib/recursive_lock.h"
#include "rlz/lib/rlz_lib.h"
-#include "rlz/lib/string_utils.h"
-#include "rlz/win/lib/registry_util.h"
namespace rlz_lib {
namespace {
-//
-// Registry keys:
-//
-// RLZ's are stored as:
-// <AccessPointName> = <RLZ value> @ kRootKey\kLibKeyName\kRlzsSubkeyName.
-//
-// Events are stored as:
-// <AccessPointName><EventName> = 1 @
-// HKCU\kLibKeyName\kEventsSubkeyName\GetProductName(product).
-//
-// The OEM Deal Confirmation Code (DCC) is stored as
-// kDccValueName = <DCC value> @ HKLM\kLibKeyName
-//
-// The last ping time, per product is stored as:
-// GetProductName(product) = <last ping time> @
-// HKCU\kLibKeyName\kPingTimesSubkeyName.
-//
-// The server does not care about any of these constants.
-//
-const char kLibKeyName[] = "Software\\Google\\Common\\Rlz";
-const wchar_t kGoogleKeyName[] = L"Software\\Google";
-const wchar_t kGoogleCommonKeyName[] = L"Software\\Google\\Common";
-const char kRlzsSubkeyName[] = "RLZs";
-const char kEventsSubkeyName[] = "Events";
-const char kStatefulEventsSubkeyName[] = "StatefulEvents";
-const char kPingTimesSubkeyName[] = "PTimes";
-
-std::wstring GetWideProductName(Product product) {
- return ASCIIToWide(GetProductName(product));
-}
-
-void AppendBrandToString(std::string* str) {
- std::string brand(SupplementaryBranding::GetBrand());
- if (!brand.empty())
- base::StringAppendF(str, "\\_%s", brand.c_str());
-}
-
-// Function to get the specific registry keys.
-bool GetRegKey(const char* name, REGSAM access, base::win::RegKey* key) {
- std::string key_location;
- base::StringAppendF(&key_location, "%s\\%s", kLibKeyName, name);
- AppendBrandToString(&key_location);
-
- LONG ret = ERROR_SUCCESS;
- if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) {
- ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
- access);
- } else {
- ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
- access);
- }
+// Product names.
+const char kProductChrome[] = "chrome";
+const char kProductOther[] = "other";
- return ret == ERROR_SUCCESS;
-}
+// Key names.
+const char kPingTimeKey[] = "ping_time";
+const char kAccessPointKey[] = "access_points";
+const char kProductEventKey[] = "product_events";
+const char kStatefulEventKey[] = "stateful_events";
-bool GetPingTimesRegKey(REGSAM access, base::win::RegKey* key) {
- return GetRegKey(kPingTimesSubkeyName, access, key);
-}
+// Brand name used when there is no supplementary brand name.
+const char kNoSupplementaryBrand[] = "_";
+// RLZ store filename.
+const FilePath::CharType kRLZDataFileName[] = FILE_PATH_LITERAL("RLZ Data");
-bool GetEventsRegKey(const char* event_type,
- const rlz_lib::Product* product,
- REGSAM access, base::win::RegKey* key) {
- std::string key_location;
- base::StringAppendF(&key_location, "%s\\%s", kLibKeyName,
- event_type);
- AppendBrandToString(&key_location);
+// RLZ store path for testing.
+FilePath g_testing_rlz_store_path_;
- if (product != NULL) {
- std::string product_name = GetProductName(*product);
- if (product_name.empty())
- return false;
-
- base::StringAppendF(&key_location, "\\%s", product_name.c_str());
- }
-
- LONG ret = ERROR_SUCCESS;
- if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) {
- ret = key->Create(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
- access);
- } else {
- ret = key->Open(HKEY_CURRENT_USER, ASCIIToWide(key_location).c_str(),
- access);
- }
+// Returns file path of the RLZ storage.
+FilePath GetRlzStorePath() {
+ return g_testing_rlz_store_path_.empty() ?
+ file_util::GetHomeDir().Append(kRLZDataFileName) :
+ g_testing_rlz_store_path_.Append(kRLZDataFileName);
+}
- return ret == ERROR_SUCCESS;
+// Returns the dictionary key for storing access point-related prefs.
+std::string GetKeyName(std::string key, AccessPoint access_point) {
+ std::string brand = SupplementaryBranding::GetBrand();
+ if (brand.empty())
+ brand = kNoSupplementaryBrand;
+ return key + "." + GetAccessPointName(access_point) + "." + brand;
}
-bool GetAccessPointRlzsRegKey(REGSAM access, base::win::RegKey* key) {
- return GetRegKey(kRlzsSubkeyName, access, key);
+// Returns the dictionary key for storing product-related prefs.
+std::string GetKeyName(std::string key, Product product) {
+ std::string brand = SupplementaryBranding::GetBrand();
+ if (brand.empty())
+ brand = kNoSupplementaryBrand;
+ return key + "." + GetProductName(product) + "." + brand;
}
-bool ClearAllProductEventValues(rlz_lib::Product product, const char* key) {
- std::wstring product_name = GetWideProductName(product);
- if (product_name.empty())
- return false;
+} // namespace
- base::win::RegKey reg_key;
- GetEventsRegKey(key, NULL, KEY_WRITE, &reg_key);
- reg_key.DeleteKey(product_name.c_str());
+// static
+base::SequencedTaskRunner* RlzValueStoreChromeOS::io_task_runner_ = NULL;
- // Verify that the value no longer exists.
- base::win::RegKey product_events(
- reg_key.Handle(), product_name.c_str(), KEY_READ);
- if (product_events.Valid()) {
- ASSERT_STRING("ClearAllProductEvents: Key deletion failed");
- return false;
- }
+// static
+bool RlzValueStoreChromeOS::created_;
- return true;
+// static
+RlzValueStoreChromeOS* RlzValueStoreChromeOS::GetInstance() {
+ return Singleton<RlzValueStoreChromeOS>::get();
}
-// Deletes a registry key if it exists and has no subkeys or values.
-// TODO: Move this to a registry_utils file and add unittest.
-bool DeleteKeyIfEmpty(HKEY root_key, const wchar_t* key_name) {
- if (!key_name) {
- ASSERT_STRING("DeleteKeyIfEmpty: key_name is NULL");
- return false;
- } else { // Scope needed for RegKey
- base::win::RegKey key(root_key, key_name, KEY_READ);
- if (!key.Valid())
- return true; // Key does not exist - nothing to do.
-
- base::win::RegistryKeyIterator key_iter(root_key, key_name);
- if (key_iter.SubkeyCount() > 0)
- return true; // Not empty, so nothing to do
-
- base::win::RegistryValueIterator value_iter(root_key, key_name);
- if (value_iter.ValueCount() > 0)
- return true; // Not empty, so nothing to do
- }
-
- // The key is empty - delete it now.
- base::win::RegKey key(root_key, L"", KEY_WRITE);
- return key.DeleteKey(key_name) == ERROR_SUCCESS;
+// static
+void RlzValueStoreChromeOS::SetIOTaskRunner(
+ base::SequencedTaskRunner* io_task_runner) {
+ io_task_runner_ = io_task_runner;
+ // Make sure |io_task_runner_| lives until constructor is called.
+ io_task_runner_->AddRef();
}
-} // namespace
-
// static
-std::wstring RlzValueStoreRegistry::GetWideLibKeyName() {
- return ASCIIToWide(kLibKeyName);
+void RlzValueStoreChromeOS::ResetForTesting() {
+ // Make sure we don't create an instance if it didn't exist.
+ if (created_)
+ GetInstance()->ReadPrefs();
}
-bool RlzValueStoreRegistry::HasAccess(AccessType type) {
- return HasUserKeyAccess(type == kWriteAccess);
+RlzValueStoreChromeOS::RlzValueStoreChromeOS() {
+ ReadPrefs();
+ created_ = true;
}
-bool RlzValueStoreRegistry::WritePingTime(Product product, int64 time) {
- base::win::RegKey key;
- std::wstring product_name = GetWideProductName(product);
- return GetPingTimesRegKey(KEY_WRITE, &key) &&
- key.WriteValue(product_name.c_str(), &time, sizeof(time),
- REG_QWORD) == ERROR_SUCCESS;
+RlzValueStoreChromeOS::~RlzValueStoreChromeOS() {
}
-bool RlzValueStoreRegistry::ReadPingTime(Product product, int64* time) {
- base::win::RegKey key;
- std::wstring product_name = GetWideProductName(product);
- return GetPingTimesRegKey(KEY_READ, &key) &&
- key.ReadInt64(product_name.c_str(), time) == ERROR_SUCCESS;
+bool RlzValueStoreChromeOS::HasAccess(AccessType type) {
+ return type == kReadAccess || !rlz_store_->ReadOnly();
}
-bool RlzValueStoreRegistry::ClearPingTime(Product product) {
- base::win::RegKey key;
- GetPingTimesRegKey(KEY_WRITE, &key);
-
- std::wstring product_name = GetWideProductName(product);
- key.DeleteValue(product_name.c_str());
+bool RlzValueStoreChromeOS::WritePingTime(Product product, int64 time) {
+ std::string value = base::Int64ToString(time);
+ rlz_store_->SetValue(GetKeyName(kPingTimeKey, product),
+ base::Value::CreateStringValue(value));
+ return true;
+}
- // Verify deletion.
- uint64 value;
- DWORD size = sizeof(value);
- if (key.ReadValue(
- product_name.c_str(), &value, &size, NULL) == ERROR_SUCCESS) {
- ASSERT_STRING("RlzValueStoreRegistry::ClearPingTime: Failed to delete.");
- return false;
- }
+bool RlzValueStoreChromeOS::ReadPingTime(Product product, int64* time) {
+ const base::Value* value = NULL;
+ rlz_store_->GetValue(GetKeyName(kPingTimeKey, product), &value);
+ std::string s_value;
+ return value && value->GetAsString(&s_value) &&
+ base::StringToInt64(s_value, time);
+}
+bool RlzValueStoreChromeOS::ClearPingTime(Product product) {
+ rlz_store_->RemoveValue(GetKeyName(kPingTimeKey, product));
return true;
}
-bool RlzValueStoreRegistry::WriteAccessPointRlz(AccessPoint access_point,
+bool RlzValueStoreChromeOS::WriteAccessPointRlz(AccessPoint access_point,
const char* new_rlz) {
- const char* access_point_name = GetAccessPointName(access_point);
- if (!access_point_name)
- return false;
-
- std::wstring access_point_name_wide(ASCIIToWide(access_point_name));
- base::win::RegKey key;
- GetAccessPointRlzsRegKey(KEY_WRITE, &key);
-
- if (!RegKeyWriteValue(key, access_point_name_wide.c_str(), new_rlz)) {
- ASSERT_STRING("SetAccessPointRlz: Could not write the new RLZ value");
- return false;
- }
+ rlz_store_->SetValue(
+ GetKeyName(kAccessPointKey, access_point),
+ base::Value::CreateStringValue(new_rlz));
return true;
}
-bool RlzValueStoreRegistry::ReadAccessPointRlz(AccessPoint access_point,
+bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point,
char* rlz,
size_t rlz_size) {
- const char* access_point_name = GetAccessPointName(access_point);
- if (!access_point_name)
- return false;
-
- size_t size = rlz_size;
- base::win::RegKey key;
- GetAccessPointRlzsRegKey(KEY_READ, &key);
- if (!RegKeyReadValue(key, ASCIIToWide(access_point_name).c_str(),
- rlz, &size)) {
- rlz[0] = 0;
- if (size > rlz_size) {
- ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size");
- return false;
- }
+ const base::Value* value = NULL;
+ rlz_store_->GetValue(
+ GetKeyName(kAccessPointKey, access_point), &value);
+ std::string s_value;
+ if (value)
+ value->GetAsString(&s_value);
+ if (s_value.size() < rlz_size) {
+ strncpy(rlz, s_value.c_str(), rlz_size);
+ return true;
}
- return true;
+ if (rlz_size > 0)
+ *rlz = '\0';
+ return false;
}
-bool RlzValueStoreRegistry::ClearAccessPointRlz(AccessPoint access_point) {
- const char* access_point_name = GetAccessPointName(access_point);
- if (!access_point_name)
- return false;
-
- std::wstring access_point_name_wide(ASCIIToWide(access_point_name));
- base::win::RegKey key;
- GetAccessPointRlzsRegKey(KEY_WRITE, &key);
-
- key.DeleteValue(access_point_name_wide.c_str());
-
- // Verify deletion.
- DWORD value;
- if (key.ReadValueDW(access_point_name_wide.c_str(), &value) ==
- ERROR_SUCCESS) {
- ASSERT_STRING("SetAccessPointRlz: Could not clear the RLZ value.");
- return false;
- }
+bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) {
+ rlz_store_->RemoveValue(
+ GetKeyName(kAccessPointKey, access_point));
return true;
}
-bool RlzValueStoreRegistry::AddProductEvent(Product product,
+bool RlzValueStoreChromeOS::AddProductEvent(Product product,
const char* event_rlz) {
- std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
- base::win::RegKey reg_key;
- GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, &reg_key);
- if (reg_key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) {
- ASSERT_STRING("AddProductEvent: Could not write the new event value");
- return false;
- }
-
- return true;
+ return AddValueToList(GetKeyName(kProductEventKey, product),
+ base::Value::CreateStringValue(event_rlz));
}
-bool RlzValueStoreRegistry::ReadProductEvents(Product product,
- std::vector<std::string>* events) {
- // Open the events key.
- base::win::RegKey events_key;
- GetEventsRegKey(kEventsSubkeyName, &product, KEY_READ, &events_key);
- if (!events_key.Valid())
+bool RlzValueStoreChromeOS::ReadProductEvents(
+ Product product,
+ std::vector<std::string>* events) {
+ base::ListValue* events_list = GetList(GetKeyName(kProductEventKey, product));
+ if (!events_list)
return false;
-
- // Append the events to the buffer.
- int num_values = 0;
- LONG result = ERROR_SUCCESS;
- for (num_values = 0; result == ERROR_SUCCESS; ++num_values) {
- // Max 32767 bytes according to MSDN, but we never use that much.
- const size_t kMaxValueNameLength = 2048;
- char buffer[kMaxValueNameLength];
- DWORD size = arraysize(buffer);
-
- result = RegEnumValueA(events_key.Handle(), num_values, buffer, &size,
- NULL, NULL, NULL, NULL);
- if (result == ERROR_SUCCESS)
- events->push_back(std::string(buffer));
+ events->clear();
+ for (size_t i = 0; i < events_list->GetSize(); ++i) {
+ std::string event;
+ if (events_list->GetString(i, &event))
+ events->push_back(event);
}
-
- return result == ERROR_NO_MORE_ITEMS;
+ return true;
}
-bool RlzValueStoreRegistry::ClearProductEvent(Product product,
+bool RlzValueStoreChromeOS::ClearProductEvent(Product product,
const char* event_rlz) {
- std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
- base::win::RegKey key;
- GetEventsRegKey(kEventsSubkeyName, &product, KEY_WRITE, &key);
- key.DeleteValue(event_rlz_wide.c_str());
-
- // Verify deletion.
- DWORD value;
- if (key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS) {
- ASSERT_STRING("ClearProductEvent: Could not delete the event value.");
- return false;
- }
+ base::StringValue event_value(event_rlz);
+ return RemoveValueFromList(GetKeyName(kProductEventKey, product),
+ event_value);
+}
+bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) {
+ rlz_store_->RemoveValue(GetKeyName(kProductEventKey, product));
return true;
}
-bool RlzValueStoreRegistry::ClearAllProductEvents(Product product) {
- return ClearAllProductEventValues(product, kEventsSubkeyName);
+bool RlzValueStoreChromeOS::AddStatefulEvent(Product product,
+ const char* event_rlz) {
+ return AddValueToList(GetKeyName(kStatefulEventKey, product),
+ base::Value::CreateStringValue(event_rlz));
}
-bool RlzValueStoreRegistry::AddStatefulEvent(Product product,
- const char* event_rlz) {
- base::win::RegKey key;
- std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
- if (!GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_WRITE, &key) ||
- key.WriteValue(event_rlz_wide.c_str(), 1) != ERROR_SUCCESS) {
- ASSERT_STRING(
- "AddStatefulEvent: Could not write the new stateful event");
- return false;
- }
+bool RlzValueStoreChromeOS::IsStatefulEvent(Product product,
+ const char* event_rlz) {
+ base::ListValue* events_list =
+ GetList(GetKeyName(kStatefulEventKey, product));
+ base::StringValue event_value(event_rlz);
+ return events_list && events_list->Find(event_value) != events_list->end();
+}
+bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) {
+ rlz_store_->RemoveValue(GetKeyName(kStatefulEventKey, product));
return true;
}
-bool RlzValueStoreRegistry::IsStatefulEvent(Product product,
- const char* event_rlz) {
- DWORD value;
- base::win::RegKey key;
- GetEventsRegKey(kStatefulEventsSubkeyName, &product, KEY_READ, &key);
- std::wstring event_rlz_wide(ASCIIToWide(event_rlz));
- return key.ReadValueDW(event_rlz_wide.c_str(), &value) == ERROR_SUCCESS;
+void RlzValueStoreChromeOS::CollectGarbage() {
+ NOTIMPLEMENTED();
+}
+
+void RlzValueStoreChromeOS::ReadPrefs() {
+ DCHECK(io_task_runner_)
+ << "Calling GetInstance or ResetForTesting before SetIOTaskRunner?";
+ rlz_store_ = new JsonPrefStore(GetRlzStorePath(), io_task_runner_);
+ rlz_store_->ReadPrefs();
+ switch (rlz_store_->GetReadError()) {
+ case PersistentPrefStore::PREF_READ_ERROR_NONE:
+ case PersistentPrefStore::PREF_READ_ERROR_NO_FILE:
+ break;
+ default:
+ LOG(ERROR) << "Error read RLZ store: " << rlz_store_->GetReadError();
+ }
+ // Restore refcount modified by SetIOTaskRunner().
+ io_task_runner_->Release();
+ io_task_runner_ = NULL;
}
-bool RlzValueStoreRegistry::ClearAllStatefulEvents(Product product) {
- return ClearAllProductEventValues(product, kStatefulEventsSubkeyName);
+base::ListValue* RlzValueStoreChromeOS::GetList(std::string list_name) {
+ base::Value* list_value = NULL;
+ rlz_store_->GetMutableValue(list_name, &list_value);
+ base::ListValue* list = NULL;
+ if (!list_value || !list_value->GetAsList(&list))
+ return NULL;
+ return list;
}
-void RlzValueStoreRegistry::CollectGarbage() {
- // Delete each of the known subkeys if empty.
- const char* subkeys[] = {
- kRlzsSubkeyName,
- kEventsSubkeyName,
- kStatefulEventsSubkeyName,
- kPingTimesSubkeyName
- };
-
- for (int i = 0; i < arraysize(subkeys); i++) {
- std::string subkey_name;
- base::StringAppendF(&subkey_name, "%s\\%s", kLibKeyName, subkeys[i]);
- AppendBrandToString(&subkey_name);
-
- VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER,
- ASCIIToWide(subkey_name).c_str()));
+bool RlzValueStoreChromeOS::AddValueToList(std::string list_name,
+ base::Value* value) {
+ base::ListValue* list = GetList(list_name);
+ if (!list) {
+ list = new base::ListValue;
+ rlz_store_->SetValue(list_name, list);
}
+ if (list->AppendIfNotPresent(value))
+ rlz_store_->ReportValueChanged(list_name);
+ return true;
+}
- // Delete the library key and its parents too now if empty.
- VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, GetWideLibKeyName().c_str()));
- VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleCommonKeyName));
- VERIFY(DeleteKeyIfEmpty(HKEY_CURRENT_USER, kGoogleKeyName));
+bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name,
+ const base::Value& value) {
+ base::ListValue* list = GetList(list_name);
+ if (!list)
+ return false;
+ rlz_store_->MarkNeedsEmptyValue(list_name);
+ size_t index;
+ if (list->Remove(value, &index))
+ rlz_store_->ReportValueChanged(list_name);
+ return true;
}
-ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
- if (!lock_.failed())
- store_.reset(new RlzValueStoreRegistry);
+
+ScopedRlzValueStoreLock::ScopedRlzValueStoreLock()
+ : store_(RlzValueStoreChromeOS::GetInstance()) {
+ DCHECK(store_->CalledOnValidThread());
}
ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
}
RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
- return store_.get();
+ return store_;
}
+namespace testing {
+
+void SetRlzStoreDirectory(const FilePath& directory) {
+ g_testing_rlz_store_path_ = directory;
+}
+
+} // namespace testing
+
} // namespace rlz_lib
« no previous file with comments | « rlz/chromeos/lib/rlz_value_store_chromeos.h ('k') | rlz/lib/financial_ping.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698