Implement missing stuff in System.cpp

Bug: 171711491
Change-Id: Iabbc3aa497f5a64261d5854ad46df0a56487e34e
diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt
index cb68c53..7e9c223 100644
--- a/base/CMakeLists.txt
+++ b/base/CMakeLists.txt
@@ -15,11 +15,13 @@
     PathUtils.cpp
     ring_buffer.c
     SharedLibrary.cpp
+    StringFormat.cpp
     Stream.cpp
     StreamSerializing.cpp
     SubAllocator.cpp
     System.cpp
     Tracing.cpp)
+
 set(gfxstream-base-posix-sources
     SharedMemory_posix.cpp
     Thread_pthread.cpp)
@@ -27,17 +29,22 @@
     SharedMemory_win32.cpp
     Thread_win32.cpp
     Win32UnicodeString.cpp)
-if (WIN32)
-add_library(
-    gfxstream-base
-    ${gfxstream-base-common-sources}
-    ${gfxstream-base-windows-sources})
+if (APPLE)
+    set(gfxstream-platform-sources
+        ${gfxstream-base-posix-sources}
+        system-native-mac.mm)
+elseif (WIN32)
+    set(gfxstream-platform-sources
+        ${gfxstream-base-windows-sources})
 else()
+    set(gfxstream-platform-sources
+        ${gfxstream-base-posix-sources})
+endif()
+
 add_library(
     gfxstream-base
     ${gfxstream-base-common-sources}
-    ${gfxstream-base-posix-sources})
-endif()
+    ${gfxstream-platform-sources})
 
 target_link_libraries(
     gfxstream-base PRIVATE lz4)
