Merge "VR: Rename HIDL service name from vr_hwcomposer to vr"
diff --git a/include/ui/ColorSpace.h b/include/ui/ColorSpace.h
index 8c4acb7..8ccf6d3 100644
--- a/include/ui/ColorSpace.h
+++ b/include/ui/ColorSpace.h
@@ -35,6 +35,16 @@
     typedef std::function<float(float)> transfer_function;
     typedef std::function<float(float)> clamping_function;
 
+    struct TransferParameters {
+        float g = 0.0f;
+        float a = 0.0f;
+        float b = 0.0f;
+        float c = 0.0f;
+        float d = 0.0f;
+        float e = 0.0f;
+        float f = 0.0f;
+    };
+
     /**
      * Creates a named color space with the specified RGB->XYZ
      * conversion matrix. The white point and primaries will be
@@ -47,8 +57,39 @@
     ColorSpace(
             const std::string& name,
             const mat3& rgbToXYZ,
-            transfer_function OETF = linearReponse,
-            transfer_function EOTF = linearReponse,
+            transfer_function OETF = linearResponse,
+            transfer_function EOTF = linearResponse,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified RGB->XYZ
+     * conversion matrix. The white point and primaries will be
+     * computed from the supplied matrix.
+     *
+     * The transfer functions are defined by the set of supplied
+     * transfer parameters. The default clamping function is a
+     * simple saturate (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const mat3& rgbToXYZ,
+            const TransferParameters parameters,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified RGB->XYZ
+     * conversion matrix. The white point and primaries will be
+     * computed from the supplied matrix.
+     *
+     * The transfer functions are defined by a simple gamma value.
+     * The default clamping function is a saturate (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const mat3& rgbToXYZ,
+            float gamma,
             clamping_function clamper = saturate<float>
     ) noexcept;
 
@@ -65,8 +106,41 @@
             const std::string& name,
             const std::array<float2, 3>& primaries,
             const float2& whitePoint,
-            transfer_function OETF = linearReponse,
-            transfer_function EOTF = linearReponse,
+            transfer_function OETF = linearResponse,
+            transfer_function EOTF = linearResponse,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified primaries
+     * and white point. The RGB<>XYZ conversion matrices are
+     * computed from the primaries and white point.
+     *
+     * The transfer functions are defined by the set of supplied
+     * transfer parameters. The default clamping function is a
+     * simple saturate (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const std::array<float2, 3>& primaries,
+            const float2& whitePoint,
+            const TransferParameters parameters,
+            clamping_function clamper = saturate<float>
+    ) noexcept;
+
+    /**
+     * Creates a named color space with the specified primaries
+     * and white point. The RGB<>XYZ conversion matrices are
+     * computed from the primaries and white point.
+     *
+     * The transfer functions are defined by a single gamma value.
+     * The default clamping function is a saturate (clamp(x, 0, 1)).
+     */
+    ColorSpace(
+            const std::string& name,
+            const std::array<float2, 3>& primaries,
+            const float2& whitePoint,
+            float gamma,
             clamping_function clamper = saturate<float>
     ) noexcept;
 
@@ -138,6 +212,10 @@
         return mWhitePoint;
     }
 
+    constexpr const TransferParameters& getTransferParameters() const noexcept {
+        return mParameters;
+    }
+
     /**
      * Converts the supplied XYZ value to xyY.
      */
@@ -166,35 +244,6 @@
     static const ColorSpace ACES();
     static const ColorSpace ACEScg();
 
-    class Connector {
-    public:
-        Connector(const ColorSpace& src, const ColorSpace& dst) noexcept;
-
-        constexpr const ColorSpace& getSource() const noexcept { return mSource; }
-        constexpr const ColorSpace& getDestination() const noexcept { return mDestination; }
-
-        constexpr const mat3& getTransform() const noexcept { return mTransform; }
-
-        constexpr float3 transform(const float3& v) const noexcept {
-            float3 linear = mSource.toLinear(apply(v, mSource.getClamper()));
-            return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper());
-        }
-
-        constexpr float3 transformLinear(const float3& v) const noexcept {
-            float3 linear = apply(v, mSource.getClamper());
-            return apply(mTransform * linear, mDestination.getClamper());
-        }
-
-    private:
-        const ColorSpace& mSource;
-        const ColorSpace& mDestination;
-        mat3 mTransform;
-    };
-
-    static const Connector connect(const ColorSpace& src, const ColorSpace& dst) {
-        return Connector(src, dst);
-    }
-
     // Creates a NxNxN 3D LUT, where N is the specified size (min=2, max=256)
     // The 3D lookup coordinates map to the RGB components: u=R, v=G, w=B
     // The generated 3D LUT is meant to be used as a 3D texture and its Y
@@ -208,7 +257,7 @@
     static constexpr mat3 computeXYZMatrix(
             const std::array<float2, 3>& primaries, const float2& whitePoint);
 
-    static constexpr float linearReponse(float v) {
+    static constexpr float linearResponse(float v) {
         return v;
     }
 
@@ -217,6 +266,7 @@
     mat3 mRGBtoXYZ;
     mat3 mXYZtoRGB;
 
+    TransferParameters mParameters;
     transfer_function mOETF;
     transfer_function mEOTF;
     clamping_function mClamper;
@@ -225,6 +275,31 @@
     float2 mWhitePoint;
 };
 
+class ColorSpaceConnector {
+public:
+    ColorSpaceConnector(const ColorSpace& src, const ColorSpace& dst) noexcept;
+
+    constexpr const ColorSpace& getSource() const noexcept { return mSource; }
+    constexpr const ColorSpace& getDestination() const noexcept { return mDestination; }
+
+    constexpr const mat3& getTransform() const noexcept { return mTransform; }
+
+    constexpr float3 transform(const float3& v) const noexcept {
+        float3 linear = mSource.toLinear(apply(v, mSource.getClamper()));
+        return apply(mDestination.fromLinear(mTransform * linear), mDestination.getClamper());
+    }
+
+    constexpr float3 transformLinear(const float3& v) const noexcept {
+        float3 linear = apply(v, mSource.getClamper());
+        return apply(mTransform * linear, mDestination.getClamper());
+    }
+
+private:
+    ColorSpace mSource;
+    ColorSpace mDestination;
+    mat3 mTransform;
+};
+
 }; // namespace android
 
 #endif // ANDROID_UI_COLOR_SPACE
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 0dc4469..327ecad 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -70,3 +70,13 @@
         "libbase",
     ],
 }
