aboutsummaryrefslogtreecommitdiff
path: root/host/msm/daemon/chre_daemon.cc
blob: f4b72102165b8a42e15e7ad5894479448f2f3807 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 * The daemon that hosts CHRE on a hexagon DSP via FastRPC. This is typically
 * the SLPI but could be the ADSP or another DSP that supports FastRPC.
 *
 * Several threads are required for this functionality:
 *   - Main thread: blocked waiting on SIGINT/SIGTERM, and requests graceful
 *     shutdown of CHRE when caught
 *   - Monitor thread: persistently blocked in a FastRPC call to the DSP that
 *     only returns when CHRE exits or the DSP crashes
 *     - TODO: see whether we can merge this with the RX thread
 *   - Reverse monitor thread: after initializing the DSP-side monitor for this
 *     process, blocks on a condition variable. If this thread exits, CHRE on
 *     the DSP side will be notified and shut down (this is only possible if
 *     this thread is not blocked in a FastRPC call).
 *     - TODO: confirm this and see whether we can merge this responsibility
 *       into the TX thread
 *   - Message to host (RX) thread: blocks in FastRPC call, waiting on incoming
 *     message from CHRE
 *   - Message to CHRE (TX) thread: blocks waiting on outbound queue, delivers
 *     messages to CHRE over FastRPC
 *
 * TODO: This file originated from an implementation for another device, and was
 * written in C, but then it was converted to C++ when adding socket support. It
 * should be fully converted to C++.
 */

// Disable verbose logging
// TODO: use property_get_bool to make verbose logging runtime configurable
// #define LOG_NDEBUG 0

#include <ctype.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <fstream>
#include <queue>
#include <string>

#include "chre/platform/slpi/fastrpc.h"
#include "chre_host/host_protocol_host.h"
#include "chre_host/log.h"
#include "chre_host/socket_server.h"
#include "generated/chre_slpi.h"

#include <android-base/logging.h>
#include <json/json.h>
#include <utils/SystemClock.h>

#ifdef CHRE_USE_TOKENIZED_LOGGING
#include "pw_tokenizer/detokenize.h"
using pw::tokenizer::DetokenizedString;
using pw::tokenizer::Detokenizer;
#endif

#ifdef CHRE_DAEMON_LOAD_INTO_SENSORSPD
#include "remote.h"

#define ITRANSPORT_PREFIX "'\":;./\\"
#endif  // CHRE_DAEMON_LOAD_INTO_SENSORSPD

//! The format string to use for logs from the CHRE implementation.
#define HUB_LOG_FORMAT_STR "@ %3" PRIu32 ".%03" PRIu32 ": [CHRE] %s"

#ifndef UNUSED_VAR
#define UNUSED_VAR(var) ((void)(var))
#endif

#ifdef CHRE_DAEMON_LPMA_ENABLED
#include <android/hardware/soundtrigger/2.0/ISoundTriggerHw.h>
#include <hardware_legacy/power.h>

using android::sp;
using android::wp;
using android::hardware::Return;
using android::hardware::soundtrigger::V2_0::ISoundTriggerHw;
using android::hardware::soundtrigger::V2_0::SoundModelHandle;
using android::hardware::soundtrigger::V2_0::SoundModelType;
#endif  // CHRE_DAEMON_LPMA_ENABLED

using android::elapsedRealtimeNano;
using android::chre::FragmentedLoadTransaction;
using android::chre::HostProtocolHost;

// Aliased for consistency with the way these symbols are referenced in
// CHRE-side code
namespace fbs = ::chre::fbs;

typedef void *(thread_entry_point_f)(void *);

struct reverse_monitor_thread_data {
  pthread_t thread;
  pthread_mutex_t mutex;
  pthread_cond_t cond;
};

static void *chre_message_to_host_thread(void *arg);
static void *chre_monitor_thread(void *arg);
static bool start_thread(pthread_t *thread_handle,
                         thread_entry_point_f *thread_entry, void *arg);

#ifdef CHRE_DAEMON_LPMA_ENABLED
//! The name of the wakelock to use for the CHRE daemon.
static const char kWakeLockName[] = "chre_daemon";

//! Forward declarations
static void onStHalServiceDeath();

//! Class to handle when a connected ST HAL service dies.
class StHalDeathRecipient : public android::hardware::hidl_death_recipient {
  virtual void serviceDied(
      uint64_t /* cookie */,
      const wp<::android::hidl::base::V1_0::IBase> & /* who */) override {
    LOGE("ST HAL service died.");
    onStHalServiceDeath();
  }
};

struct LpmaEnableThreadData {
  pthread_t thread;
  pthread_mutex_t mutex;
  pthread_cond_t cond;
  bool currentLpmaEnabled;
  bool targetLpmaEnabled;
  bool connectedToService;
  sp<StHalDeathRecipient> deathRecipient = new StHalDeathRecipient();
  sp<ISoundTriggerHw> stHalService;
};