diff --git a/base/StringFormat.cpp b/base/StringFormat.cpp
new file mode 100644
index 0000000..17f7dbe
--- /dev/null
+++ b/base/StringFormat.cpp
@@ -0,0 +1,81 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#include "base/StringFormat.h"
+
+#include <stdio.h>
+
+namespace android {
+namespace base {
+
+std::string StringFormatRaw(const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    auto result = StringFormatWithArgs(format, args);
+    va_end(args);
+    return result;
+}
+
+std::string StringFormatWithArgs(const char* format, va_list args) {
+    std::string result;
+    StringAppendFormatWithArgs(&result, format, args);
+    return result;
+}
+
+void StringAppendFormatRaw(std::string* string, const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    StringAppendFormatWithArgs(string, format, args);
+    va_end(args);
+}
+
+void StringAppendFormatWithArgs(std::string* string,
+                                const char* format,
+                                va_list args) {
+    size_t cur_size = string->size();
+    size_t extra = 0;
+    for (;;) {
+        va_list args2;
+        va_copy(args2, args);
+        int ret = vsnprintf(&(*string)[cur_size], extra, format, args2);
+        va_end(args2);
+
+        if (ret == 0) {
+            // Nothing to do here.
+            break;
+        }
+
+        if (ret > 0) {
+            size_t ret_sz = static_cast<size_t>(ret);
+            if (extra == 0) {
+                // First pass, resize the string and try again.
+                extra = ret_sz + 1;
+                string->resize(cur_size + extra);
+                continue;
+            }
+            if (ret_sz < extra) {
+                // Second pass or later, success!
+                string->resize(cur_size + ret_sz);
+                return;
+            }
+        }
+
+        // NOTE: The MSVCRT.DLL implementation of snprintf() is broken and
+        // will return -1 in case of truncation. This code path is taken
+        // when this happens, or when |ret_sz| is equal or larger than
+        // |extra|. Grow the buffer to allow for more room, then try again.
+        extra += (extra >> 1) + 32;
+        string->resize(cur_size + extra);
+    }
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/StringFormat.h b/base/StringFormat.h
new file mode 100644
index 0000000..893f139
--- /dev/null
+++ b/base/StringFormat.h
@@ -0,0 +1,78 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#pragma once
+
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include <stdarg.h>
+
+namespace android {
+namespace base {
+
+// Create a new string instance that contains the printf-style formatted
+// output from |format| and potentially any following arguments.
+std::string StringFormatRaw(const char* format, ...);
+
+// A variant of StringFormat() which uses a va_list to list formatting
+// parameters instead.
+std::string StringFormatWithArgs(const char* format, va_list args);
+
+// Appends a formatted string at the end of an existing string.
+// |string| is the target string instance, |format| the format string,
+// followed by any formatting parameters. This is more efficient than
+// appending the result of StringFormat(format,...) to |*string| directly.
+void StringAppendFormatRaw(std::string* string, const char* format, ...);
+
+// A variant of StringAppendFormat() that takes a va_list to list
+// formatting parameters.
+void StringAppendFormatWithArgs(std::string* string,
+                                const char* format,
+                                va_list args);
+
+// unpackFormatArg() is a set of overloaded functions needed to unpack
+// an argument of the formatting list to a POD value which can be passed
+// into the sprintf()-like C function
+
+// Anything which can be used to construct a string goes here and unpacks into
+// a const char*
+inline const char* unpackFormatArg(const std::string& str) {
+    return str.c_str();
+}
+
+// Forward all PODs as-is
+template <class T>
+constexpr T&& unpackFormatArg(T&& t,
+        typename std::enable_if<
+                    std::is_pod<typename std::decay<T>::type>::value
+                 >::type* = nullptr) {
+    return std::forward<T>(t);
+}
+
+// These templated versions of StringFormat*() allow one to pass all kinds of
+// string objects into the argument list
+template <class... Args>
+std::string StringFormat(const char* format, Args&&... args) {
+    return StringFormatRaw(format, unpackFormatArg(std::forward<Args>(args))...);
+}
+
+template <class... Args>
+void StringAppendFormat(std::string* string,
+                        const char* format,
+                        Args&&... args) {
+    StringAppendFormatRaw(string, format,
+                          unpackFormatArg(std::forward<Args>(args))...);
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/StringFormat_unittest.cpp b/base/StringFormat_unittest.cpp
new file mode 100644
index 0000000..bc19704
--- /dev/null
+++ b/base/StringFormat_unittest.cpp
@@ -0,0 +1,78 @@
+// Copyright 2014 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#include "android/base/StringFormat.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+TEST(StringFormat, EmptyString) {
+    std::string s = StringFormat("");
+    EXPECT_TRUE(s.empty());
+    EXPECT_STREQ("", s.c_str());
+}
+
+TEST(StringFormat, SimpleString) {
+    std::string s = StringFormat("foobar");
+    EXPECT_STREQ("foobar", s.c_str());
+}
+
+TEST(StringFormat, SimpleDecimal) {
+    std::string s = StringFormat("Pi is about %d.%d\n", 3, 1415);
+    EXPECT_STREQ("Pi is about 3.1415\n", s.c_str());
+}
+
+TEST(StringFormat, VeryLongString) {
+    static const char kPiece[] = "A hospital bed is a parked taxi with the meter running - Groucho Marx. ";
+    const size_t kPieceLen = sizeof(kPiece) - 1U;
+    std::string s = StringFormat("%s%s%s%s%s%s%s", kPiece, kPiece, kPiece,
+                                 kPiece, kPiece, kPiece, kPiece);
+    EXPECT_EQ(7U * kPieceLen, s.size());
+    for (size_t n = 0; n < 7U; ++n) {
+        std::string s2 = std::string(s.c_str() + n * kPieceLen, kPieceLen);
+        EXPECT_STREQ(kPiece, s2.c_str()) << "Index #" << n;
+    }
+}
+
+TEST(StringAppendFormat, EmptyString) {
+    std::string s = "foo";
+    StringAppendFormat(&s, "");
+    EXPECT_EQ(3U, s.size());
+    EXPECT_STREQ("foo", s.c_str());
+}
+
+TEST(StringAppendFormat, SimpleString) {
+    std::string s = "foo";
+    StringAppendFormat(&s, "bar");
+    EXPECT_EQ(6U, s.size());
+    EXPECT_STREQ("foobar", s.c_str());
+}
+
+TEST(StringAppendFormat, VeryLongString) {
+    static const char kPiece[] = "A hospital bed is a parked taxi with the meter running - Groucho Marx. ";
+    const size_t kPieceLen = sizeof(kPiece) - 1U;
+    const size_t kCount = 12;
+    std::string s;
+    for (size_t n = 0; n < kCount; ++n) {
+        StringAppendFormat(&s, "%s", kPiece);
+    }
+
+    EXPECT_EQ(kCount * kPieceLen, s.size());
+    for (size_t n = 0; n < kCount; ++n) {
+        std::string s2 = std::string(s.c_str() + n * kPieceLen, kPieceLen);
+        EXPECT_STREQ(kPiece, s2.c_str()) << "Index #" << n;
+    }
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/base/System.cpp b/base/System.cpp
index 6306778..88fd21f 100644
--- a/base/System.cpp
+++ b/base/System.cpp
@@ -1,5 +1,82 @@
+#include "base/StringFormat.h"
 #include "base/System.h"
 
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#ifdef _MSC_VER
+#include "msvc-posix.h"
+#include "dirent.h"
+#else
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+namespace {
+
+struct TickCountImpl {
+private:
+    uint64_t mStartTimeUs;
+#ifdef _WIN32
+    long long mFreqPerSec = 0;    // 0 means 'high perf counter isn't available'
+#elif defined(__APPLE__)
+    clock_serv_t mClockServ;
+#endif
+
+public:
+    TickCountImpl() {
+#ifdef _WIN32
+        LARGE_INTEGER freq;
+        if (::QueryPerformanceFrequency(&freq)) {
+            mFreqPerSec = freq.QuadPart;
+        }
+#elif defined(__APPLE__)
+        host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &mClockServ);
+#endif
+        mStartTimeUs = getUs();
+    }
+
+#ifdef __APPLE__
+    ~TickCountImpl() {
+        mach_port_deallocate(mach_task_self(), mClockServ);
+    }
+#endif
+
+    uint64_t getStartTimeUs() const {
+        return mStartTimeUs;
+    }
+
+    uint64_t getUs() const {
+#ifdef _WIN32
+    if (!mFreqPerSec) {
+        return ::GetTickCount() * 1000;
+    }
+    LARGE_INTEGER now;
+    ::QueryPerformanceCounter(&now);
+    return (now.QuadPart * 1000000ull) / mFreqPerSec;
+#elif defined __linux__
+    timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return ts.tv_sec * 1000000ll + ts.tv_nsec / 1000;
+#else // APPLE
+    mach_timespec_t mts;
+    clock_get_time(mClockServ, &mts);
+    return mts.tv_sec * 1000000ll + mts.tv_nsec / 1000;
+#endif
+    }
+};
+
+// This is, maybe, the only static variable that may not be a LazyInstance:
+// it holds the actual timestamp at startup, and has to be initialized as
+// soon as possible after the application launch.
+static const TickCountImpl kTickCount;
+
+}  // namespace
+
 namespace android {
 namespace base {
 
@@ -7,12 +84,162 @@
     return {};
 }
 
-void setEnvironmentVariable(const std::string& key) {
+void setEnvironmentVariable(const std::string& key, const std::string& value) {
+#ifdef _WIN32
+    std::string envStr =
+        StringFormat("%s=%s", key, value);
+    // Note: this leaks the result of release().
+    _wputenv(Win32UnicodeString(envStr).release());
+#else
+    if (value.empty()) {
+        unsetenv(key.c_str());
+    } else {
+        setenv(key.c_str(), value.c_str(), 1);
+    }
+#endif
 }
 
 bool isVerboseLogging() {
     return false;
 }
 
+void sleepMs(uint64_t n) {
+#ifdef _WIN32
+    ::Sleep(n);
+#else
+    usleep(n * 1000);
+#endif
+}
+
+void sleepUs(uint64_t n) {
+#ifdef _WIN32
+    ::Sleep(n / 1000);
+#else
+    usleep(n);
+#endif
+}
+
+uint64_t getUnixTimeUs() {
+    timeval tv;
+    gettimeofday(&tv, nullptr);
+    return tv.tv_sec * 1000000LL + tv.tv_usec;
+}
+
+uint64_t getHighResTimeUs() {
+    return kTickCount.getUs();
+}
+
+uint64_t getUptimeMs() {
+    return (kTickCount.getUs() - kTickCount.getStartTimeUs()) / 1000;
+}
+
+std::string getProgramDirectoryFromPlatform() {
+    std::string res;
+#if defined(__linux__)
+    char path[1024];
+    memset(path, 0, sizeof(path));  // happy valgrind!
+    int len = readlink("/proc/self/exe", path, sizeof(path));
+    if (len > 0 && len < (int)sizeof(path)) {
+        char* x = ::strrchr(path, '/');
+        if (x) {
+            *x = '\0';
+            res.assign(path);
+        }
+    }
+#elif defined(__APPLE__)
+    char s[PATH_MAX];
+    auto pid = getpid();
+    proc_pidpath(pid, s, sizeof(s));
+    char* x = ::strrchr(s, '/');
+    if (x) {
+        // skip all slashes - there might be more than one
+        while (x > s && x[-1] == '/') {
+            --x;
+        }
+        *x = '\0';
+        res.assign(s);
+    } else {
+        res.assign("<unknown-application-dir>");
+    }
+#elif defined(_WIN32)
+    Win32UnicodeString appDir(PATH_MAX);
+    int len = GetModuleFileNameW(0, appDir.data(), appDir.size());
+    res.assign("<unknown-application-dir>");
+    if (len > 0) {
+        if (len > (int)appDir.size()) {
+            appDir.resize(static_cast<size_t>(len));
+            GetModuleFileNameW(0, appDir.data(), appDir.size());
+        }
+        std::string dir = appDir.toString();
+        char* sep = ::strrchr(&dir[0], '\\');
+        if (sep) {
+            *sep = '\0';
+            res.assign(dir.c_str());
+        }
+    }
+#else
+#error "Unsupported platform!"
+#endif
+    return res;
+}
+std::string getProgramDirectory() {
+    static std::string progDir;
+    if (progDir.empty()) {
+        progDir.assign(getProgramDirectoryFromPlatform());
+    }
+    return progDir;
+}
+
+std::string getLauncherDirectory() {
+    return getProgramDirectory();
+}
+
+CpuTime cpuTime() {
+    CpuTime res;
+
+    res.wall_time_us = kTickCount.getUs();
+
+#ifdef __APPLE__
+    cpuUsageCurrentThread_macImpl(
+        &res.user_time_us,
+        &res.system_time_us);
+#else
+
+#ifdef __linux__
+    struct rusage usage;
+    getrusage(RUSAGE_THREAD, &usage);
+    res.user_time_us =
+        usage.ru_utime.tv_sec * 1000000ULL +
+        usage.ru_utime.tv_usec;
+    res.system_time_us =
+        usage.ru_stime.tv_sec * 1000000ULL +
+        usage.ru_stime.tv_usec;
+#else // Windows
+    FILETIME creation_time_struct;
+    FILETIME exit_time_struct;
+    FILETIME kernel_time_struct;
+    FILETIME user_time_struct;
+    GetThreadTimes(
+        GetCurrentThread(),
+        &creation_time_struct,
+        &exit_time_struct,
+        &kernel_time_struct,
+        &user_time_struct);
+    (void)creation_time_struct;
+    (void)exit_time_struct;
+    uint64_t user_time_100ns =
+        user_time_struct.dwLowDateTime |
+        ((uint64_t)user_time_struct.dwHighDateTime << 32);
+    uint64_t system_time_100ns =
+        kernel_time_struct.dwLowDateTime |
+        ((uint64_t)kernel_time_struct.dwHighDateTime << 32);
+    res.user_time_us = user_time_100ns / 10;
+    res.system_time_us = system_time_100ns / 10;
+#endif
+
+#endif
+    return res;
+}
+
 } // namespace base
 } // namespace android
diff --git a/base/system-native-mac.mm b/base/system-native-mac.mm
new file mode 100644
index 0000000..873b1ff
--- /dev/null
+++ b/base/system-native-mac.mm
@@ -0,0 +1,165 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// This software is licensed under the terms of the GNU General Public
+// License version 2, as published by the Free Software Foundation, and
+// may be copied, distributed, and modified under those terms.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+#include "android/base/Optional.h"
+#include "android/base/system/System.h"
+
+#include <Cocoa/Cocoa.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/IOKitLib.h>
+#include <Carbon/Carbon.h>
+
+#include <IOKit/kext/KextManager.h>
+#include <IOKit/storage/IOBlockStorageDevice.h>
+
+#import <Foundation/Foundation.h>
+#import <Foundation/NSProcessInfo.h>
+
+#include <mach/mach_init.h>
+#include <mach/thread_act.h>
+#include <mach/mach_port.h>
+
+#include <stdlib.h>
+#include <sys/stat.h>
+
+namespace android {
+namespace base {
+
+// From:
+// https://stackoverflow.com/questions/49677034/thread-sleeps-too-long-on-os-x-when-in-background
+void disableAppNap_macImpl(void) {
+   if ([[NSProcessInfo processInfo]
+        respondsToSelector:@selector(beginActivityWithOptions:reason:)]) {
+      [[NSProcessInfo processInfo]
+       beginActivityWithOptions:0x00FFFFFF
+       reason:@"Not sleepy and don't want to nap"];
+   }
+}
+
+// based on https://stackoverflow.com/questions/13893134/get-current-pthread-cpu-usage-mac-os-x
+void cpuUsageCurrentThread_macImpl(
+    uint64_t* user,
+    uint64_t* sys) {
+
+    kern_return_t kr;
+    mach_msg_type_number_t count;
+    mach_port_t thread;
+    thread_basic_info_data_t info;
+
+    thread = mach_thread_self();
+
+    count = THREAD_BASIC_INFO_COUNT;
+    kr = thread_info(
+        thread,
+        THREAD_BASIC_INFO,
+        (thread_info_t)&info,
+        &count);
+
+    if (kr == KERN_SUCCESS) {
+        *user =
+            info.user_time.seconds * 1000000ULL +
+            info.user_time.microseconds;
+        *sys =
+            info.system_time.seconds * 1000000ULL +
+            info.system_time.microseconds;
+    } else {
+        fprintf(stderr, "%s: WARNING: could not get thread info for CPU usage\n", __func__);
+    }
+
+    mach_port_deallocate(mach_task_self(), thread);
+}
+
+Optional<System::DiskKind> nativeDiskKind(int st_dev) {
+    const char* devName = devname(st_dev, S_IFBLK);
+    if (!devName) {
+        return {};
+    }
+    CFMutableDictionaryRef classesToMatch =
+            IOBSDNameMatching(kIOMasterPortDefault, 0, devName);
+    if (!classesToMatch) {
+        NSLog(@"Could not find io classes of disk");
+        return {};
+    }
+
+    // get iterator of matching services
+    io_iterator_t entryIterator;
+
+    if (KERN_SUCCESS != IOServiceGetMatchingServices(kIOMasterPortDefault,
+                                                     classesToMatch,
+                                                     &entryIterator)) {
+        NSLog(@"Can't iterate services");
+        return {};
+    }
+
+    // iterate over all found medias
+    io_object_t serviceEntry, parentMedia;
+    while ((serviceEntry = IOIteratorNext(entryIterator)) != 0) {
+        // We assume there won't be more levels of nesting here. The limit is
+        // arbitrary and can be increased if we hit it.
+        int maxlevels = 8;
+        do {
+            kern_return_t kernResult = IORegistryEntryGetParentEntry(
+                    serviceEntry, kIOServicePlane, &parentMedia);
+            IOObjectRelease(serviceEntry);
+
+            if (KERN_SUCCESS != kernResult) {
+                serviceEntry = 0;
+                NSLog(@"Error while getting parent service entry");
+                break;
+            }
+
+            serviceEntry = parentMedia;
+            if (!parentMedia) {
+                break;  // finished iterator
+            }
+
+            CFTypeRef res = IORegistryEntryCreateCFProperty(
+                    serviceEntry, CFSTR(kIOPropertyDeviceCharacteristicsKey),
+                    kCFAllocatorDefault, 0);
+            if (res) {
+                NSString* type = [(NSDictionary*)res
+                        objectForKey:(id)CFSTR(kIOPropertyMediumTypeKey)];
+                if ([@kIOPropertyMediumTypeSolidStateKey
+                            isEqualToString:type]) {
+                    CFRelease(res);
+                    return System::DiskKind::Ssd;
+                } else if ([@kIOPropertyMediumTypeRotationalKey
+                                   isEqualToString:type]) {
+                    CFRelease(res);
+                    return System::DiskKind::Hdd;
+                }
+                CFRelease(res);
+            }
+        } while (maxlevels--);
+
+        if (serviceEntry) {
+            IOObjectRelease(serviceEntry);
+        }
+    }
+    IOObjectRelease(entryIterator);
+
+    return {};
+}
+
+// From:
+// https://stackoverflow.com/questions/6796028/start-a-gui-process-in-mac-os-x-without-dock-icon
+void hideDockIcon_macImpl(void) {
+    if (NSApp == nil) {
+        // Initialize the global variable "NSApp"
+        [NSApplication sharedApplication];
+    }
+    [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
+
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/stream-servers/CMakeLists.txt b/stream-servers/CMakeLists.txt
index 394aaba..938d0f4 100644
--- a/stream-servers/CMakeLists.txt
+++ b/stream-servers/CMakeLists.txt
@@ -44,7 +44,8 @@
     RendererImpl.cpp
     FrameBuffer.cpp
     GfxStreamAgents.cpp
-    GfxStreamBackend.cpp)
+    GfxStreamBackend.cpp
+    virtio-gpu-gfxstream-renderer.cpp)
 if (APPLE)
     set(gfxstream_backend-platform-sources NativeSubWindow_cocoa.m)
 elseif (WIN32)
@@ -90,6 +91,7 @@
     PRIVATE
     OSWindow
     gfxstream_backend
+    gfxstream-base
     gtest_main)