+
+cc_test {
+    name: "schd-dbg",
+    srcs: ["schd-dbg.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libutils",
+        "libbase",
+    ],
+}
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp
new file mode 100644
index 0000000..2732071
--- /dev/null
+++ b/libs/binder/tests/schd-dbg.cpp
@@ -0,0 +1,426 @@
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
+#include <iomanip>
+#include <iostream>
+#include <tuple>
+#include <vector>
+
+#include <pthread.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+using namespace std;
+using namespace android;
+
+enum BinderWorkerServiceCode {
+  BINDER_NOP = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+#define ASSERT(cond)                                                \
+  do {                                                              \
+    if (!(cond)) {                                                  \
+      cerr << __func__ << ":" << __LINE__ << " condition:" << #cond \
+           << " failed\n"                                           \
+           << endl;                                                 \
+      exit(EXIT_FAILURE);                                           \
+    }                                                               \
+  } while (0)
+
+vector<sp<IBinder> > workers;
+
+// the ratio that the service is synced on the same cpu beyond
+// GOOD_SYNC_MIN is considered as good
+#define GOOD_SYNC_MIN (0.6)
+
+#define DUMP_PRICISION 3
+
+// the default value
+int no_process = 2;
+int iterations = 100;
+int payload_size = 16;
+int no_inherent = 0;
+int no_sync = 0;
+int verbose = 0;
+
+// the deadline latency that we are interested in
+uint64_t deadline_us = 2500;
+
+int thread_pri() {
+  struct sched_param param;
+  int policy;
+  ASSERT(!pthread_getschedparam(pthread_self(), &policy, &param));
+  return param.sched_priority;
+}
+
+void thread_dump(const char* prefix) {
+  struct sched_param param;
+  int policy;
+  if (!verbose) return;
+  cout << "--------------------------------------------------" << endl;
+  cout << setw(12) << left << prefix << " pid: " << getpid()
+       << " tid: " << gettid() << " cpu: " << sched_getcpu() << endl;
+  ASSERT(!pthread_getschedparam(pthread_self(), &policy, &param));
+  string s = (policy == SCHED_OTHER)
+                 ? "SCHED_OTHER"
+                 : (policy == SCHED_FIFO)
+                       ? "SCHED_FIFO"
+                       : (policy == SCHED_RR) ? "SCHED_RR" : "???";
+  cout << setw(12) << left << s << param.sched_priority << endl;
+  return;
+}
+
+class BinderWorkerService : public BBinder {
+ public:
+  BinderWorkerService() {
+  }
+  ~BinderWorkerService() {
+  }
+  virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                              uint32_t flags = 0) {
+    (void)flags;
+    (void)data;
+    (void)reply;
+    switch (code) {
+      // The transaction format is like
+      //
+      // data[in]:  int32: caller priority
+      //            int32: caller cpu
+      //
+      // reply[out]: int32: 1 if caller's priority != callee's priority
+      //             int32: 1 if caller's cpu != callee's cpu
+      //
+      // note the caller cpu read here is not always correct
+      // there're still chances that the caller got switched out
+      // right after it read the cpu number and still before the transaction.
+      case BINDER_NOP: {
+        thread_dump("binder");
+        int priority = thread_pri();
+        int priority_caller = data.readInt32();
+        int h = 0, s = 0;
+        if (priority_caller != priority) {
+          h++;
+          if (verbose) {
+            cout << "err priority_caller:" << priority_caller
+                 << ", priority:" << priority << endl;
+          }
+        }
+        if (priority == sched_get_priority_max(SCHED_FIFO)) {
+          int cpu = sched_getcpu();
+          int cpu_caller = data.readInt32();
+          if (cpu != cpu_caller) {
+            s++;
+          }
+        }
+        reply->writeInt32(h);
+        reply->writeInt32(s);
+        return NO_ERROR;
+      }
+      default:
+        return UNKNOWN_TRANSACTION;
+    };
+  }
+};
+
+class Pipe {
+  int m_readFd;
+  int m_writeFd;
+  Pipe(int readFd, int writeFd) : m_readFd{readFd}, m_writeFd{writeFd} {
+  }
+  Pipe(const Pipe&) = delete;
+  Pipe& operator=(const Pipe&) = delete;
+  Pipe& operator=(const Pipe&&) = delete;
+
+ public:
+  Pipe(Pipe&& rval) noexcept {
+    m_readFd = rval.m_readFd;
+    m_writeFd = rval.m_writeFd;
+    rval.m_readFd = 0;
+    rval.m_writeFd = 0;
+  }
+  ~Pipe() {
+    if (m_readFd) close(m_readFd);
+    if (m_writeFd) close(m_writeFd);
+  }
+  void signal() {
+    bool val = true;
+    int error = write(m_writeFd, &val, sizeof(val));
+    ASSERT(error >= 0);
+  };
+  void wait() {
+    bool val = false;
+    int error = read(m_readFd, &val, sizeof(val));
+    ASSERT(error >= 0);
+  }
+  template <typename T>
+  void send(const T& v) {
+    int error = write(m_writeFd, &v, sizeof(T));
+    ASSERT(error >= 0);
+  }
+  template <typename T>
+  void recv(T& v) {
+    int error = read(m_readFd, &v, sizeof(T));
+    ASSERT(error >= 0);
+  }
+  static tuple<Pipe, Pipe> createPipePair() {
+    int a[2];
+    int b[2];
+
+    int error1 = pipe(a);
+    int error2 = pipe(b);
+    ASSERT(error1 >= 0);
+    ASSERT(error2 >= 0);
+
+    return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
+  }
+};
+
+typedef chrono::time_point<chrono::high_resolution_clock> Tick;
+
+static inline Tick tickNow() {
+  return chrono::high_resolution_clock::now();
+}
+
+static inline uint64_t tickNano(Tick& sta, Tick& end) {
+  return uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - sta).count());
+}
+
+struct Results {
+  uint64_t m_best = 0xffffffffffffffffULL;
+  uint64_t m_worst = 0;
+  uint64_t m_transactions = 0;
+  uint64_t m_total_time = 0;
+  uint64_t m_miss = 0;
+
+  void add_time(uint64_t nano) {
+    m_best = min(nano, m_best);
+    m_worst = max(nano, m_worst);
+    m_transactions += 1;
+    m_total_time += nano;
+    if (nano > deadline_us * 1000) m_miss++;
+  }
+  void dump() {
+    double best = (double)m_best / 1.0E6;
+    double worst = (double)m_worst / 1.0E6;
+    double average = (double)m_total_time / m_transactions / 1.0E6;
+    // FIXME: libjson?
+    cout << std::setprecision(DUMP_PRICISION) << "{ \"avg\":" << setw(5) << left
+         << average << ", \"wst\":" << setw(5) << left << worst
+         << ", \"bst\":" << setw(5) << left << best << ", \"miss\":" << m_miss
+         << "}";
+  }
+};
+
+String16 generateServiceName(int num) {
+  char num_str[32];
+  snprintf(num_str, sizeof(num_str), "%d", num);
+  String16 serviceName = String16("binderWorker") + String16(num_str);
+  return serviceName;
+}
+
+static void parcel_fill(Parcel& data, int sz, int priority, int cpu) {
+  ASSERT(sz >= (int)sizeof(uint32_t) * 2);
+  data.writeInt32(priority);
+  data.writeInt32(cpu);
+  sz -= sizeof(uint32_t);
+  while (sz > (int)sizeof(uint32_t)) {
+    data.writeInt32(0);
+    sz -= sizeof(uint32_t);
+  }
+}
+
+static void* thread_start(void* p) {
+  Results* results_fifo = (Results*)p;
+  Parcel data, reply;
+  Tick sta, end;
+
+  parcel_fill(data, payload_size, thread_pri(), sched_getcpu());
+  thread_dump("fifo-caller");
+
+  sta = tickNow();
+  status_t ret = workers[0]->transact(BINDER_NOP, data, &reply);
+  end = tickNow();
+  results_fifo->add_time(tickNano(sta, end));
+
+  no_inherent += reply.readInt32();
+  no_sync += reply.readInt32();
+  return 0;
+}
+
+// create a fifo thread to transact and wait it to finished
+static void thread_transaction(Results* results_fifo) {
+  void* dummy;
+  pthread_t thread;
+  pthread_attr_t attr;
+  struct sched_param param;
+  ASSERT(!pthread_attr_init(&attr));
+  ASSERT(!pthread_attr_setschedpolicy(&attr, SCHED_FIFO));
+  param.sched_priority = sched_get_priority_max(SCHED_FIFO);
+  ASSERT(!pthread_attr_setschedparam(&attr, &param));
+  ASSERT(!pthread_create(&thread, &attr, &thread_start, results_fifo));
+  ASSERT(!pthread_join(thread, &dummy));
+}
+
+#define is_client(_num) ((_num) >= (no_process / 2))
+
+void worker_fx(int num, int no_process, int iterations, int payload_size,
+               Pipe p) {
+  int dummy;
+  Results results_other, results_fifo;
+
+  // Create BinderWorkerService and for go.
+  ProcessState::self()->startThreadPool();
+  sp<IServiceManager> serviceMgr = defaultServiceManager();
+  sp<BinderWorkerService> service = new BinderWorkerService;
+  serviceMgr->addService(generateServiceName(num), service);
+  p.signal();
+  p.wait();
+
+  // If client/server pairs, then half the workers are
+  // servers and half are clients
+  int server_count = no_process / 2;
+
+  for (int i = 0; i < server_count; i++) {
+    // self service is in-process so just skip
+    if (num == i) continue;
+    workers.push_back(serviceMgr->getService(generateServiceName(i)));
+  }
+
+  // Client for each pair iterates here
+  // each iterations contains exatcly 2 transactions
+  for (int i = 0; is_client(num) && i < iterations; i++) {
+    Parcel data, reply;
+    Tick sta, end;
+    // the target is paired to make it easier to diagnose
+    int target = num % server_count;
+
+    // 1. transaction by fifo thread
+    thread_transaction(&results_fifo);
+    parcel_fill(data, payload_size, thread_pri(), sched_getcpu());
+    thread_dump("other-caller");
+
+    // 2. transaction by other thread
+    sta = tickNow();
+    ASSERT(NO_ERROR == workers[target]->transact(BINDER_NOP, data, &reply));
+    end = tickNow();
+    results_other.add_time(tickNano(sta, end));
+
+    no_inherent += reply.readInt32();
+    no_sync += reply.readInt32();
+  }
+  // Signal completion to master and wait.
+  p.signal();
+  p.wait();
+
+  p.send(&dummy);
+  p.wait();
+  // Client for each pair dump here
+  if (is_client(num)) {
+    int no_trans = iterations * 2;
+    double sync_ratio = (1.0 - (double)no_sync / no_trans);
+    // FIXME: libjson?
+    cout << "\"P" << (num - server_count) << "\":{\"SYNC\":\""
+         << ((sync_ratio > GOOD_SYNC_MIN) ? "GOOD" : "POOR") << "\","
+         << "\"S\":" << (no_trans - no_sync) << ",\"I\":" << no_trans << ","
+         << "\"R\":" << sync_ratio << "," << endl;
+
+    cout << "      \"other_ms\":";
+    results_other.dump();
+    cout << "," << endl;
+    cout << "      \"fifo_ms\": ";
+    results_fifo.dump();
+    cout << endl;
+    cout << "}," << endl;
+  }
+  exit(no_inherent);
+}
+
+Pipe make_process(int num, int iterations, int no_process, int payload_size) {
+  auto pipe_pair = Pipe::createPipePair();
+  pid_t pid = fork();
+  if (pid) {
+    // parent
+    return move(get<0>(pipe_pair));
+  } else {
+    // child
+    thread_dump(is_client(num) ? "client" : "server");
+    worker_fx(num, no_process, iterations, payload_size,
+              move(get<1>(pipe_pair)));
+    // never get here
+    return move(get<0>(pipe_pair));
+  }
+}
+
+void wait_all(vector<Pipe>& v) {
+  for (size_t i = 0; i < v.size(); i++) {
+    v[i].wait();
+  }
+}
+
+void signal_all(vector<Pipe>& v) {
+  for (size_t i = 0; i < v.size(); i++) {
+    v[i].signal();
+  }
+}
+
+// This test is modified from binderThroughputTest.cpp
+int main(int argc, char** argv) {
+  for (int i = 1; i < argc; i++) {
+    if (string(argv[i]) == "-i") {
+      iterations = atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (string(argv[i]) == "-pair") {
+      no_process = 2 * atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (string(argv[i]) == "-deadline_us") {
+      deadline_us = atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (string(argv[i]) == "-v") {
+      verbose = 1;
+      i++;
+    }
+  }
+  vector<Pipe> pipes;
+  thread_dump("main");
+  // FIXME: libjson?
+  cout << "{" << endl;
+  cout << "\"cfg\":{\"pair\":" << (no_process / 2)
+       << ",\"iterations\":" << iterations << ",\"deadline_us\":" << deadline_us
+       << "}," << endl;
+
+  // the main process fork 2 processes for each pairs
+  // 1 server + 1 client
+  // each has a pipe to communicate with
+  for (int i = 0; i < no_process; i++) {
+    pipes.push_back(make_process(i, iterations, no_process, payload_size));
+  }
+  wait_all(pipes);
+  signal_all(pipes);
+  wait_all(pipes);
+  signal_all(pipes);
+  for (int i = 0; i < no_process; i++) {
+    int status;
+    pipes[i].signal();
+    wait(&status);
+    // the exit status is number of transactions without priority inheritance
+    // detected in the child process
+    no_inherent += status;
+  }
+  // FIXME: libjson?
+  cout << "\"inheritance\": " << (no_inherent == 0 ? "\"PASS\"" : "\"FAIL\"")
+       << endl;
+  cout << "}" << endl;
+  return -no_inherent;
+}
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp
index 49390d9..5b4bf23 100644
--- a/libs/ui/ColorSpace.cpp
+++ b/libs/ui/ColorSpace.cpp
@@ -20,6 +20,83 @@
 
 namespace android {
 
+static constexpr float linearResponse(float v) {
+    return v;
+}
+
+static constexpr float rcpResponse(float x, const ColorSpace::TransferParameters& p) {
+    return x >= p.d * p.c ? (std::pow(x, 1.0f / p.g) - p.b) / p.a : x / p.c;
+}
+
+static constexpr float response(float x, const ColorSpace::TransferParameters& p) {
+    return x >= p.d ? std::pow(p.a * x + p.b, p.g) : p.c * x;
+}
+
+static constexpr float rcpFullResponse(float x, const ColorSpace::TransferParameters& p) {
+    return x >= p.d * p.c ? (std::pow(x - p.e, 1.0f / p.g) - p.b) / p.a : (x - p.f) / p.c;
+}
+
+static constexpr float fullResponse(float x, const ColorSpace::TransferParameters& p) {
+    return x >= p.d ? std::pow(p.a * x + p.b, p.g) + p.e : p.c * x + p.f;
+}
+
+static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
+    float xx = std::abs(x);
+    return std::copysign(xx >= d * c ? (std::pow(xx, 1.0f / g) - b) / a : xx / c, x);
+}
+
+static float absResponse(float x, float g, float a, float b, float c, float d) {
+   float xx = std::abs(x);
+   return std::copysign(xx >= d ? std::pow(a * xx + b, g) : c * xx, x);
+}
+
+static float safePow(float x, float e) {
+    return powf(x < 0.0f ? 0.0f : x, e);
+}
+
+static ColorSpace::transfer_function toOETF(const ColorSpace::TransferParameters& parameters) {
+    if (parameters.e == 0.0f && parameters.f == 0.0f) {
+        return std::bind(rcpResponse, _1, parameters);
+    }
+    return std::bind(rcpFullResponse, _1, parameters);
+}
+
+static ColorSpace::transfer_function toEOTF( const ColorSpace::TransferParameters& parameters) {
+    if (parameters.e == 0.0f && parameters.f == 0.0f) {
+        return std::bind(response, _1, parameters);
+    }
+    return std::bind(fullResponse, _1, parameters);
+}
+
+static ColorSpace::transfer_function toOETF(float gamma) {
+    if (gamma == 1.0f) {
+        return linearResponse;
+    }
+    return std::bind(safePow, _1, 1.0f / gamma);
+}
+
+static ColorSpace::transfer_function toEOTF(float gamma) {
+    if (gamma == 1.0f) {
+        return linearResponse;
+    }
+    return std::bind(safePow, _1, gamma);
+}
+
+static constexpr std::array<float2, 3> computePrimaries(const mat3& rgbToXYZ) {
+    float3 r(rgbToXYZ * float3{1, 0, 0});
+    float3 g(rgbToXYZ * float3{0, 1, 0});
+    float3 b(rgbToXYZ * float3{0, 0, 1});
+
+    return {{r.xy / dot(r, float3{1}),
+             g.xy / dot(g, float3{1}),
+             b.xy / dot(b, float3{1})}};
+}
+
+static constexpr float2 computeWhitePoint(const mat3& rgbToXYZ) {
+    float3 w(rgbToXYZ * float3{1});
+    return w.xy / dot(w, float3{1});
+}
+
 ColorSpace::ColorSpace(
         const std::string& name,
         const mat3& rgbToXYZ,
@@ -31,18 +108,41 @@
         , mXYZtoRGB(inverse(rgbToXYZ))
         , mOETF(std::move(OETF))
         , mEOTF(std::move(EOTF))
-        , mClamper(std::move(clamper)) {
+        , mClamper(std::move(clamper))
+        , mPrimaries(computePrimaries(rgbToXYZ))
+        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
 
-    float3 r(rgbToXYZ * float3{1, 0, 0});
-    float3 g(rgbToXYZ * float3{0, 1, 0});
-    float3 b(rgbToXYZ * float3{0, 0, 1});
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const mat3& rgbToXYZ,
+        const TransferParameters parameters,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(rgbToXYZ)
+        , mXYZtoRGB(inverse(rgbToXYZ))
+        , mParameters(parameters)
+        , mOETF(toOETF(mParameters))
+        , mEOTF(toEOTF(mParameters))
+        , mClamper(std::move(clamper))
+        , mPrimaries(computePrimaries(rgbToXYZ))
+        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
+}
 
-    mPrimaries[0] = r.xy / dot(r, float3{1});
-    mPrimaries[1] = g.xy / dot(g, float3{1});
-    mPrimaries[2] = b.xy / dot(b, float3{1});
-
-    float3 w(rgbToXYZ * float3{1});
-    mWhitePoint = w.xy / dot(w, float3{1});
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const mat3& rgbToXYZ,
+        float gamma,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(rgbToXYZ)
+        , mXYZtoRGB(inverse(rgbToXYZ))
+        , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
+        , mOETF(toOETF(gamma))
+        , mEOTF(toEOTF(gamma))
+        , mClamper(std::move(clamper))
+        , mPrimaries(computePrimaries(rgbToXYZ))
+        , mWhitePoint(computeWhitePoint(rgbToXYZ)) {
 }
 
 ColorSpace::ColorSpace(
@@ -62,6 +162,40 @@
         , mWhitePoint(whitePoint) {
 }
 
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const std::array<float2, 3>& primaries,
+        const float2& whitePoint,
+        const TransferParameters parameters,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+        , mXYZtoRGB(inverse(mRGBtoXYZ))
+        , mParameters(parameters)
+        , mOETF(toOETF(mParameters))
+        , mEOTF(toEOTF(mParameters))
+        , mClamper(std::move(clamper))
+        , mPrimaries(primaries)
+        , mWhitePoint(whitePoint) {
+}
+
+ColorSpace::ColorSpace(
+        const std::string& name,
+        const std::array<float2, 3>& primaries,
+        const float2& whitePoint,
+        float gamma,
+        clamping_function clamper) noexcept
+        : mName(name)
+        , mRGBtoXYZ(computeXYZMatrix(primaries, whitePoint))
+        , mXYZtoRGB(inverse(mRGBtoXYZ))
+        , mParameters({gamma, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f})
+        , mOETF(toOETF(gamma))
+        , mEOTF(toEOTF(gamma))
+        , mClamper(std::move(clamper))
+        , mPrimaries(primaries)
+        , mWhitePoint(whitePoint) {
+}
+
 constexpr mat3 ColorSpace::computeXYZMatrix(
         const std::array<float2, 3>& primaries, const float2& whitePoint) {
     const float2& R = primaries[0];
@@ -96,33 +230,12 @@
     };
 }
 
-static constexpr float rcpResponse(float x, float g,float a, float b, float c, float d) {
-    return x >= d * c ? (std::pow(x, 1.0f / g) - b) / a : x / c;
-}
-
-static constexpr float response(float x, float g, float a, float b, float c, float d) {
-    return x >= d ? std::pow(a * x + b, g) : c * x;
-}
-
-static float absRcpResponse(float x, float g,float a, float b, float c, float d) {
-    return std::copysign(rcpResponse(std::abs(x), g, a, b, c, d), x);
-}
-
-static float absResponse(float x, float g, float a, float b, float c, float d) {
-    return std::copysign(response(std::abs(x), g, a, b, c, d), x);
-}
-
-static float safePow(float x, float e) {
-    return powf(x < 0.0f ? 0.0f : x, e);
-}
-
 const ColorSpace ColorSpace::sRGB() {
     return {
         "sRGB IEC61966-2.1",
         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
         {0.3127f, 0.3290f},
-        std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f),
-        std::bind(response,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f)
+        {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.04045f, 0.0f, 0.0f}
     };
 }
 
@@ -150,8 +263,7 @@
         "scRGB IEC 61966-2-2:2003",
         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
         {0.3127f, 0.3290f},
-        linearReponse,
-        linearReponse,
+        1.0f,
         std::bind(clamp<float>, _1, -0.5f, 7.499f)
     };
 }
@@ -161,8 +273,7 @@
         "NTSC (1953)",
         {{float2{0.67f, 0.33f}, {0.21f, 0.71f}, {0.14f, 0.08f}}},
         {0.310f, 0.316f},
-        std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
-        std::bind(response,    _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
     };
 }
 