static LpmaEnableThreadData lpmaEnableThread;

#endif  // CHRE_DAEMON_LPMA_ENABLED

//! The host ID to use when preloading nanoapps. This is used before the server
//! is started and is sufficiently high enough so as to not collide with any
//! clients after the server starts.
static const uint16_t kHostClientIdDaemon = UINT16_MAX;

//! Contains a set of transaction IDs used to load the preloaded nanoapps.
//! The IDs are stored in the order they are sent.
static std::queue<uint32_t> gPreloadedNanoappPendingTransactionIds;

//! Set to true when we request a graceful shutdown of CHRE
static volatile bool chre_shutdown_requested = false;

#if !defined(LOG_NDEBUG) || LOG_NDEBUG != 0
static void log_buffer(const uint8_t * /*buffer*/, size_t /*size*/) {}
#else
static void log_buffer(const uint8_t *buffer, size_t size) {
  char line[32];
  int offset = 0;
  char line_chars[32];
  int offset_chars = 0;

  size_t orig_size = size;
  if (size > 128) {
    size = 128;
    LOGV("Dumping first 128 bytes of buffer of size %zu", orig_size);
  } else {
    LOGV("Dumping buffer of size %zu bytes", size);
  }
  for (size_t i = 1; i <= size; ++i) {
    offset +=
        snprintf(&line[offset], sizeof(line) - offset, "%02x ", buffer[i - 1]);
    offset_chars +=
        snprintf(&line_chars[offset_chars], sizeof(line_chars) - offset_chars,
                 "%c", (isprint(buffer[i - 1])) ? buffer[i - 1] : '.');
    if ((i % 8) == 0) {
      LOGV("  %s\t%s", line, line_chars);
      offset = 0;
      offset_chars = 0;
    } else if ((i % 4) == 0) {
      offset += snprintf(&line[offset], sizeof(line) - offset, " ");
    }
  }

  if (offset > 0) {
    char tabs[8];
    char *pos = tabs;
    while (offset < 28) {
      *pos++ = '\t';
      offset += 8;
    }
    *pos = '\0';
    LOGV("  %s%s%s", line, tabs, line_chars);
  }
}
#endif

#ifdef CHRE_USE_TOKENIZED_LOGGING
static android_LogPriority chreLogLevelToAndroidLogPriority(uint8_t level) {
  switch (level) {
    case CHRE_LOG_LEVEL_ERROR:
      return ANDROID_LOG_ERROR;
    case CHRE_LOG_LEVEL_WARN:
      return ANDROID_LOG_WARN;
    case CHRE_LOG_LEVEL_INFO:
      return ANDROID_LOG_INFO;
    case CHRE_LOG_LEVEL_DEBUG:
      return ANDROID_LOG_DEBUG;
    default:
      return ANDROID_LOG_SILENT;
  }
}

void emitLogMessage(uint8_t level, uint64_t timestampNanos, const char *log) {
  constexpr const char kLogTag[] = "CHRE";
  constexpr const uint64_t kNanosPerSec = 1e9;
  constexpr const uint64_t kNanosPerMsec = 1e6;

  uint32_t timeSec = timestampNanos / kNanosPerSec;
  timestampNanos -= (timeSec * kNanosPerSec);

  uint32_t timeMsec = timestampNanos / kNanosPerMsec;

  android_LogPriority priority = chreLogLevelToAndroidLogPriority(level);
  LOG_PRI(priority, kLogTag, HUB_LOG_FORMAT_STR, timeSec, timeMsec, log);
}

void parseAndEmitTokenizedLogMessages(unsigned char *message,
                                      unsigned int messageLen,
                                      const Detokenizer *detokenizer) {
  if (detokenizer != nullptr) {
    // TODO: Pull out common code from the tokenized/standard log
    // parser functions when we implement batching for tokenized
    // logs (b/148873804)
    constexpr size_t kLogMessageHeaderSize =
        1 /*logLevel*/ + sizeof(uint64_t) /*timestamp*/;

    const fbs::MessageContainer *container = fbs::GetMessageContainer(message);
    const auto *logMessage =
        static_cast<const fbs::LogMessage *>(container->message());

    const flatbuffers::Vector<int8_t> &logData = *logMessage->buffer();
    const uint8_t *log = reinterpret_cast<const uint8_t *>(logData.data());
    uint8_t level = *log;
    ++log;

    uint64_t timestampNanos;
    memcpy(&timestampNanos, log, sizeof(uint64_t));
    timestampNanos = le64toh(timestampNanos);
    log += sizeof(uint64_t);

    DetokenizedString detokenizedLog =
        detokenizer->Detokenize(log, messageLen - kLogMessageHeaderSize);
    std::string decodedLog = detokenizedLog.BestStringWithErrors();
    emitLogMessage(level, timestampNanos, decodedLog.c_str());
  } else {
    // log an error and risk log spam? fail silently? log once?
  }
}

#endif

