Merge from Chromium at DEPS revision r167172

This commit was generated by merge_to_master.py.

Change-Id: Ib8d56fd5ae39a2d7e8c91dcd76cc6d13f25f2aab
diff --git a/rlz/mac/lib/machine_id_mac.cc b/rlz/mac/lib/machine_id_mac.cc
new file mode 100644
index 0000000..fb9a34c
--- /dev/null
+++ b/rlz/mac/lib/machine_id_mac.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/network/IOEthernetInterface.h>
+#include <IOKit/network/IONetworkInterface.h>
+#include <IOKit/network/IOEthernetController.h>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_ioobject.h"
+#include "base/string16.h"
+#include "base/stringprintf.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+
+namespace rlz_lib {
+
+namespace {
+
+// See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html
+
+// The caller is responsible for freeing |matching_services|.
+bool FindEthernetInterfaces(io_iterator_t* matching_services) {
+  base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
+      IOServiceMatching(kIOEthernetInterfaceClass));
+  if (!matching_dict)
+    return false;
+
+  base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> primary_interface(
+      CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                &kCFTypeDictionaryKeyCallBacks,
+                                &kCFTypeDictionaryValueCallBacks));
+  if (!primary_interface)
+    return false;
+
+  CFDictionarySetValue(
+      primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
+  CFDictionarySetValue(
+      matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface);
+
+  kern_return_t kern_result = IOServiceGetMatchingServices(
+      kIOMasterPortDefault, matching_dict.release(), matching_services);
+
+  return kern_result == KERN_SUCCESS;
+}
+
+bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator,
+                               uint8_t* buffer, size_t buffer_size) {
+  if (buffer_size < kIOEthernetAddressSize)
+    return false;
+
+  bool success = false;
+
+  bzero(buffer, buffer_size);
+  base::mac::ScopedIOObject<io_object_t> primary_interface;
+  while (primary_interface.reset(IOIteratorNext(primary_interface_iterator)),
+         primary_interface) {
+    io_object_t primary_interface_parent;
+    kern_return_t kern_result = IORegistryEntryGetParentEntry(
+        primary_interface, kIOServicePlane, &primary_interface_parent);
+    base::mac::ScopedIOObject<io_object_t> primary_interface_parent_deleter(
+        primary_interface_parent);
+    success = kern_result == KERN_SUCCESS;
+
+    if (!success)
+      continue;
+
+    base::mac::ScopedCFTypeRef<CFTypeRef> mac_data(
+        IORegistryEntryCreateCFProperty(primary_interface_parent,
+                                        CFSTR(kIOMACAddress),
+                                        kCFAllocatorDefault,
+                                        0));
+    CFDataRef mac_data_data = base::mac::CFCast<CFDataRef>(mac_data);
+    if (mac_data_data) {
+      CFDataGetBytes(
+          mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer);
+    }
+  }
+
+  return success;
+}
+
+bool GetMacAddress(unsigned char* buffer, size_t size) {
+  io_iterator_t primary_interface_iterator;
+  if (!FindEthernetInterfaces(&primary_interface_iterator))
+    return false;
+  bool result = GetMACAddressFromIterator(
+      primary_interface_iterator, buffer, size);
+  IOObjectRelease(primary_interface_iterator);
+  return result;
+}
+
+CFStringRef CopySerialNumber() {
+  base::mac::ScopedIOObject<io_service_t> expert_device(
+      IOServiceGetMatchingService(kIOMasterPortDefault,
+          IOServiceMatching("IOPlatformExpertDevice")));
+  if (!expert_device)
+    return NULL;
+
+  base::mac::ScopedCFTypeRef<CFTypeRef> serial_number(
+      IORegistryEntryCreateCFProperty(expert_device,
+                                      CFSTR(kIOPlatformSerialNumberKey),
+                                      kCFAllocatorDefault,
+                                      0));
+  CFStringRef serial_number_cfstring =
+      base::mac::CFCast<CFStringRef>(serial_number);
+  if (!serial_number_cfstring)
+    return NULL;
+
+  ignore_result(serial_number.release());
+  return serial_number_cfstring;
+}
+
+}  // namespace
+
+bool GetRawMachineId(string16* data, int* more_data) {
+  uint8_t mac_address[kIOEthernetAddressSize];
+
+  data->clear();
+  if (GetMacAddress(mac_address, sizeof(mac_address))) {
+    *data += ASCIIToUTF16(base::StringPrintf("mac:%02x%02x%02x%02x%02x%02x",
+        mac_address[0], mac_address[1], mac_address[2],
+        mac_address[3], mac_address[4], mac_address[5]));
+  }
+
+  // A MAC address is enough to uniquely identify a machine, but it's only 6
+  // bytes, 3 of which are manufacturer-determined. To make brute-forcing the
+  // SHA1 of this harder, also append the system's serial number.
+  CFStringRef serial = CopySerialNumber();
+  if (serial) {
+    if (!data->empty())
+      *data += UTF8ToUTF16(" ");
+    *data += UTF8ToUTF16("serial:") + base::SysCFStringRefToUTF16(serial);
+    CFRelease(serial);
+  }
+
+  // On windows, this is set to the volume id. Since it's not scrambled before
+  // being sent, just set it to 1.
+  *more_data = 1;
+  return true;
+}
+
+}  // namespace rlz_lib
diff --git a/rlz/mac/lib/rlz_value_store_mac.h b/rlz/mac/lib/rlz_value_store_mac.h
new file mode 100644
index 0000000..b7ffb4e
--- /dev/null
+++ b/rlz/mac/lib/rlz_value_store_mac.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_
+#define RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_
+
+#include "rlz/lib/rlz_value_store.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_nsobject.h"
+
+@class NSDictionary;
+@class NSMutableDictionary;
+
+namespace rlz_lib {
+
+// An implementation of RlzValueStore for mac. It stores information in a
+// plist file in the user's Application Support folder.
+class RlzValueStoreMac : public RlzValueStore {
+ public:
+  virtual bool HasAccess(AccessType type) OVERRIDE;
+
+  virtual bool WritePingTime(Product product, int64 time) OVERRIDE;
+  virtual bool ReadPingTime(Product product, int64* time) OVERRIDE;
+  virtual bool ClearPingTime(Product product) OVERRIDE;
+
+  virtual bool WriteAccessPointRlz(AccessPoint access_point,
+                                   const char* new_rlz) OVERRIDE;
+  virtual bool ReadAccessPointRlz(AccessPoint access_point,
+                                  char* rlz,
+                                  size_t rlz_size) OVERRIDE;
+  virtual bool ClearAccessPointRlz(AccessPoint access_point) OVERRIDE;
+
+  virtual bool AddProductEvent(Product product, const char* event_rlz) OVERRIDE;
+  virtual bool ReadProductEvents(Product product,
+                                 std::vector<std::string>* events) OVERRIDE;
+  virtual bool ClearProductEvent(Product product,
+                                 const char* event_rlz) OVERRIDE;
+  virtual bool ClearAllProductEvents(Product product) OVERRIDE;
+
+  virtual bool AddStatefulEvent(Product product,
+                                const char* event_rlz) OVERRIDE;
+  virtual bool IsStatefulEvent(Product product,
+                               const char* event_rlz) OVERRIDE;
+  virtual bool ClearAllStatefulEvents(Product product) OVERRIDE;
+
+  virtual void CollectGarbage() OVERRIDE;
+
+ private:
+  // |dict| is the dictionary that backs all data. plist_path is the name of the
+  // plist file, used solely for implementing HasAccess().
+  RlzValueStoreMac(NSMutableDictionary* dict, NSString* plist_path);
+  virtual ~RlzValueStoreMac();
+  friend class ScopedRlzValueStoreLock;
+
+  // Returns the backing dictionary that should be written to disk.
+  NSDictionary* dictionary();
+
+  // Returns the dictionary to which all data should be written. Usually, this
+  // is just |dictionary()|, but if supplementary branding is used, it's a
+  // subdirectory at key "brand_<supplementary branding code>".
+  // Note that windows stores data at
+  //    rlz/name (e.g. "pingtime")/supplementalbranding/productcode
+  // Mac on the other hand does
+  //    supplementalbranding/productcode/pingtime.
+  NSMutableDictionary* WorkingDict();
+
+  // Returns the subdirectory of |WorkingDict()| used to store data for
+  // product p.
+  NSMutableDictionary* ProductDict(Product p);
+
+  scoped_nsobject<NSMutableDictionary> dict_;
+  scoped_nsobject<NSString> plist_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(RlzValueStoreMac);
+};
+
+}  // namespace rlz_lib
+
+#endif  // RLZ_MAC_LIB_RLZ_VALUE_STORE_MAC_H_
diff --git a/rlz/mac/lib/rlz_value_store_mac.mm b/rlz/mac/lib/rlz_value_store_mac.mm
new file mode 100644
index 0000000..795a0cc
--- /dev/null
+++ b/rlz/mac/lib/rlz_value_store_mac.mm
@@ -0,0 +1,462 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "rlz/mac/lib/rlz_value_store_mac.h"
+
+#include "base/eintr_wrapper.h"
+#include "base/mac/foundation_util.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/sys_string_conversions.h"
+#include "rlz/lib/assert.h"
+#include "rlz/lib/lib_values.h"
+#include "rlz/lib/rlz_lib.h"
+
+#import <Foundation/Foundation.h>
+#include <pthread.h>
+
+using base::mac::ObjCCast;
+
+namespace rlz_lib {
+
+// These are written to disk and should not be changed.
+NSString* const kPingTimeKey = @"pingTime";
+NSString* const kAccessPointKey = @"accessPoints";
+NSString* const kProductEventKey = @"productEvents";
+NSString* const kStatefulEventKey = @"statefulEvents";
+
+namespace {
+
+NSString* GetNSProductName(Product product) {
+  return base::SysUTF8ToNSString(GetProductName(product));
+}
+
+NSString* GetNSAccessPointName(AccessPoint p) {
+  return base::SysUTF8ToNSString(GetAccessPointName(p));
+}
+
+// Retrieves a subdictionary in |p| for key |k|, creating it if necessary.
+// If the dictionary contains an object for |k| that is not a mutable
+// dictionary, that object is replaced with an empty mutable dictinary.
+NSMutableDictionary* GetOrCreateDict(
+    NSMutableDictionary* p, NSString* k) {
+  NSMutableDictionary* d = ObjCCast<NSMutableDictionary>([p objectForKey:k]);
+  if (!d) {
+    d = [NSMutableDictionary dictionaryWithCapacity:0];
+    [p setObject:d forKey:k];
+  }
+  return d;
+}
+
+}  // namespace
+
+RlzValueStoreMac::RlzValueStoreMac(NSMutableDictionary* dict,
+                                   NSString* plist_path)
+  : dict_([dict retain]), plist_path_([plist_path retain]) {
+}
+
+RlzValueStoreMac::~RlzValueStoreMac() {
+}
+
+bool RlzValueStoreMac::HasAccess(AccessType type) {
+  NSFileManager* manager = [NSFileManager defaultManager];
+  switch (type) {
+    case kReadAccess:  return [manager isReadableFileAtPath:plist_path_];
+    case kWriteAccess: return [manager isWritableFileAtPath:plist_path_];
+  }
+}
+
+bool RlzValueStoreMac::WritePingTime(Product product, int64 time) {
+  NSNumber* n = [NSNumber numberWithLongLong:time];
+  [ProductDict(product) setObject:n forKey:kPingTimeKey];
+  return true;
+}
+
+bool RlzValueStoreMac::ReadPingTime(Product product, int64* time) {
+  if (NSNumber* n =
+      ObjCCast<NSNumber>([ProductDict(product) objectForKey:kPingTimeKey])) {
+    *time = [n longLongValue];
+    return true;
+  }
+  return false;
+}
+
+bool RlzValueStoreMac::ClearPingTime(Product product) {
+  [ProductDict(product) removeObjectForKey:kPingTimeKey];
+  return true;
+}
+
+
+bool RlzValueStoreMac::WriteAccessPointRlz(AccessPoint access_point,
+                                           const char* new_rlz) {
+  NSMutableDictionary* d = GetOrCreateDict(WorkingDict(), kAccessPointKey);
+  [d setObject:base::SysUTF8ToNSString(new_rlz)
+      forKey:GetNSAccessPointName(access_point)];
+  return true;
+}
+
+bool RlzValueStoreMac::ReadAccessPointRlz(AccessPoint access_point,
+                                          char* rlz,
+                                          size_t rlz_size) {
+  // Reading a non-existent access point counts as success.
+  if (NSDictionary* d = ObjCCast<NSDictionary>(
+        [WorkingDict() objectForKey:kAccessPointKey])) {
+    NSString* val = ObjCCast<NSString>(
+        [d objectForKey:GetNSAccessPointName(access_point)]);
+    if (!val) {
+      if (rlz_size > 0)
+        rlz[0] = '\0';
+      return true;
+    }
+
+    std::string s = base::SysNSStringToUTF8(val);
+    if (s.size() >= rlz_size) {
+      rlz[0] = 0;
+      ASSERT_STRING("GetAccessPointRlz: Insufficient buffer size");
+      return false;
+    }
+    strncpy(rlz, s.c_str(), rlz_size);
+    return true;
+  }
+  if (rlz_size > 0)
+    rlz[0] = '\0';
+  return true;
+}
+
+bool RlzValueStoreMac::ClearAccessPointRlz(AccessPoint access_point) {
+  if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>(
+      [WorkingDict() objectForKey:kAccessPointKey])) {
+    [d removeObjectForKey:GetNSAccessPointName(access_point)];
+  }
+  return true;
+}
+
+
+bool RlzValueStoreMac::AddProductEvent(Product product,
+                                       const char* event_rlz) {
+  [GetOrCreateDict(ProductDict(product), kProductEventKey)
+      setObject:[NSNumber numberWithBool:YES]
+      forKey:base::SysUTF8ToNSString(event_rlz)];
+  return true;
+}
+
+bool RlzValueStoreMac::ReadProductEvents(Product product,
+                                         std::vector<std::string>* events) {
+  if (NSDictionary* d = ObjCCast<NSDictionary>(
+      [ProductDict(product) objectForKey:kProductEventKey])) {
+    for (NSString* s in d)
+      events->push_back(base::SysNSStringToUTF8(s));
+    return true;
+  }
+  return true;
+}
+
+bool RlzValueStoreMac::ClearProductEvent(Product product,
+                                         const char* event_rlz) {
+  if (NSMutableDictionary* d = ObjCCast<NSMutableDictionary>(
+      [ProductDict(product) objectForKey:kProductEventKey])) {
+    [d removeObjectForKey:base::SysUTF8ToNSString(event_rlz)];
+    return true;
+  }
+  return false;
+}
+
+bool RlzValueStoreMac::ClearAllProductEvents(Product product) {
+  [ProductDict(product) removeObjectForKey:kProductEventKey];
+  return true;
+}
+
+
+bool RlzValueStoreMac::AddStatefulEvent(Product product,
+                                        const char* event_rlz) {
+  [GetOrCreateDict(ProductDict(product), kStatefulEventKey)
+      setObject:[NSNumber numberWithBool:YES]
+      forKey:base::SysUTF8ToNSString(event_rlz)];
+  return true;
+}
+
+bool RlzValueStoreMac::IsStatefulEvent(Product product,
+                                       const char* event_rlz) {
+  if (NSDictionary* d = ObjCCast<NSDictionary>(
+        [ProductDict(product) objectForKey:kStatefulEventKey])) {
+    return [d objectForKey:base::SysUTF8ToNSString(event_rlz)] != nil;
+  }
+  return false;
+}
+
+bool RlzValueStoreMac::ClearAllStatefulEvents(Product product) {
+  [ProductDict(product) removeObjectForKey:kStatefulEventKey];
+  return true;
+}
+
+
+void RlzValueStoreMac::CollectGarbage() {
+  NOTIMPLEMENTED();
+}
+
+NSDictionary* RlzValueStoreMac::dictionary() {
+  return dict_.get();
+}
+
+NSMutableDictionary* RlzValueStoreMac::WorkingDict() {
+  std::string brand(SupplementaryBranding::GetBrand());
+  if (brand.empty())
+    return dict_;
+
+  NSString* brand_ns =
+      [@"brand_" stringByAppendingString:base::SysUTF8ToNSString(brand)];
+
+  return GetOrCreateDict(dict_.get(), brand_ns);
+}
+
+NSMutableDictionary* RlzValueStoreMac::ProductDict(Product p) {
+  return GetOrCreateDict(WorkingDict(), GetNSProductName(p));
+}
+
+
+namespace {
+
+// Creating a recursive cross-process mutex on windows is one line. On mac,
+// there's no primitve for that, so this lock is emulated by an in-process
+// mutex to get the recursive part, followed by a cross-process lock for the
+// cross-process part.
+
+// This is a struct so that it doesn't need a static initializer.
+struct RecursiveCrossProcessLock {
+  // Tries to acquire a recursive cross-process lock. Note that this _always_
+  // acquires the in-process lock (if it wasn't already acquired). The parent
+  // directory of |lock_file| must exist.
+  bool TryGetCrossProcessLock(NSString* lock_filename);
+
+  // Releases the lock. Should always be called, even if
+  // TryGetCrossProcessLock() returns false.
+  void ReleaseLock();
+
+  pthread_mutex_t recursive_lock_;
+  pthread_t locking_thread_;
+
+  int file_lock_;
+} g_recursive_lock = {
+  // PTHREAD_RECURSIVE_MUTEX_INITIALIZER doesn't exist before 10.7 and is buggy
+  // on 10.7 (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51906#c34), so emulate
+  // recursive locking with a normal non-recursive mutex.
+  PTHREAD_MUTEX_INITIALIZER,
+  0,
+  -1
+};
+
+bool RecursiveCrossProcessLock::TryGetCrossProcessLock(
+    NSString* lock_filename) {
+  bool just_got_lock = false;
+
+  // Emulate a recursive mutex with a non-recursive one.
+  if (pthread_mutex_trylock(&recursive_lock_) == EBUSY) {
+    if (pthread_equal(pthread_self(), locking_thread_) == 0) {
+      // Some other thread has the lock, wait for it.
+      pthread_mutex_lock(&recursive_lock_);
+      CHECK(locking_thread_ == 0);
+      just_got_lock = true;
+    }
+  } else {
+    just_got_lock = true;
+  }
+
+  locking_thread_ = pthread_self();
+
+  // Try to acquire file lock.
+  if (just_got_lock) {
+    const int kMaxTimeoutMS = 5000;  // Matches windows.
+    const int kSleepPerTryMS = 200;
+
+    CHECK(file_lock_ == -1);
+    file_lock_ = open([lock_filename fileSystemRepresentation],
+                      O_RDWR | O_CREAT,
+                      0666);
+    if (file_lock_ == -1)
+      return false;
+
+    int flock_result = -1;
+    int elapsed_ms = 0;
+    while ((flock_result =
+               HANDLE_EINTR(flock(file_lock_, LOCK_EX | LOCK_NB))) == -1 &&
+           errno == EWOULDBLOCK &&
+           elapsed_ms < kMaxTimeoutMS) {
+      usleep(kSleepPerTryMS * 1000);
+      elapsed_ms += kSleepPerTryMS;
+    }
+
+    if (flock_result == -1) {
+      ignore_result(HANDLE_EINTR(close(file_lock_)));
+      file_lock_ = -1;
+      return false;
+    }
+    return true;
+  } else {
+    return file_lock_ != -1;
+  }
+}
+
+void RecursiveCrossProcessLock::ReleaseLock() {
+  if (file_lock_ != -1) {
+    ignore_result(HANDLE_EINTR(flock(file_lock_, LOCK_UN)));
+    ignore_result(HANDLE_EINTR(close(file_lock_)));
+    file_lock_ = -1;
+  }
+
+  locking_thread_ = 0;
+  pthread_mutex_unlock(&recursive_lock_);
+}
+
+
+// This is set during test execution, to write RLZ files into a temporary
+// directory instead of the user's Application Support folder.
+NSString* g_test_folder;
+
+// RlzValueStoreMac keeps its data in memory and only writes it to disk when
+// ScopedRlzValueStoreLock goes out of scope. Hence, if several
+// ScopedRlzValueStoreLocks are nested, they all need to use the same store
+// object.
+
+// This counts the nesting depth.
+int g_lock_depth = 0;
+
+// This is the store object that might be shared. Only set if g_lock_depth > 0.
+RlzValueStoreMac* g_store_object = NULL;
+
+
+NSString* CreateRlzDirectory() {
+  NSFileManager* manager = [NSFileManager defaultManager];
+  NSArray* paths = NSSearchPathForDirectoriesInDomains(
+      NSApplicationSupportDirectory, NSUserDomainMask, /*expandTilde=*/YES);
+  NSString* folder = nil;
+  if ([paths count] > 0)
+    folder = ObjCCast<NSString>([paths objectAtIndex:0]);
+  if (!folder)
+    folder = [@"~/Library/Application Support" stringByStandardizingPath];
+  folder = [folder stringByAppendingPathComponent:@"Google/RLZ"];
+
+  if (g_test_folder)
+    folder = [g_test_folder stringByAppendingPathComponent:folder];
+
+  [manager createDirectoryAtPath:folder
+     withIntermediateDirectories:YES
+                      attributes:nil
+                           error:nil];
+  return folder;
+}
+
+// Returns the path of the rlz plist store, also creates the parent directory
+// path if it doesn't exist.
+NSString* RlzPlistFilename() {
+  NSString* const kRlzFile = @"RlzStore.plist";
+  return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzFile];
+}
+
+// Returns the path of the rlz lock file, also creates the parent directory
+// path if it doesn't exist.
+NSString* RlzLockFilename() {
+  NSString* const kRlzLockfile = @"flockfile";
+  return [CreateRlzDirectory() stringByAppendingPathComponent:kRlzLockfile];
+}
+
+}  // namespace
+
+ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
+  bool got_distributed_lock =
+      g_recursive_lock.TryGetCrossProcessLock(RlzLockFilename());
+  // At this point, we hold the in-process lock, no matter the value of
+  // |got_distributed_lock|.
+
+  ++g_lock_depth;
+
+  if (!got_distributed_lock) {
+    // Give up. |store_| isn't set, which signals to callers that acquiring
+    // the lock failed. |g_recursive_lock| will be released by the
+    // destructor.
+    CHECK(!g_store_object);
+    return;
+  }
+
+  if (g_lock_depth > 1) {
+    // Reuse the already existing store object. Note that it can be NULL when
+    // lock acquisition succeeded but the rlz data file couldn't be read.
+    store_.reset(g_store_object);
+    return;
+  }
+
+  CHECK(!g_store_object);
+
+  NSString* plist = RlzPlistFilename();
+
+  // Create an empty file if none exists yet.
+  NSFileManager* manager = [NSFileManager defaultManager];
+  if (![manager fileExistsAtPath:plist isDirectory:NULL])
+    [[NSDictionary dictionary] writeToFile:plist atomically:YES];
+
+  NSMutableDictionary* dict =
+      [NSMutableDictionary dictionaryWithContentsOfFile:plist];
+  VERIFY(dict);
+
+  if (dict) {
+    store_.reset(new RlzValueStoreMac(dict, plist));
+    g_store_object = (RlzValueStoreMac*)store_.get();
+  }
+}
+
+ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
+  --g_lock_depth;
+  CHECK(g_lock_depth >= 0);
+
+  if (g_lock_depth > 0) {
+    // Other locks are still using store_, don't free it yet.
+    ignore_result(store_.release());
+    return;
+  }
+
+  if (store_.get()) {
+    g_store_object = NULL;
+
+    NSDictionary* dict =
+        static_cast<RlzValueStoreMac*>(store_.get())->dictionary();
+    VERIFY([dict writeToFile:RlzPlistFilename() atomically:YES]);
+  }
+
+  // Check that "store_ set" => "file_lock acquired". The converse isn't true,
+  // for example if the rlz data file can't be read.
+  if (store_.get())
+    CHECK(g_recursive_lock.file_lock_ != -1);
+  if (g_recursive_lock.file_lock_ == -1)
+    CHECK(!store_.get());
+
+  g_recursive_lock.ReleaseLock();
+}
+
+RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
+  return store_.get();
+}
+
+namespace testing {
+
+void SetRlzStoreDirectory(const FilePath& directory) {
+  base::mac::ScopedNSAutoreleasePool pool;
+
+  [g_test_folder release];
+  if (directory.empty()) {
+    g_test_folder = nil;
+  } else {
+    // Not Unsafe on OS X.
+    g_test_folder =
+      [[NSString alloc] initWithUTF8String:directory.AsUTF8Unsafe().c_str()];
+  }
+}
+
+std::string RlzPlistFilenameStr() {
+  @autoreleasepool {
+    return std::string([RlzPlistFilename() fileSystemRepresentation]);
+  }
+}
+
+}  // namespace testing
+
+}  // namespace rlz_lib