@@ -171,8 +282,7 @@
         "Rec. ITU-R BT.709-5",
         {{float2{0.640f, 0.330f}, {0.300f, 0.600f}, {0.150f, 0.060f}}},
         {0.3127f, 0.3290f},
-        std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
-        std::bind(response,    _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
     };
 }
 
@@ -181,8 +291,7 @@
         "Rec. ITU-R BT.2020-1",
         {{float2{0.708f, 0.292f}, {0.170f, 0.797f}, {0.131f, 0.046f}}},
         {0.3127f, 0.3290f},
-        std::bind(rcpResponse, _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f),
-        std::bind(response,    _1, 1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f)
+        {1 / 0.45f, 1 / 1.099f, 0.099f / 1.099f, 1 / 4.5f, 0.081f, 0.0f, 0.0f}
     };
 }
 
@@ -191,8 +300,7 @@
         "Adobe RGB (1998)",
         {{float2{0.64f, 0.33f}, {0.21f, 0.71f}, {0.15f, 0.06f}}},
         {0.3127f, 0.3290f},
-        std::bind(safePow, _1, 1.0f / 2.2f),
-        std::bind(safePow, _1, 2.2f)
+        2.2f
     };
 }
 
@@ -201,8 +309,7 @@
         "ROMM RGB ISO 22028-2:2013",
         {{float2{0.7347f, 0.2653f}, {0.1596f, 0.8404f}, {0.0366f, 0.0001f}}},
         {0.34567f, 0.35850f},