static int64_t getTimeOffset(bool *success) {
  int64_t timeOffset = 0;

#if defined(__aarch64__)
  // Reads the system time counter (CNTVCT) and its frequency (CNTFRQ)
  // CNTVCT is used in the sensors HAL for time synchronization.
  // More information can be found in the ARM reference manual
  // (http://infocenter.arm.com/help/index.jsp?topic=
  // /com.arm.doc.100048_0002_05_en/jfa1406793266982.html)
  // Use uint64_t to store since the MRS instruction uses 64 bit (X) registers
  // (http://infocenter.arm.com/help/topic/
  // com.arm.doc.den0024a/ch06s05s02.html)
  uint64_t qTimerCount = 0, qTimerFreq = 0;
  uint64_t hostTimeNano = elapsedRealtimeNano();
  asm volatile("mrs %0, cntvct_el0" : "=r"(qTimerCount));
  asm volatile("mrs %0, cntfrq_el0" : "=r"(qTimerFreq));

  constexpr uint64_t kOneSecondInNanoseconds = 1000000000;
  if (qTimerFreq != 0) {
    // Get the seconds part first, then convert the remainder to prevent
    // overflow
    uint64_t qTimerNanos = (qTimerCount / qTimerFreq);
    if (qTimerNanos > UINT64_MAX / kOneSecondInNanoseconds) {
      LOGE(
          "CNTVCT_EL0 conversion to nanoseconds overflowed during time sync."
          " Aborting time sync.");
      *success = false;
    } else {
      qTimerNanos *= kOneSecondInNanoseconds;

      // Round the remainder portion to the nearest nanosecond
      uint64_t remainder = (qTimerCount % qTimerFreq);
      qTimerNanos +=
          (remainder * kOneSecondInNanoseconds + qTimerFreq / 2) / qTimerFreq;

      timeOffset = hostTimeNano - qTimerNanos;
      *success = true;
    }
  } else {
    LOGE("CNTFRQ_EL0 had 0 value. Aborting time sync.");
    *success = false;
  }
#else
#error "Unsupported CPU architecture type"
#endif

  return timeOffset;
}

/**
 * @param logOnError If true, logs an error message on failure.
 *
 * @return true if the time sync message was successfully sent to CHRE.
 */
static bool sendTimeSyncMessage(bool logOnError) {
  bool timeSyncSuccess = true;
  int64_t timeOffset = getTimeOffset(&timeSyncSuccess);

  if (timeSyncSuccess) {
    flatbuffers::FlatBufferBuilder builder(64);
    HostProtocolHost::encodeTimeSyncMessage(builder, timeOffset);
    int success = chre_slpi_deliver_message_from_host(
        static_cast<const unsigned char *>(builder.GetBufferPointer()),
        static_cast<int>(builder.GetSize()));

    if (success != 0) {
      if (logOnError) {
        LOGE("Failed to deliver time sync message from host to CHRE: %d",
             success);
      }
      timeSyncSuccess = false;
    }
  }

  return timeSyncSuccess;
}

/**
 * Sends a time sync message to CHRE, retrying a specified time until success.
 *
 * @param maxNumRetries The number of times to retry sending the message
 *
 * @return true if the time sync message was successfully sent to CHRE.
 */
static bool sendTimeSyncMessageRetry(size_t maxNumRetries) {
  size_t numRetries = 0;
  useconds_t retryDelayUs = 50000;  // 50 ms initially
  bool success = sendTimeSyncMessage(numRetries == maxNumRetries);
  while (!success && numRetries < maxNumRetries) {
    usleep(retryDelayUs);
    numRetries++;
    retryDelayUs *= 2;
    success = sendTimeSyncMessage(numRetries == maxNumRetries);
  }

  return success;
}

#ifdef CHRE_DAEMON_LPMA_ENABLED

static void acquireWakeLock() {
  if (acquire_wake_lock(PARTIAL_WAKE_LOCK, kWakeLockName) != 0) {
    LOGE("Failed to acquire wakelock");
  }
}

static void releaseWakeLock() {
  static bool initialRelease = true;

  // It's expected to get an error when we first try to release the wakelock
  // as it won't exist unless it was leaked previously - don't output a
  // false warning for this case
  if (release_wake_lock(kWakeLockName) != 0 && !initialRelease) {
    LOGE("Failed to release wakelock");
  }

  initialRelease = false;
}

/**
 * Sets the target state for LPMA to be enabled. This triggers another thread to
 * perform the async operation of enabling or disabling the LPMA use case.
 *
 * @param enabled Whether LPMA is to be enabled or disabled.
 */
static void setLpmaState(bool enabled) {
  pthread_mutex_lock(&lpmaEnableThread.mutex);
  lpmaEnableThread.targetLpmaEnabled = enabled;
  pthread_mutex_unlock(&lpmaEnableThread.mutex);
  pthread_cond_signal(&lpmaEnableThread.cond);
}

