1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <chre/util/nanoapp/app_id.h>
17 #include <chre_host/host_protocol_host.h>
18 #include <chre_host/socket_client.h>
19 #include <pixelstats/DropDetect.h>
20 #include <pixelstats/StatsHelper.h>
21 #include <pixelstatsatoms.h>
22 
23 #define LOG_TAG "pixelstats-vendor"
24 #include <log/log.h>
25 
26 #include <inttypes.h>
27 #include <math.h>
28 
29 using aidl::android::frameworks::stats::IStats;
30 using aidl::android::frameworks::stats::VendorAtom;
31 using aidl::android::frameworks::stats::VendorAtomValue;
32 using android::sp;
33 using android::chre::HostProtocolHost;
34 using android::chre::IChreMessageHandlers;
35 using android::chre::SocketClient;
36 using android::hardware::google::pixel::PixelAtoms::VENDOR_PHYSICAL_DROP_DETECTED;
37 
38 // following convention of CHRE code.
39 namespace fbs = ::chre::fbs;
40 
41 namespace android {
42 namespace hardware {
43 namespace google {
44 namespace pixel {
45 
46 namespace {  // anonymous namespace for file-local definitions
47 
48 // The following two structs are defined in nanoapps/drop/messaging.h
49 // by the DropDetect nanoapp.
50 struct __attribute__((__packed__)) DropEventPayload {
51     float confidence;
52     float accel_magnitude_peak;
53     int32_t free_fall_duration_ns;
54 };
55 
56 struct __attribute__((__packed__)) DropEventPayloadV2 {
57     uint64_t free_fall_duration_ns;
58     float impact_accel_x;
59     float impact_accel_y;
60     float impact_accel_z;
61 };
62 
63 // This enum is defined in nanoapps/drop/messaging.h
64 // by the DropDetect nanoapp.
65 enum DropConstants {
66     kDropEnableRequest = 1,
67     kDropEnableNotification = 2,
68     kDropDisableRequest = 3,
69     kDropDisableNotification = 4,
70     kDropEventDetection = 5,
71     kDropEventDetectionV2 = 6,
72 };
73 
requestNanoappList(SocketClient * client)74 void requestNanoappList(SocketClient *client) {
75     if (client != nullptr) {
76         flatbuffers::FlatBufferBuilder builder(64);
77         HostProtocolHost::encodeNanoappListRequest(builder);
78 
79         if (!client->sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
80             ALOGE("Failed to send NanoappList request");
81         }
82     }
83 }
84 
85 }  // namespace
86 
DropDetect(const uint64_t drop_detect_app_id)87 DropDetect::DropDetect(const uint64_t drop_detect_app_id) : kDropDetectAppId(drop_detect_app_id) {}
88 
start(const uint64_t drop_detect_app_id,const char * const chre_socket)89 sp<DropDetect> DropDetect::start(const uint64_t drop_detect_app_id, const char *const chre_socket) {
90     sp<DropDetect> dropDetect = new DropDetect(drop_detect_app_id);
91     if (!dropDetect->connectInBackground(chre_socket, dropDetect)) {
92         ALOGE("Couldn't connect to CHRE socket");
93         return nullptr;
94     }
95     return dropDetect;
96 }
97 
onConnected()98 void DropDetect::onConnected() {
99     requestNanoappList(this);
100 }
101 
102 /**
103  * Decode unix socket msgs to CHRE messages, and call the appropriate
104  * callback depending on the CHRE message.
105  */
onMessageReceived(const void * data,size_t length)106 void DropDetect::onMessageReceived(const void *data, size_t length) {
107     if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
108         ALOGE("Failed to decode message");
109     }
110 }
111 
112 /**
113  * Handle the response of a NanoappList request.
114  * Ensure that the Drop Detect nanoapp is running.
115  */
handleNanoappListResponse(const fbs::NanoappListResponseT & response)116 void DropDetect::handleNanoappListResponse(const fbs::NanoappListResponseT &response) {
117     for (const std::unique_ptr<fbs::NanoappListEntryT> &nanoapp : response.nanoapps) {
118         if (nanoapp->app_id == kDropDetectAppId) {
119             if (!nanoapp->enabled)
120                 ALOGE("Drop Detect app not enabled");
121             else
122                 ALOGI("Drop Detect enabled");
123             return;
124         }
125     }
126     ALOGE("Drop Detect app not found");
127 }
128 
dropEventFromNanoappPayload(const struct DropEventPayload * p)129 static VendorAtom dropEventFromNanoappPayload(const struct DropEventPayload *p) {
130     ALOGI("Received drop detect message! Confidence %f Peak %f Duration %g",
131           p->confidence, p->accel_magnitude_peak, p->free_fall_duration_ns / 1e9);
132 
133     uint8_t confidence = p->confidence * 100;
134     confidence = std::min<int>(confidence, 100);
135     confidence = std::max<int>(0, confidence);
136     int32_t accel_magnitude_peak_1000ths_g = p->accel_magnitude_peak * 1000.0;
137     int32_t free_fall_duration_ms = p->free_fall_duration_ns / 1000000;
138 
139     return createVendorAtom(VENDOR_PHYSICAL_DROP_DETECTED, "", confidence,
140                             accel_magnitude_peak_1000ths_g, free_fall_duration_ms);
141 }
142 
dropEventFromNanoappPayload(const struct DropEventPayloadV2 * p)143 static VendorAtom dropEventFromNanoappPayload(const struct DropEventPayloadV2 *p) {
144     ALOGI("Received drop detect message: "
145           "duration %g ms, impact acceleration: x = %f, y = %f, z = %f",
146           p->free_fall_duration_ns / 1e6,
147           p->impact_accel_x,
148           p->impact_accel_y,
149           p->impact_accel_z);
150 
151     float impact_magnitude = sqrt(p->impact_accel_x * p->impact_accel_x +
152                                   p->impact_accel_y * p->impact_accel_y +
153                                   p->impact_accel_z * p->impact_accel_z);
154     /* Scale impact magnitude as percentage between [50, 100] m/s2. */
155     constexpr float min_confidence_magnitude = 50;
156     constexpr float max_confidence_magnitude = 100;
157     uint8_t confidence_percentage =
158         impact_magnitude < min_confidence_magnitude ? 0 :
159         impact_magnitude > max_confidence_magnitude ? 100 :
160             (impact_magnitude - min_confidence_magnitude) /
161             (max_confidence_magnitude - min_confidence_magnitude) * 100;
162 
163     const int32_t accel_peak_thousandths_g = static_cast<int32_t>(impact_magnitude * 1000);
164     const int32_t free_fall_duration_ms = static_cast<int32_t>(p->free_fall_duration_ns / 1000000);
165 
166     return createVendorAtom(VENDOR_PHYSICAL_DROP_DETECTED, "", confidence_percentage,
167                             accel_peak_thousandths_g, free_fall_duration_ms);
168 }
169 
reportDropEventToStatsd(const VendorAtom & atom)170 static void reportDropEventToStatsd(const VendorAtom &atom) {
171     const std::shared_ptr<IStats> stats_client = getStatsService();
172     if (!stats_client) {
173         ALOGE("Unable to get AIDL Stats service");
174         return;
175     }
176 
177     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(atom);
178     if (!ret.isOk()) {
179         ALOGE("Unable to report VENDOR_PHYSICAL_DROP_DETECTED to Stats service");
180     }
181 }
182 
183 /**
184  * listen for messages from the DropDetect nanoapp and report them to
185  * PixelStats.
186  */
handleNanoappMessage(const fbs::NanoappMessageT & message)187 void DropDetect::handleNanoappMessage(const fbs::NanoappMessageT &message) {
188     if (message.app_id != kDropDetectAppId)
189         return;
190 
191     if (message.message_type == kDropEventDetection &&
192         message.message.size() >= sizeof(struct DropEventPayload)) {
193         reportDropEventToStatsd(dropEventFromNanoappPayload(
194             reinterpret_cast<const struct DropEventPayload *>(&message.message[0])));
195     } else if (message.message_type == kDropEventDetectionV2 &&
196                message.message.size() >= sizeof(struct DropEventPayloadV2)) {
197         reportDropEventToStatsd(dropEventFromNanoappPayload(
198             reinterpret_cast<const struct DropEventPayloadV2 *>(&message.message[0])));
199     }
200 }
201 
202 }  // namespace pixel
203 }  // namespace google
204 }  // namespace hardware
205 }  // namespace android
206