-        std::bind(rcpResponse, _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f),
-        std::bind(response,    _1, 1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f)
+        {1.8f, 1.0f, 0.0f, 1 / 16.0f, 0.031248f, 0.0f, 0.0f}
     };
 }
 
@@ -211,8 +318,7 @@
         "Display P3",
         {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
         {0.3127f, 0.3290f},
-        std::bind(rcpResponse, _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f),
-        std::bind(response,    _1, 2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f)
+        {2.4f, 1 / 1.055f, 0.055f / 1.055f, 1 / 12.92f, 0.039f, 0.0f, 0.0f}
     };
 }
 
@@ -221,8 +327,7 @@
         "SMPTE RP 431-2-2007 DCI (P3)",
         {{float2{0.680f, 0.320f}, {0.265f, 0.690f}, {0.150f, 0.060f}}},
         {0.314f, 0.351f},
-        std::bind(safePow, _1, 1.0f / 2.6f),
-        std::bind(safePow, _1, 2.6f)
+        2.6f
     };
 }
 
@@ -231,8 +336,7 @@
         "SMPTE ST 2065-1:2012 ACES",
         {{float2{0.73470f, 0.26530f}, {0.0f, 1.0f}, {0.00010f, -0.0770f}}},
         {0.32168f, 0.33767f},
-        linearReponse,
-        linearReponse,
+        1.0f,
         std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
     };
 }
@@ -242,12 +346,33 @@
         "Academy S-2014-004 ACEScg",
         {{float2{0.713f, 0.293f}, {0.165f, 0.830f}, {0.128f, 0.044f}}},
         {0.32168f, 0.33767f},
-        linearReponse,
-        linearReponse,
+        1.0f,
         std::bind(clamp<float>, _1, -65504.0f, 65504.0f)
     };
 }
 
+std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size,
+        const ColorSpace& src, const ColorSpace& dst) {
+
+    size = clamp(size, 2u, 256u);
+    float m = 1.0f / float(size - 1);
+
+    std::unique_ptr<float3> lut(new float3[size * size * size]);
+    float3* data = lut.get();
+
+    ColorSpaceConnector connector(src, dst);
+
+    for (uint32_t z = 0; z < size; z++) {
+        for (int32_t y = int32_t(size - 1); y >= 0; y--) {
+            for (uint32_t x = 0; x < size; x++) {
+                *data++ = connector.transform({x * m, y * m, z * m});
+            }
+        }
+    }
+
+    return lut;
+}
+
 static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f};
 static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f};
 static const mat3 BRADFORD = mat3{
@@ -262,7 +387,7 @@
     return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix;
 }
 