static void onStHalServiceDeath() {
  pthread_mutex_lock(&lpmaEnableThread.mutex);
  lpmaEnableThread.connectedToService = false;
  if (lpmaEnableThread.targetLpmaEnabled) {
    // ST HAL has died, so assume that the sound model is no longer active,
    // and trigger a reload of the sound model.
    lpmaEnableThread.currentLpmaEnabled = false;
    pthread_cond_signal(&lpmaEnableThread.cond);
  }
  pthread_mutex_unlock(&lpmaEnableThread.mutex);
}

/**
 * Connects to the ST HAL service, if not already. This method should only
 * be invoked after acquiring the lpmaEnableThread.mutex lock.
 *
 * @return true if successfully connected to the HAL.
 */
static bool connectToStHalServiceLocked() {
  if (!lpmaEnableThread.connectedToService) {
    lpmaEnableThread.stHalService = ISoundTriggerHw::getService();
    if (lpmaEnableThread.stHalService != nullptr) {
      LOGI("Connected to ST HAL service");
      lpmaEnableThread.connectedToService = true;
      lpmaEnableThread.stHalService->linkToDeath(
          lpmaEnableThread.deathRecipient, 0 /* flags */);
    }
  }

  return lpmaEnableThread.connectedToService;
}

/**
 * Loads the LPMA use case via the SoundTrigger HAL HIDL service.
 *
 * @param lpmaHandle The handle that was generated as a result of enabling
 *        the LPMA use case successfully.
 * @return true if LPMA was enabled successfully, false otherwise.
 */
static bool loadLpma(SoundModelHandle *lpmaHandle) {
  LOGD("Loading LPMA");

  ISoundTriggerHw::SoundModel soundModel;
  soundModel.type = SoundModelType::GENERIC;
  soundModel.vendorUuid.timeLow = 0x57CADDB1;
  soundModel.vendorUuid.timeMid = 0xACDB;
  soundModel.vendorUuid.versionAndTimeHigh = 0x4DCE;
  soundModel.vendorUuid.variantAndClockSeqHigh = 0x8CB0;

  const uint8_t uuidNode[6] = {0x2E, 0x95, 0xA2, 0x31, 0x3A, 0xEE};
  memcpy(&soundModel.vendorUuid.node[0], uuidNode, sizeof(uuidNode));
  soundModel.data.resize(1);  // Insert a dummy byte to bypass HAL NULL checks.

  bool loaded = false;
  if (!connectToStHalServiceLocked()) {
    LOGE("Failed to get ST HAL service for LPMA load");
  } else {
    int32_t loadResult;
    Return<void> hidlResult = lpmaEnableThread.stHalService->loadSoundModel(
        soundModel, NULL /* callback */, 0 /* cookie */,
        [&](int32_t retval, SoundModelHandle handle) {
          loadResult = retval;
          *lpmaHandle = handle;
        });

    if (hidlResult.isOk()) {
      if (loadResult == 0) {
        LOGI("Loaded LPMA");
        loaded = true;
      } else {
        LOGE("Failed to load LPMA with %" PRId32, loadResult);
      }
    } else {
      LOGE("Failed to load LPMA due to hidl error %s",
           hidlResult.description().c_str());
    }
  }

  return loaded;
}

/**
 * Unloads the LPMA use case via the SoundTrigger HAL HIDL service. This
 * function does not indicate success/failure as it is expected that even in the
 * event of a failure to unload, the use case will be unloaded. As long as the
 * sound trigger HAL received the request we can be assured that the use case
 * will be unloaded (even if it means reseting the codec or otherwise).
 *
 * @param lpmaHandle A handle that was previously produced by the setLpmaEnabled
 *        function. This is the handle that is unloaded from the ST HAL to
 *        disable LPMA.
 */
static void unloadLpma(SoundModelHandle lpmaHandle) {
  LOGD("Unloading LPMA");

  if (!connectToStHalServiceLocked()) {
    LOGE("Failed to get ST HAL service for LPMA unload");
  } else {
    Return<int32_t> hidlResult =
        lpmaEnableThread.stHalService->unloadSoundModel(lpmaHandle);

    if (hidlResult.isOk()) {
      if (hidlResult == 0) {
        LOGI("Unloaded LPMA");
      } else {
        LOGE("Failed to unload LPMA with %" PRId32, int32_t(hidlResult));
      }
    } else {
      LOGE("Failed to unload LPMA due to hidl error %s",
           hidlResult.description().c_str());
    }
  }
}