-ColorSpace::Connector::Connector(
+ColorSpaceConnector::ColorSpaceConnector(
         const ColorSpace& src,
         const ColorSpace& dst) noexcept
         : mSource(src)
@@ -274,8 +399,8 @@
         mat3 rgbToXYZ(src.getRGBtoXYZ());
         mat3 xyzToRGB(dst.getXYZtoRGB());
 
-        float3 srcXYZ = XYZ(float3{src.getWhitePoint(), 1});
-        float3 dstXYZ = XYZ(float3{dst.getWhitePoint(), 1});
+        float3 srcXYZ = ColorSpace::XYZ(float3{src.getWhitePoint(), 1});
+        float3 dstXYZ = ColorSpace::XYZ(float3{dst.getWhitePoint(), 1});
 
         if (any(greaterThan(abs(src.getWhitePoint() - ILLUMINANT_D50_XY), float2{1e-3f}))) {
             rgbToXYZ = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * src.getRGBtoXYZ();
@@ -289,27 +414,4 @@
     }
 }
 
-std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size,
-        const ColorSpace& src, const ColorSpace& dst) {
-
-    size = clamp(size, 2u, 256u);
-    float m = 1.0f / float(size - 1);
-
-    std::unique_ptr<float3> lut(new float3[size * size * size]);
-    float3* data = lut.get();
-
-    Connector connector(src, dst);
-
-    for (uint32_t z = 0; z < size; z++) {
-        for (int32_t y = int32_t(size - 1); y >= 0; y--) {
-            for (uint32_t x = 0; x < size; x++) {
-                *data++ = connector.transform({x * m, y * m, z * m});
-            }
-        }
-    }
-
-    return lut;
-}
-
-
 }; // namespace android
diff --git a/libs/ui/tests/colorspace_test.cpp b/libs/ui/tests/colorspace_test.cpp
index 1e359d3..0a4873c 100644
--- a/libs/ui/tests/colorspace_test.cpp
+++ b/libs/ui/tests/colorspace_test.cpp
@@ -152,12 +152,12 @@
 
 TEST_F(ColorSpaceTest, Connect) {
     // No chromatic adaptation
-    auto r = ColorSpace::connect(ColorSpace::sRGB(), ColorSpace::AdobeRGB())
+    auto r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::AdobeRGB())
             .transform({1.0f, 0.5f, 0.0f});
     EXPECT_TRUE(all(lessThan(abs(r - float3{0.8912f, 0.4962f, 0.1164f}), float3{1e-4f})));
 
     // Test with chromatic adaptation
-    r = ColorSpace::connect(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB())
+    r = ColorSpaceConnector(ColorSpace::sRGB(), ColorSpace::ProPhotoRGB())
             .transform({1.0f, 0.0f, 0.0f});
     EXPECT_TRUE(all(lessThan(abs(r - float3{0.70226f, 0.2757f, 0.1036f}), float3{1e-4f})));
 }
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
index 81404a4..eb78348 100644
--- a/libs/vr/libdvrcommon/Android.bp
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -14,8 +14,6 @@
 
 sourceFiles = [
     "frame_time_history.cpp",
-    "revision.cpp",
-    "revision_path.cpp",
     "sync_util.cpp",
 ]
 
diff --git a/libs/vr/libdvrcommon/include/private/dvr/revision.h b/libs/vr/libdvrcommon/include/private/dvr/revision.h
deleted file mode 100644
index dda0fce..0000000
--- a/libs/vr/libdvrcommon/include/private/dvr/revision.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
-#define LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// List of DreamOS products
-typedef enum DvrProduct {
-  DVR_PRODUCT_UNKNOWN,
-  DVR_PRODUCT_A00,
-  DVR_PRODUCT_A65R,
-  DVR_PRODUCT_TWILIGHT = DVR_PRODUCT_A65R
-} DvrProduct;
-
-// List of possible revisions.
-typedef enum DvrRevision {
-  DVR_REVISION_UNKNOWN,
-  DVR_REVISION_P1,
-  DVR_REVISION_P2,
-  DVR_REVISION_P3,
-} DvrRevision;
-
-// Query the device's product.
-//
-// @return DvrProduct value, or DvrProductUnknown on error.
-DvrProduct dvr_get_product();
-
-// Query the device's revision.
-//
-// @return DvrRevision value, or DvrRevisionUnknown on error.
-DvrRevision dvr_get_revision();
-
-// Returns the device's board revision string.
-//
-// @return NULL-terminated string such as 'a00-p1'.
-const char* dvr_get_product_revision_str();
-
-// Returns the device's serial number.
-//
-// @return Returns NULL on error, or a NULL-terminated string.
-const char* dvr_get_serial_number();
-
-#ifdef __cplusplus
-}
-#endif  // extern "C"
-#endif  // LIBS_VR_LIBDVRCOMMON_INCLUDE_PRIVATE_DVR_REVISION_H_
diff --git a/libs/vr/libdvrcommon/revision.cpp b/libs/vr/libdvrcommon/revision.cpp
deleted file mode 100644
index 7925f65..0000000
--- a/libs/vr/libdvrcommon/revision.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "private/dvr/revision.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include "revision_path.h"
-
-namespace {
-
-// Allows quicker access to the product revision. If non-zero, then
-// the product revision file has already been processed.
-static bool global_product_revision_processed = false;
-
-static bool global_serial_number_processed = false;
-
-// The product.
-static DvrProduct global_product = DVR_PRODUCT_UNKNOWN;
-
-// The revision.
-static DvrRevision global_revision = DVR_REVISION_UNKNOWN;
-
-// Maximum size of the product revision string.
-constexpr int kProductRevisionStringSize = 32;
-
-// Maximum size of the serial number.
-constexpr int kSerialNumberStringSize = 32;
-
-// The product revision string.
-static char global_product_revision_str[kProductRevisionStringSize + 1] = "";
-
-// The serial number string
-static char global_serial_number[kSerialNumberStringSize + 1] = "";
-
-// Product and revision combinations.
-struct DvrProductRevision {
-  const char* str;
-  DvrProduct product;
-  DvrRevision revision;
-};
-
-// Null-terminated list of all product and revision combinations.
-static constexpr DvrProductRevision kProductRevisions[] = {
-    {"a00-p1", DVR_PRODUCT_A00, DVR_REVISION_P1},
-    {"a00-p2", DVR_PRODUCT_A00, DVR_REVISION_P2},
-    {"a00-p3", DVR_PRODUCT_A00, DVR_REVISION_P3},
-    {"twilight-p1", DVR_PRODUCT_A65R, DVR_REVISION_P1},
-    {"twilight-p2", DVR_PRODUCT_A65R, DVR_REVISION_P2},
-    {NULL, DVR_PRODUCT_UNKNOWN, DVR_REVISION_UNKNOWN}};
-
-// Read the product revision string, and store the global data.
-static void process_product_revision() {
-  int fd;
-  ssize_t read_rc;
-  const DvrProductRevision* product_revision = kProductRevisions;
-
-  // Of course in a multi-threaded environment, for a few microseconds
-  // during process startup, it is possible that this function will be
-  // called and execute fully multiple times. That is why the product
-  // revision string is statically allocated.
-
-  if (global_product_revision_processed)
-    return;
-
-  // Whether there was a failure or not, we don't want to do this again.
-  // Upon failure it's most likely to fail again anyway.
-
-  fd = open(dvr_product_revision_file_path(), O_RDONLY);
-  if (fd < 0) {
-    ALOGE("Could not open '%s' to get product revision: %s",
-          dvr_product_revision_file_path(), strerror(errno));
-    global_product_revision_processed = true;
-    return;
-  }
-
-  read_rc = read(fd, global_product_revision_str, kProductRevisionStringSize);
-  if (read_rc <= 0) {
-    ALOGE("Could not read from '%s': %s", dvr_product_revision_file_path(),
-          strerror(errno));
-    global_product_revision_processed = true;
-    return;
-  }
-
-  close(fd);
-
-  global_product_revision_str[read_rc] = '\0';
-
-  while (product_revision->str) {
-    if (!strcmp(product_revision->str, global_product_revision_str))
-      break;
-    product_revision++;
-  }
-
-  if (product_revision->str) {
-    global_product = product_revision->product;
-    global_revision = product_revision->revision;
-  } else {
-    ALOGE("Unable to match '%s' to a product/revision.",
-          global_product_revision_str);
-  }
-
-  global_product_revision_processed = true;
-}
-
-}  // anonymous namespace
-
-extern "C" DvrProduct dvr_get_product() {
-  process_product_revision();
-  return global_product;
-}
-
-extern "C" DvrRevision dvr_get_revision() {
-  process_product_revision();
-  return global_revision;
-}
-
-extern "C" const char* dvr_get_product_revision_str() {
-  process_product_revision();
-  return global_product_revision_str;
-}
-
-extern "C" const char* dvr_get_serial_number() {
-  process_product_revision();
-  if (global_product == DVR_PRODUCT_A00) {
-    if (!global_serial_number_processed) {
-#ifdef DVR_HOST
-      global_serial_number_processed = true;
-#else
-      int width = 4;
-      uintptr_t addr = 0x00074138;
-      uintptr_t endaddr = addr + width - 1;
-
-      int fd = open("/dev/mem", O_RDWR | O_SYNC);
-      if (fd < 0) {
-        if (errno == EPERM)
-          global_serial_number_processed = true;
-        fprintf(stderr, "cannot open /dev/mem\n");
-        return global_serial_number;
-      }
-
-      off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
-      size_t mmap_size = endaddr - mmap_start + 1;
-      mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
-      void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
-                          mmap_start);
-
-      if (page == MAP_FAILED) {
-        global_serial_number_processed = true;
-        fprintf(stderr, "cannot mmap region\n");
-        close(fd);
-        return global_serial_number;
-      }
-
-      uint32_t* x =
-          reinterpret_cast<uint32_t*>((((uintptr_t)page) + (addr & 4095)));
-      snprintf(global_serial_number, kSerialNumberStringSize, "%08x", *x);
-      global_serial_number_processed = true;
-
-      munmap(page, mmap_size);
-      close(fd);
-#endif
-    }
-    return global_serial_number;
-  } else {
-    return nullptr;
-  }
-}
diff --git a/libs/vr/libdvrcommon/revision_path.cpp b/libs/vr/libdvrcommon/revision_path.cpp
deleted file mode 100644
index c49f9aa..0000000
--- a/libs/vr/libdvrcommon/revision_path.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "revision_path.h"
-
-namespace {
-
-// The path to the product revision file.
-static const char* kProductRevisionFilePath =
-    "/sys/firmware/devicetree/base/goog,board-revision";
-
-}  // anonymous namespace
-
-// This exists in a separate file so that it can be replaced for
-// testing.
-const char* dvr_product_revision_file_path() {
-  return kProductRevisionFilePath;
-}
diff --git a/libs/vr/libdvrcommon/revision_path.h b/libs/vr/libdvrcommon/revision_path.h
deleted file mode 100644
index afcea46..0000000
--- a/libs/vr/libdvrcommon/revision_path.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
-#define ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
-
-// Returns the revision file path.
-// This exists in a separate file so that it can be replaced for
-// testing.
-const char* dvr_product_revision_file_path();
-
-#endif  // ANDROID_DVR_LIBDVRCOMMON_REVISION_PATH_H_
diff --git a/libs/vr/libdvrcommon/tests/revision_app_tests.cpp b/libs/vr/libdvrcommon/tests/revision_app_tests.cpp
deleted file mode 100644
index 772481b..0000000
--- a/libs/vr/libdvrcommon/tests/revision_app_tests.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <dvr/test/app_test.h>
-#include <gtest/gtest.h>
-#include <private/dvr/revision.h>
-
-// Making sure this information is not available
-// inside the sandbox
-
-namespace {
-
-TEST(RevisionTests, GetProduct) {
-  ASSERT_EQ(DVR_PRODUCT_UNKNOWN, dvr_get_product());
-}
-
-TEST(RevisionTests, GetRevision) {
-  ASSERT_EQ(DVR_REVISION_UNKNOWN, dvr_get_revision());
-}
-
-TEST(RevisionTests, GetRevisionStr) {
-  ASSERT_STREQ("", dvr_get_product_revision_str());
-}
-
-TEST(RevisionTests, GetSerialNo) {
-  ASSERT_EQ(nullptr, dvr_get_serial_number());
-}
-
-}  // namespace
-
-int main(int argc, char* argv[]) {
-  dreamos::test::AppTestBegin();
-  ::testing::InitGoogleTest(&argc, argv);
-  int result = RUN_ALL_TESTS();
-  dreamos::test::AppTestEnd(result);
-  return result;
-}
diff --git a/libs/vr/libdvrcommon/tests/revision_tests.cpp b/libs/vr/libdvrcommon/tests/revision_tests.cpp
deleted file mode 100644
index 9abf480..0000000
--- a/libs/vr/libdvrcommon/tests/revision_tests.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <gtest/gtest.h>
-#include <private/dvr/revision.h>
-
-namespace {
-
-TEST(RevisionTests, GetProduct) {
-  ASSERT_NE(DVR_PRODUCT_UNKNOWN, dvr_get_product());
-}
-
-TEST(RevisionTests, GetRevision) {
-  ASSERT_NE(DVR_REVISION_UNKNOWN, dvr_get_revision());
-}
-
-TEST(RevisionTests, GetRevisionStr) {
-  ASSERT_NE(nullptr, dvr_get_product_revision_str());
-}
-
-TEST(RevisionTests, GetSerialNo) {
-  ASSERT_NE(nullptr, dvr_get_serial_number());
-}
-
-}  // namespace
-
-int main(int argc, char* argv[]) {
-  ::testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/libs/vr/libpdx/service.cpp b/libs/vr/libpdx/service.cpp
index d2804d5..daf9af8 100644
--- a/libs/vr/libpdx/service.cpp
+++ b/libs/vr/libpdx/service.cpp
@@ -129,7 +129,7 @@
   PDX_TRACE_NAME("Message::PushFileHandle");
   if (auto svc = service_.lock()) {
     ErrnoGuard errno_guard;
-    return svc->endpoint()->PushFileHandle(this, handle);
+    return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
   } else {
     return -ESHUTDOWN;
   }
@@ -139,7 +139,7 @@
   PDX_TRACE_NAME("Message::PushFileHandle");
   if (auto svc = service_.lock()) {
     ErrnoGuard errno_guard;
-    return svc->endpoint()->PushFileHandle(this, handle);
+    return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
   } else {
     return -ESHUTDOWN;
   }
@@ -149,7 +149,7 @@
   PDX_TRACE_NAME("Message::PushFileHandle");
   if (auto svc = service_.lock()) {
     ErrnoGuard errno_guard;
-    return svc->endpoint()->PushFileHandle(this, handle);
+    return ReturnCodeOrError(svc->endpoint()->PushFileHandle(this, handle));
   } else {
     return -ESHUTDOWN;
   }
@@ -159,7 +159,7 @@
   PDX_TRACE_NAME("Message::PushChannelHandle");
   if (auto svc = service_.lock()) {
     ErrnoGuard errno_guard;
-    return svc->endpoint()->PushChannelHandle(this, handle);
+    return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
   } else {
     return -ESHUTDOWN;
   }
@@ -170,7 +170,7 @@
   PDX_TRACE_NAME("Message::PushChannelHandle");
   if (auto svc = service_.lock()) {
     ErrnoGuard errno_guard;
-    return svc->endpoint()->PushChannelHandle(this, handle);
+    return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
   } else {
     return -ESHUTDOWN;
   }
@@ -180,7 +180,7 @@
   PDX_TRACE_NAME("Message::PushChannelHandle");
   if (auto svc = service_.lock()) {
     ErrnoGuard errno_guard;
-    return svc->endpoint()->PushChannelHandle(this, handle);
+    return ReturnCodeOrError(svc->endpoint()->PushChannelHandle(this, handle));
   } else {
     return -ESHUTDOWN;
   }
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 2a17a7f..080c02b 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -52,6 +52,31 @@
 }
 
 SensorDevice::SensorDevice() : mHidlTransportErrors(20) {
+    if (!connectHidlService()) {
+        return;
+    }
+    checkReturn(mSensors->getSensorsList(
+            [&](const auto &list) {
+                const size_t count = list.size();
+
+                mActivationCount.setCapacity(count);
+                Info model;
+                for (size_t i=0 ; i < count; i++) {
+                    sensor_t sensor;
+                    convertToSensor(list[i], &sensor);
+                    mSensorList.push_back(sensor);
+
+                    mActivationCount.add(list[i].sensorHandle, model);
+
+                    checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
+                }
+            }));
+
+    mIsDirectReportSupported =
+           (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION);
+}
+
+bool SensorDevice::connectHidlService() {
     // SensorDevice may wait upto 100ms * 10 = 1s for hidl service.
     constexpr auto RETRY_DELAY = std::chrono::milliseconds(100);
     size_t retry = 10;
@@ -74,7 +99,7 @@
 
         if (--retry <= 0) {
             ALOGE("Cannot connect to ISensors hidl service!");
-            return;
+            break;
         }
         // Delay 100ms before retry, hidl service is expected to come up in short time after
         // crash.
@@ -82,29 +107,11 @@
                 (initStep == 0) ? "getService()" : "poll() check", retry);
         std::this_thread::sleep_for(RETRY_DELAY);
     }