static void *chreLpmaEnableThread(void *arg) {
  auto *state = static_cast<LpmaEnableThreadData *>(arg);

  const useconds_t kInitialRetryDelayUs = 500000;
  const int kRetryGrowthFactor = 2;
  const int kRetryGrowthLimit = 5;     // Terminates at 8s retry interval.
  const int kRetryWakeLockLimit = 10;  // Retry with a wakelock 10 times.

  int retryCount = 0;
  useconds_t retryDelay = 0;
  SoundModelHandle lpmaHandle;

  while (true) {
    pthread_mutex_lock(&state->mutex);
    if (state->currentLpmaEnabled == state->targetLpmaEnabled) {
      retryCount = 0;
      retryDelay = 0;
      releaseWakeLock();  // Allow the system to suspend while waiting.
      pthread_cond_wait(&state->cond, &state->mutex);
      acquireWakeLock();  // Ensure the system stays up while retrying.
    } else if (state->targetLpmaEnabled && loadLpma(&lpmaHandle)) {
      state->currentLpmaEnabled = state->targetLpmaEnabled;
    } else if (!state->targetLpmaEnabled) {
      // Regardless of whether the use case fails to unload, set the
      // currentLpmaEnabled to the targetLpmaEnabled. This will allow the next
      // enable request to proceed. After a failure to unload occurs, the
      // supplied handle is invalid and should not be unloaded again.
      unloadLpma(lpmaHandle);
      state->currentLpmaEnabled = state->targetLpmaEnabled;
    } else {
      // Unlock while delaying to avoid blocking the client thread. No shared
      // state is modified here.
      pthread_mutex_unlock(&state->mutex);

      if (retryDelay == 0) {
        retryDelay = kInitialRetryDelayUs;
      } else if (retryCount < kRetryGrowthLimit) {
        retryDelay *= kRetryGrowthFactor;
      }

      LOGD("Delaying retry %d for %uus", retryCount, retryDelay);
      usleep(retryDelay);

      retryCount++;
      if (retryCount > kRetryWakeLockLimit) {
        releaseWakeLock();
      }

      pthread_mutex_lock(&state->mutex);
    }

    pthread_mutex_unlock(&state->mutex);
  }

  LOGV("LPMA enable thread exited");
  return NULL;
}

/**
 * Initializes the data shared with the LPMA enable thread and starts the
 * thread.
 *
 * @param data Pointer to structure containing the (uninitialized) condition
 *        variable and associated data passed to the LPMA enable thread.
 * @return true on success, false otherwise.
 */
static bool initLpmaEnableThread(LpmaEnableThreadData *data) {
  bool success = false;
  int ret;

  if ((ret = pthread_mutex_init(&data->mutex, NULL)) != 0) {
    LOG_ERROR("Failed to initialize lpma enable mutex", ret);
  } else if ((ret = pthread_cond_init(&data->cond, NULL)) != 0) {
    LOG_ERROR("Failed to initialize lpma enable condition variable", ret);
  } else if (!start_thread(&data->thread, chreLpmaEnableThread, data)) {
    LOGE("Couldn't start lpma enable thread");
  } else {
    data->currentLpmaEnabled = false;
    data->targetLpmaEnabled = false;
    success = true;
  }

  return success;
}

#endif  // CHRE_DAEMON_LPMA_ENABLED

/**
 * Sends a message to CHRE.
 *
 * @param clientId The client ID that this message originates from.
 * @param data The data to pass down.
 * @param length The size of the data to send.
 * @return true if successful, false otherwise.
 */
static bool sendMessageToChre(uint16_t clientId, void *data, size_t length) {
  constexpr size_t kMaxPayloadSize = 1024 * 1024;  // 1 MiB

  // This limitation is due to FastRPC, but there's no case where we should come
  // close to this limit...
  static_assert(kMaxPayloadSize <= INT32_MAX,
                "DSP uses 32-bit signed integers to represent message size");

  bool success = false;
  if (length > kMaxPayloadSize) {
    LOGE("Message too large (got %zu, max %zu bytes)", length, kMaxPayloadSize);
  } else if (!HostProtocolHost::mutateHostClientId(data, length, clientId)) {
    LOGE("Couldn't set host client ID in message container!");
  } else {
    LOGV("Delivering message from host (size %zu)", length);
    log_buffer(static_cast<const uint8_t *>(data), length);
    int ret = chre_slpi_deliver_message_from_host(
        static_cast<const unsigned char *>(data), static_cast<int>(length));
    if (ret != 0) {
      LOGE("Failed to deliver message from host to CHRE: %d", ret);
    } else {
      success = true;
    }
  }

  return success;
}

/**
 * Loads a nanoapp by sending the nanoapp filename to the CHRE framework. This
 * method will return after sending the request so no guarantee is made that
 * the nanoapp is loaded until after the response is received.
 *
 * @param appId The ID of the nanoapp to load.
 * @param appVersion The version of the nanoapp to load.
 * @param appTargetApiVersion The version of the CHRE API that the app targets.
 * @param appBinaryName The name of the binary as stored in the filesystem. This
 *     will be used to load the nanoapp into CHRE.
 * @param transactionId The transaction ID to use when loading.
 * @return true if a request was successfully sent, false otherwise.
 */