-
-    checkReturn(mSensors->getSensorsList(
-            [&](const auto &list) {
-                const size_t count = list.size();
-
-                mActivationCount.setCapacity(count);
-                Info model;
-                for (size_t i=0 ; i < count; i++) {
-                    sensor_t sensor;
-                    convertToSensor(list[i], &sensor);
-                    mSensorList.push_back(sensor);
-
-                    mActivationCount.add(list[i].sensorHandle, model);
-
-                    checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
-                }
-            }));
-
-    mIsDirectReportSupported =
-           (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION);
+    return (mSensors != nullptr);
 }
 
 void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) {
+    // not need to check mSensors because this is is only called after successful poll()
     if (connected) {
         Info model;
         mActivationCount.add(handle, model);
@@ -115,64 +122,38 @@
 }
 
 std::string SensorDevice::dump() const {
-    if (mSensors == NULL) return "HAL not initialized\n";
+    if (mSensors == nullptr) return "HAL not initialized\n";
 
     String8 result;
+    result.appendFormat("Total %zu h/w sensors, %zu running:\n",
+                        mSensorList.size(), mActivationCount.size());
 
-    result.appendFormat("Saw %d hidlTransport Errors\n", mTotalHidlTransportErrors);
-    for (auto it = mHidlTransportErrors.begin() ; it != mHidlTransportErrors.end(); it++ ) {
-        result += "\t";
-        result += it->toString();
-        result += "\n";
+    Mutex::Autolock _l(mLock);
+    for (const auto & s : mSensorList) {
+        int32_t handle = s.handle;
+        const Info& info = mActivationCount.valueFor(handle);
+        if (info.batchParams.isEmpty()) continue;
+
+        result.appendFormat("0x%08x) active-count = %zu; ", handle, info.batchParams.size());
+
+        result.append("sampling_period(ms) = {");
+        for (size_t j = 0; j < info.batchParams.size(); j++) {
+            const BatchParams& params = info.batchParams.valueAt(j);
+            result.appendFormat("%.1f%s", params.batchDelay / 1e6f,
+                j < info.batchParams.size() - 1 ? ", " : "");
+        }
+        result.appendFormat("}, selected = %.1f ms; ", info.bestBatchParams.batchDelay / 1e6f);
+
+        result.append("batching_period(ms) = {");
+        for (size_t j = 0; j < info.batchParams.size(); j++) {
+            BatchParams params = info.batchParams.valueAt(j);
+
+            result.appendFormat("%.1f%s", params.batchTimeout / 1e6f,
+                    j < info.batchParams.size() - 1 ? ", " : "");
+        }
+        result.appendFormat("}, selected = %.1f ms\n", info.bestBatchParams.batchTimeout / 1e6f);
     }
 
-    checkReturn(mSensors->getSensorsList([&](const auto &list){
-            const size_t count = list.size();
-
-            result.appendFormat(
-                "Total %zu h/w sensors, %zu running:\n",
-                count,
-                mActivationCount.size());
-
-            Mutex::Autolock _l(mLock);
-            for (size_t i = 0 ; i < count ; i++) {
-                const Info& info = mActivationCount.valueFor(
-                    list[i].sensorHandle);
-
-                if (info.batchParams.isEmpty()) continue;
-                result.appendFormat(
-                    "0x%08x) active-count = %zu; ",
-                    list[i].sensorHandle,
-                    info.batchParams.size());
-
-                result.append("sampling_period(ms) = {");
-                for (size_t j = 0; j < info.batchParams.size(); j++) {
-                    const BatchParams& params = info.batchParams.valueAt(j);
-                    result.appendFormat(
-                        "%.1f%s",
-                        params.batchDelay / 1e6f,
-                        j < info.batchParams.size() - 1 ? ", " : "");
-                }
-                result.appendFormat(
-                        "}, selected = %.1f ms; ",
-                        info.bestBatchParams.batchDelay / 1e6f);
-
-                result.append("batching_period(ms) = {");
-                for (size_t j = 0; j < info.batchParams.size(); j++) {
-                    BatchParams params = info.batchParams.valueAt(j);
-
-                    result.appendFormat(
-                            "%.1f%s",
-                            params.batchTimeout / 1e6f,
-                            j < info.batchParams.size() - 1 ? ", " : "");
-                }
-
-                result.appendFormat(
-                        "}, selected = %.1f ms\n",
-                        info.bestBatchParams.batchTimeout / 1e6f);
-            }
-        }));
-
     return result.string();
 }
 
@@ -187,7 +168,7 @@
 }
 
 ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
-    if (mSensors == NULL) return NO_INIT;
+    if (mSensors == nullptr) return NO_INIT;
 
     ssize_t err;
     int numHidlTransportErrors = 0;
@@ -239,7 +220,7 @@
 }
 
 status_t SensorDevice::activate(void* ident, int handle, int enabled) {
-    if (mSensors == NULL) return NO_INIT;
+    if (mSensors == nullptr) return NO_INIT;
 
     status_t err(NO_ERROR);
     bool actuateHardware = false;
@@ -328,7 +309,7 @@
         int flags,
         int64_t samplingPeriodNs,
         int64_t maxBatchReportLatencyNs) {
-    if (mSensors == NULL) return NO_INIT;
+    if (mSensors == nullptr) return NO_INIT;
 
     if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
         samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
@@ -382,7 +363,7 @@
 }
 
 status_t SensorDevice::setDelay(void* ident, int handle, int64_t samplingPeriodNs) {
-    if (mSensors == NULL) return NO_INIT;
+    if (mSensors == nullptr) return NO_INIT;
     if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
         samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
     }