static bool sendNanoappLoad(uint64_t appId, uint32_t appVersion,
                            uint32_t appTargetApiVersion,
                            const std::string &appBinaryName,
                            uint32_t transactionId) {
  flatbuffers::FlatBufferBuilder builder;
  HostProtocolHost::encodeLoadNanoappRequestForFile(
      builder, transactionId, appId, appVersion, appTargetApiVersion,
      appBinaryName.c_str());

  bool success = sendMessageToChre(
      kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());

  if (!success) {
    LOGE("Failed to send nanoapp filename.");
  } else {
    gPreloadedNanoappPendingTransactionIds.push(transactionId);
  }

  return success;
}

/**
 * Sends a preloaded nanoapp filename / metadata to CHRE.
 *
 * @param header The nanoapp header binary blob.
 * @param nanoappName The filename of the nanoapp to be loaded.
 * @param transactionId The transaction ID to use when loading the app.
 * @return true if successful, false otherwise.
 */
static bool loadNanoapp(const std::vector<uint8_t> &header,
                        const std::string &nanoappName,
                        uint32_t transactionId) {
  // This struct comes from build/build_template.mk and must not be modified.
  // Refer to that file for more details.
  struct NanoAppBinaryHeader {
    uint32_t headerVersion;
    uint32_t magic;
    uint64_t appId;
    uint32_t appVersion;
    uint32_t flags;
    uint64_t hwHubType;
    uint8_t targetChreApiMajorVersion;
    uint8_t targetChreApiMinorVersion;
    uint8_t reserved[6];
  } __attribute__((packed));

  bool success = false;
  if (header.size() != sizeof(NanoAppBinaryHeader)) {
    LOGE("Header size mismatch");
  } else {
    // The header blob contains the struct above.
    const auto *appHeader =
        reinterpret_cast<const NanoAppBinaryHeader *>(header.data());

    // Build the target API version from major and minor.
    uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) |
                                (appHeader->targetChreApiMinorVersion << 16);

    success = sendNanoappLoad(appHeader->appId, appHeader->appVersion,
                              targetApiVersion, nanoappName, transactionId);
  }

  return success;
}

/**
 * Loads the supplied file into the provided buffer.
 *
 * @param filename The name of the file to load.
 * @param buffer The buffer to load into.
 * @return true if successful, false otherwise.
 */
static bool readFileContents(const char *filename,
                             std::vector<uint8_t> *buffer) {
  bool success = false;
  std::ifstream file(filename, std::ios::binary | std::ios::ate);
  if (!file) {
    LOGE("Couldn't open file '%s': %d (%s)", filename, errno, strerror(errno));
  } else {
    ssize_t size = file.tellg();
    file.seekg(0, std::ios::beg);

    buffer->resize(size);
    if (!file.read(reinterpret_cast<char *>(buffer->data()), size)) {
      LOGE("Couldn't read from file '%s': %d (%s)", filename, errno,
           strerror(errno));
    } else {
      success = true;
    }
  }

  return success;
}

/**
 * Loads a preloaded nanoapp given a filename to load from. Allows the
 * transaction to complete before the nanoapp starts so the server can start
 * serving requests as soon as possible.
 *
 * @param directory The directory to load the nanoapp from.
 * @param name The filename of the nanoapp to load.
 * @param transactionId The transaction ID to use when loading the app.
 */
static void loadPreloadedNanoapp(const std::string &directory,
                                 const std::string &name,
                                 uint32_t transactionId) {
  std::vector<uint8_t> headerBuffer;

  std::string headerFile = directory + "/" + name + ".napp_header";

  // Only create the nanoapp filename as the CHRE framework will load from
  // within the directory its own binary resides in.
  std::string nanoappFilename = name + ".so";

  if (readFileContents(headerFile.c_str(), &headerBuffer) &&
      !loadNanoapp(headerBuffer, nanoappFilename, transactionId)) {
    LOGE("Failed to load nanoapp: '%s'", name.c_str());
  }
}

/**
 * Attempts to load all preloaded nanoapps from a config file. The config file
 * is expected to be valid JSON with the following structure:
 *
 * { "nanoapps": [
 *     "/path/to/nanoapp_1",
 *     "/path/to/nanoapp_2"
 * ]}
 *
 * The napp_header and so files will both be loaded. All errors are logged.
 */
static void loadPreloadedNanoapps() {
  constexpr char kPreloadedNanoappsConfigPath[] =
      "/vendor/etc/chre/preloaded_nanoapps.json";
  std::ifstream configFileStream(kPreloadedNanoappsConfigPath);

  Json::Reader reader;
  Json::Value config;
  if (!configFileStream) {
    LOGE("Failed to open config file '%s': %d (%s)",
         kPreloadedNanoappsConfigPath, errno, strerror(errno));
  } else if (!reader.parse(configFileStream, config)) {
    LOGE("Failed to parse nanoapp config file");
  } else if (!config.isMember("nanoapps") || !config.isMember("source_dir")) {
    LOGE("Malformed preloaded nanoapps config");
  } else {
    const Json::Value &directory = config["source_dir"];
    for (Json::ArrayIndex i = 0; i < config["nanoapps"].size(); i++) {
      const Json::Value &nanoapp = config["nanoapps"][i];
      loadPreloadedNanoapp(directory.asString(), nanoapp.asString(),
                           static_cast<uint32_t>(i));
    }
  }
}

/**
 * Handles a message that is directed towards the daemon.
 *
 * @param message The message sent to the daemon.
 */
static void handleDaemonMessage(const uint8_t *message) {
  std::unique_ptr<fbs::MessageContainerT> container =
      fbs::UnPackMessageContainer(message);
  if (container->message.type != fbs::ChreMessage::LoadNanoappResponse) {
    LOGE("Invalid message from CHRE directed to daemon");
  } else {
    const auto *response = container->message.AsLoadNanoappResponse();
    if (gPreloadedNanoappPendingTransactionIds.empty()) {
      LOGE("Received nanoapp load response with no pending load");
    } else if (gPreloadedNanoappPendingTransactionIds.front() !=
               response->transaction_id) {
      LOGE("Received nanoapp load response with ID %" PRIu32
           " expected transaction id %" PRIu32,
           response->transaction_id,
           gPreloadedNanoappPendingTransactionIds.front());
    } else {
      if (!response->success) {
        LOGE("Received unsuccessful nanoapp load response with ID %" PRIu32,
             gPreloadedNanoappPendingTransactionIds.front());
      }
      gPreloadedNanoappPendingTransactionIds.pop();
    }
  }
}

#ifdef CHRE_USE_TOKENIZED_LOGGING
/**
 * Initialize the Log Detokenizer
 *
 * The log detokenizer reads a binary database file that contains key value
 * pairs of hash-keys <--> Decoded log messages, and creates an instance
 * of the Detokenizer.
 *
 * @return an instance of the Detokenizer
 */
static std::unique_ptr<Detokenizer> logDetokenizerInit() {
  constexpr const char kLogDatabaseFilePath[] =
      "/vendor/etc/chre/libchre_log_database.bin";
  std::vector<uint8_t> tokenData;
  if (readFileContents(kLogDatabaseFilePath, &tokenData)) {
    pw::tokenizer::TokenDatabase database =
        pw::tokenizer::TokenDatabase::Create(tokenData);
    if (database.ok()) {
      return std::make_unique<Detokenizer>(database);
    } else {
      LOGE("CHRE Token database creation not OK");
    }
  } else {
    LOGE("Failed to read CHRE Token database file");
  }
  return std::unique_ptr<Detokenizer>(nullptr);
}
#endif

/**
 * Entry point for the thread that receives messages sent by CHRE.
 *
 * @return always returns NULL
 */
static void *chre_message_to_host_thread(void *arg) {
  unsigned char messageBuffer[4096];
  unsigned int messageLen;
  int result = 0;
  auto *server = static_cast<::android::chre::SocketServer *>(arg);

#ifdef CHRE_USE_TOKENIZED_LOGGING
  std::unique_ptr<Detokenizer> detokenizer = logDetokenizerInit();
#endif

  while (true) {
    messageLen = 0;
    LOGV("Calling into chre_slpi_get_message_to_host");
    result = chre_slpi_get_message_to_host(messageBuffer, sizeof(messageBuffer),
                                           &messageLen);
    LOGV("Got message from CHRE with size %u (result %d)", messageLen, result);

    if (result == CHRE_FASTRPC_ERROR_SHUTTING_DOWN) {
      LOGD("CHRE shutting down, exiting CHRE->Host message thread");
      break;
    } else if (result == CHRE_FASTRPC_SUCCESS && messageLen > 0) {
      log_buffer(messageBuffer, messageLen);
      uint16_t hostClientId;
      fbs::ChreMessage messageType;
      if (!HostProtocolHost::extractHostClientIdAndType(
              messageBuffer, messageLen, &hostClientId, &messageType)) {
        LOGW(
            "Failed to extract host client ID from message - sending "
            "broadcast");
        hostClientId = chre::kHostClientIdUnspecified;
      }

      if (messageType == fbs::ChreMessage::LogMessage) {
        // Log messages are routed through ashLog if tokenized logging
        // is disabled, so only parse tokenized log messages here.
#ifdef CHRE_USE_TOKENIZED_LOGGING
        parseAndEmitTokenizedLogMessages(messageBuffer, messageLen,
                                         detokenizer.get());
#endif
      } else if (messageType == fbs::ChreMessage::TimeSyncRequest) {
        sendTimeSyncMessage(true /* logOnError */);
#ifdef CHRE_DAEMON_LPMA_ENABLED
      } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRequest) {
        setLpmaState(true);
      } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRelease) {
        setLpmaState(false);
#endif  // CHRE_DAEMON_LPMA_ENABLED
      } else if (hostClientId == kHostClientIdDaemon) {
        handleDaemonMessage(messageBuffer);
      } else if (hostClientId == chre::kHostClientIdUnspecified) {
        server->sendToAllClients(messageBuffer,
                                 static_cast<size_t>(messageLen));
      } else {
        server->sendToClientById(messageBuffer, static_cast<size_t>(messageLen),
                                 hostClientId);
      }
    } else if (!chre_shutdown_requested) {
      LOGE(
          "Received an unknown result (%d) and no shutdown was requested. "
          "Quitting",
          result);
      exit(-1);
    } else {
      // Received an unknown result but a shutdown was requested. Break from the
      // loop to allow the daemon to cleanup.
      break;
    }
  }

  LOGV("Message to host thread exited");
  return NULL;
}