@@ -407,11 +388,12 @@
 }
 
 int SensorDevice::getHalDeviceVersion() const {
-    if (mSensors == NULL) return -1;
+    if (mSensors == nullptr) return -1;
     return SENSORS_DEVICE_API_VERSION_1_4;
 }
 
 status_t SensorDevice::flush(void* ident, int handle) {
+    if (mSensors == nullptr) return NO_INIT;
     if (isClientDisabled(ident)) return INVALID_OPERATION;
     ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
     return StatusFromResult(checkReturn(mSensors->flush(handle)));
@@ -427,6 +409,7 @@
 }
 
 void SensorDevice::enableAllSensors() {
+    if (mSensors == nullptr) return;
     Mutex::Autolock _l(mLock);
     mDisabledClients.clear();
     ALOGI("cleared mDisabledClients");
@@ -453,8 +436,9 @@
 }
 
 void SensorDevice::disableAllSensors() {
+    if (mSensors == nullptr) return;
     Mutex::Autolock _l(mLock);
-   for (size_t i = 0; i< mActivationCount.size(); ++i) {
+    for (size_t i = 0; i< mActivationCount.size(); ++i) {
         const Info& info = mActivationCount.valueAt(i);
         // Check if this sensor has been activated previously and disable it.
         if (info.batchParams.size() > 0) {
@@ -475,6 +459,7 @@
 
 status_t SensorDevice::injectSensorData(
         const sensors_event_t *injected_sensor_event) {
+    if (mSensors == nullptr) return NO_INIT;
     ALOGD_IF(DEBUG_CONNECTIONS,
             "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
             injected_sensor_event->sensor,
@@ -490,10 +475,97 @@
 }
 
 status_t SensorDevice::setMode(uint32_t mode) {
+    if (mSensors == nullptr) return NO_INIT;
+    return StatusFromResult(
+            checkReturn(mSensors->setOperationMode(
+                    static_cast<hardware::sensors::V1_0::OperationMode>(mode))));
+}
 
-     return StatusFromResult(
-             checkReturn(mSensors->setOperationMode(
-                 static_cast<hardware::sensors::V1_0::OperationMode>(mode))));
+int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
+    if (mSensors == nullptr) return NO_INIT;
+    Mutex::Autolock _l(mLock);
+
+    SharedMemType type;
+    switch (memory->type) {
+        case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+            type = SharedMemType::ASHMEM;
+            break;
+        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+            type = SharedMemType::GRALLOC;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    SharedMemFormat format;
+    if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
+        return BAD_VALUE;
+    }
+    format = SharedMemFormat::SENSORS_EVENT;
+
+    SharedMemInfo mem = {
+        .type = type,
+        .format = format,
+        .size = static_cast<uint32_t>(memory->size),
+        .memoryHandle = memory->handle,
+    };
+
+    int32_t ret;
+    checkReturn(mSensors->registerDirectChannel(mem,
+            [&ret](auto result, auto channelHandle) {
+                if (result == Result::OK) {
+                    ret = channelHandle;
+                } else {
+                    ret = StatusFromResult(result);
+                }
+            }));
+    return ret;
+}
+
+void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
+    if (mSensors == nullptr) return;
+    Mutex::Autolock _l(mLock);
+    checkReturn(mSensors->unregisterDirectChannel(channelHandle));
+}
+
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
+        int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
+    if (mSensors == nullptr) return NO_INIT;
+    Mutex::Autolock _l(mLock);
+
+    RateLevel rate;
+    switch(config->rate_level) {
+        case SENSOR_DIRECT_RATE_STOP:
+            rate = RateLevel::STOP;
+            break;
+        case SENSOR_DIRECT_RATE_NORMAL:
+            rate = RateLevel::NORMAL;
+            break;
+        case SENSOR_DIRECT_RATE_FAST:
+            rate = RateLevel::FAST;
+            break;
+        case SENSOR_DIRECT_RATE_VERY_FAST:
+            rate = RateLevel::VERY_FAST;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    int32_t ret;
+    checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
+            [&ret, rate] (auto result, auto token) {
+                if (rate == RateLevel::STOP) {
+                    ret = StatusFromResult(result);
+                } else {
+                    if (result == Result::OK) {
+                        ret = token;
+                    } else {
+                        ret = StatusFromResult(result);
+                    }
+                }
+            }));
+
+    return ret;
 }
 
 // ---------------------------------------------------------------------------
@@ -555,90 +627,6 @@
     mDisabledClients.remove(ident);
 }
 
-int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
-    Mutex::Autolock _l(mLock);
-
-    SharedMemType type;
-    switch (memory->type) {
-        case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
-            type = SharedMemType::ASHMEM;
-            break;
-        case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
-            type = SharedMemType::GRALLOC;
-            break;
-        default:
-            return BAD_VALUE;
-    }
-
-    SharedMemFormat format;
-    if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
-        return BAD_VALUE;
-    }
-    format = SharedMemFormat::SENSORS_EVENT;
-
-    SharedMemInfo mem = {
-        .type = type,
-        .format = format,
-        .size = static_cast<uint32_t>(memory->size),
-        .memoryHandle = memory->handle,
-    };
-
-    int32_t ret;
-    checkReturn(mSensors->registerDirectChannel(mem,
-            [&ret](auto result, auto channelHandle) {
-                if (result == Result::OK) {
-                    ret = channelHandle;
-                } else {
-                    ret = StatusFromResult(result);
-                }
-            }));
-    return ret;
-}
-
-void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
-    Mutex::Autolock _l(mLock);
-    checkReturn(mSensors->unregisterDirectChannel(channelHandle));
-}
-
-int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
-        int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
-    Mutex::Autolock _l(mLock);
-
-    RateLevel rate;
-    switch(config->rate_level) {
-        case SENSOR_DIRECT_RATE_STOP:
-            rate = RateLevel::STOP;
-            break;
-        case SENSOR_DIRECT_RATE_NORMAL:
-            rate = RateLevel::NORMAL;
-            break;
-        case SENSOR_DIRECT_RATE_FAST:
-            rate = RateLevel::FAST;
-            break;
-        case SENSOR_DIRECT_RATE_VERY_FAST:
-            rate = RateLevel::VERY_FAST;
-            break;
-        default:
-            return BAD_VALUE;
-    }
-
-    int32_t ret;
-    checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
-            [&ret, rate] (auto result, auto token) {
-                if (rate == RateLevel::STOP) {
-                    ret = StatusFromResult(result);
-                } else {
-                    if (result == Result::OK) {
-                        ret = token;
-                    } else {
-                        ret = StatusFromResult(result);
-                    }
-                }
-            }));
-
-    return ret;
-}
-
 bool SensorDevice::isDirectReportSupported() const {
     return mIsDirectReportSupported;
 }
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 410531b..2520a81 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -161,6 +161,7 @@
     // Use this vector to determine which client is activated or deactivated.
     SortedVector<void *> mDisabledClients;
     SensorDevice();
+    bool connectHidlService();
 
     static void handleHidlDeath(const std::string &detail);
     template<typename T>
diff --git a/services/vr/vr_window_manager/application.cpp b/services/vr/vr_window_manager/application.cpp
index c7d7d50..b2f02e5 100644
--- a/services/vr/vr_window_manager/application.cpp
+++ b/services/vr/vr_window_manager/application.cpp
@@ -174,7 +174,12 @@
   // TODO(steventhomas): If we're not visible, block until we are. For now we
   // throttle by calling dvrGraphicsWaitNextFrame.
   DvrFrameSchedule schedule;
-  dvrGraphicsWaitNextFrame(graphics_context_, 0, &schedule);
+  int status = dvrGraphicsWaitNextFrame(graphics_context_, 0, &schedule);
+  if (status < 0) {
+    ALOGE("Context lost, deallocating graphics resources");
+    SetVisibility(false);
+    DeallocateResources();
+  }
 
   OnDrawFrame();
 
diff --git a/services/vr/vr_window_manager/shell_view.cpp b/services/vr/vr_window_manager/shell_view.cpp
index 67ef5d4..c270be2 100644
--- a/services/vr/vr_window_manager/shell_view.cpp
+++ b/services/vr/vr_window_manager/shell_view.cpp
@@ -126,10 +126,6 @@
   if (!surface_flinger_view_->Initialize(this))
     return 1;
 
-  // This is a temporary fix for now. These APIs will be changed when everything
-  // is moved into vrcore.
-  display_client_ = DisplayClient::Create();
-
   return 0;
 }
 
@@ -164,7 +160,14 @@
 }
 
 void ShellView::DeallocateResources() {
-  surface_flinger_view_.reset();
+  {
+    std::unique_lock<std::mutex> l(display_frame_mutex_);
+    removed_displays_.clear();
+    new_displays_.clear();
+    displays_.clear();
+  }
+
+  display_client_.reset();
   reticle_.reset();
   controller_mesh_.reset();
   program_.reset(new ShaderProgram);
@@ -283,6 +286,20 @@
 
   bool showing = false;
 
+  // This is a temporary fix for now. These APIs will be changed when everything
+  // is moved into vrcore.
+  // Do this on demand in case vr_flinger crashed and we are reconnecting.
+  if (!display_client_.get()) {
+    int error = 0;
+    display_client_ = DisplayClient::Create(&error);
+
+    if (error) {
+      ALOGE("Could not connect to display service : %s(%d)", strerror(error),
+            error);
+      return base::unique_fd();
+    }
+  }
+
   // TODO(achaulk): change when moved into vrcore.
   bool vr_running = display_client_->IsVrAppRunning();