/**
 * Entry point for the thread that blocks in a FastRPC call to monitor for
 * abnormal exit of CHRE or reboot of the DSP.
 *
 * @return always returns NULL
 */
static void *chre_monitor_thread(void *arg) {
  (void)arg;
  int ret = chre_slpi_wait_on_thread_exit();
  if (!chre_shutdown_requested) {
    LOGE("Detected unexpected CHRE thread exit (%d)\n", ret);
    exit(EXIT_FAILURE);
  }

  LOGV("Monitor thread exited");
  return NULL;
}

/**
 * Start a thread with default attributes, or log an error on failure
 *
 * @return bool true if the thread was successfully started
 */
static bool start_thread(pthread_t *thread_handle,
                         thread_entry_point_f *thread_entry, void *arg) {
  int ret = pthread_create(thread_handle, NULL, thread_entry, arg);
  if (ret != 0) {
    LOG_ERROR("pthread_create failed", ret);
  }
  return (ret == 0);
}

namespace {

void onMessageReceivedFromClient(uint16_t clientId, void *data, size_t length) {
  sendMessageToChre(clientId, data, length);
}

}  // anonymous namespace

int main() {
  int ret = -1;
  pthread_t monitor_thread;
  pthread_t msg_to_host_thread;

  ::android::chre::SocketServer server;

#ifdef CHRE_DAEMON_LOAD_INTO_SENSORSPD
  remote_handle remote_handle_fd = 0xFFFFFFFF;
  if (remote_handle_open(ITRANSPORT_PREFIX "createstaticpd:sensorspd",
                         &remote_handle_fd)) {
    LOGE("Failed to open remote handle for sensorspd");
  } else {
    LOGV("Successfully opened remote handle for sensorspd");
  }
#endif  // CHRE_DAEMON_LOAD_INTO_SENSORSPD

  // Send time sync message before nanoapps start, retrying a few times
  // in case the SLPI is not ready yet. This retry logic must be placed before
  // any of the other FastRPC method invocations.
  constexpr size_t kMaxNumRetries = 5;
  if (!sendTimeSyncMessageRetry(kMaxNumRetries)) {
    LOGE("Failed to send initial time sync message");
    // Reverse monitor invokes a FastRPC method to allow the code running in
    // CHRE to detect abnormal shutdown of the host-side daemon and perform a
    // gracefull cleanup.
    // ToDo: consolidate the chre_slpi_initialize_reverse_monitor() logic into
    // an always-running daemon thread to eliminate the FastRPC call and save
    // space in the CHRE framework.
  } else if ((ret = chre_slpi_initialize_reverse_monitor()) !=
             CHRE_FASTRPC_SUCCESS) {
    LOGE("Failed to initialize reverse monitor: %d", ret);
#ifdef CHRE_DAEMON_LPMA_ENABLED
  } else if (!initLpmaEnableThread(&lpmaEnableThread)) {
    LOGE("Couldn't initialize LPMA enable thread");
#endif  // CHRE_DAEMON_LPMA_ENABLED
  } else {
    if ((ret = chre_slpi_start_thread()) != CHRE_FASTRPC_SUCCESS) {
      LOGE("Failed to start CHRE: %d", ret);
    } else {
      if (!start_thread(&monitor_thread, chre_monitor_thread, NULL)) {
        LOGE("Couldn't start monitor thread");
      } else if (!start_thread(&msg_to_host_thread, chre_message_to_host_thread,
                               &server)) {
        LOGE("Couldn't start CHRE->Host message thread");
      } else {
        LOGI("CHRE started");
        loadPreloadedNanoapps();

        // TODO: take 2nd argument as command-line parameter
        server.run("chre", true, onMessageReceivedFromClient);
      }

      chre_shutdown_requested = true;
      ret = chre_slpi_stop_thread();
      if (ret != CHRE_FASTRPC_SUCCESS) {
        LOGE("Failed to stop CHRE: %d", ret);
      } else {
        // TODO: don't call pthread_join if the thread failed to start
        LOGV("Joining monitor thread");
        ret = pthread_join(monitor_thread, NULL);
        if (ret != 0) {
          LOG_ERROR("Join on monitor thread failed", ret);
        }

        LOGV("Joining message to host thread");
        ret = pthread_join(msg_to_host_thread, NULL);
        if (ret != 0) {
          LOG_ERROR("Join on monitor thread failed", ret);
        }

        LOGI("Shutdown complete");
      }
    }
  }

  return ret;
}