blob: f48d8d8d4614fdb1ec237f0a12b3527dc997f234 [file] [log] [blame]
Max Dashoukff9ffbc2021-02-16 11:36:39 -08001/*
2 * Copyright (C) 2021 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 */
16package com.android.car.telemetry;
17
Rui Qiuf721b132021-09-10 12:41:09 -070018import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_NONE;
Rui Qiu9b9341c2021-08-25 10:39:04 -070019import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_PARSE_FAILED;
Rui Qiuf721b132021-09-10 12:41:09 -070020import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_UNKNOWN;
Rui Qiu9bc1bb92021-04-08 15:41:48 -070021
Rui Qiuf6668202021-04-08 15:37:20 -070022import android.annotation.NonNull;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070023import android.app.StatsManager;
Rui Qiuf6668202021-04-08 15:37:20 -070024import android.car.Car;
25import android.car.telemetry.ICarTelemetryService;
26import android.car.telemetry.ICarTelemetryServiceListener;
Rui Qiu9b9341c2021-08-25 10:39:04 -070027import android.car.telemetry.MetricsConfigKey;
Rui Qiuf6668202021-04-08 15:37:20 -070028import android.content.Context;
Rui Qiua3418982021-08-20 11:21:12 -070029import android.os.Handler;
30import android.os.HandlerThread;
Rui Qiud5b4b0b2021-09-14 15:51:50 -070031import android.os.PersistableBundle;
Rui Qiu9b9341c2021-08-25 10:39:04 -070032import android.os.RemoteException;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080033import android.util.IndentingPrintWriter;
Rui Qiuf6668202021-04-08 15:37:20 -070034import android.util.Slog;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080035
Rui Qiua3418982021-08-20 11:21:12 -070036import com.android.car.CarLocalServices;
Rui Qiu9b9341c2021-08-25 10:39:04 -070037import com.android.car.CarLog;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070038import com.android.car.CarPropertyService;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080039import com.android.car.CarServiceBase;
Rui Qiua3418982021-08-20 11:21:12 -070040import com.android.car.CarServiceUtils;
41import com.android.car.systeminterface.SystemInterface;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070042import com.android.car.telemetry.databroker.DataBroker;
43import com.android.car.telemetry.databroker.DataBrokerController;
44import com.android.car.telemetry.databroker.DataBrokerImpl;
45import com.android.car.telemetry.publisher.PublisherFactory;
46import com.android.car.telemetry.publisher.StatsManagerImpl;
47import com.android.car.telemetry.publisher.StatsManagerProxy;
48import com.android.car.telemetry.systemmonitor.SystemMonitor;
Rui Qiu9b9341c2021-08-25 10:39:04 -070049import com.android.internal.annotations.VisibleForTesting;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080050
Rui Qiu8c415de2021-05-03 15:52:16 -070051import com.google.protobuf.InvalidProtocolBufferException;
52
Rui Qiud5b4b0b2021-09-14 15:51:50 -070053import java.io.ByteArrayOutputStream;
Rui Qiua3418982021-08-20 11:21:12 -070054import java.io.File;
Rui Qiud5b4b0b2021-09-14 15:51:50 -070055import java.io.IOException;
Rui Qiu8c415de2021-05-03 15:52:16 -070056
Max Dashoukff9ffbc2021-02-16 11:36:39 -080057/**
58 * CarTelemetryService manages OEM telemetry collection, processing and communication
59 * with a data upload service.
60 */
Rui Qiuf6668202021-04-08 15:37:20 -070061public class CarTelemetryService extends ICarTelemetryService.Stub implements CarServiceBase {
Max Dashoukff9ffbc2021-02-16 11:36:39 -080062
Rui Qiuf6668202021-04-08 15:37:20 -070063 private static final boolean DEBUG = false;
Rui Qiua3418982021-08-20 11:21:12 -070064 public static final String TELEMETRY_DIR = "telemetry";
Rui Qiuf6668202021-04-08 15:37:20 -070065
66 private final Context mContext;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070067 private final CarPropertyService mCarPropertyService;
Rui Qiua3418982021-08-20 11:21:12 -070068 private final HandlerThread mTelemetryThread = CarServiceUtils.getHandlerThread(
69 CarTelemetryService.class.getSimpleName());
70 private final Handler mTelemetryHandler = new Handler(mTelemetryThread.getLooper());
Rui Qiuf6668202021-04-08 15:37:20 -070071
72 private ICarTelemetryServiceListener mListener;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070073 private DataBroker mDataBroker;
74 private DataBrokerController mDataBrokerController;
Rui Qiua3418982021-08-20 11:21:12 -070075 private MetricsConfigStore mMetricsConfigStore;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070076 private PublisherFactory mPublisherFactory;
77 private ResultStore mResultStore;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070078 private StatsManagerProxy mStatsManagerProxy;
79 private SystemMonitor mSystemMonitor;
Rui Qiuf6668202021-04-08 15:37:20 -070080
Rui Qiuac8b5ba2021-08-25 16:04:17 -070081 public CarTelemetryService(Context context, CarPropertyService carPropertyService) {
Rui Qiuf6668202021-04-08 15:37:20 -070082 mContext = context;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070083 mCarPropertyService = carPropertyService;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080084 }
85
86 @Override
87 public void init() {
Rui Qiua3418982021-08-20 11:21:12 -070088 mTelemetryHandler.post(() -> {
Rui Qiu3d3806b2021-09-22 11:42:45 -070089 SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class);
90 // full root directory path is /data/system/car/telemetry
91 File rootDirectory = new File(systemInterface.getSystemCarDir(), TELEMETRY_DIR);
Rui Qiuac8b5ba2021-08-25 16:04:17 -070092 // initialize all necessary components
Rui Qiu3d3806b2021-09-22 11:42:45 -070093 mMetricsConfigStore = new MetricsConfigStore(rootDirectory);
94 mResultStore = new ResultStore(rootDirectory);
Rui Qiuac8b5ba2021-08-25 16:04:17 -070095 mStatsManagerProxy = new StatsManagerImpl(
96 mContext.getSystemService(StatsManager.class));
Rui Qiuac8b5ba2021-08-25 16:04:17 -070097 mPublisherFactory = new PublisherFactory(mCarPropertyService, mTelemetryHandler,
Rui Qiu3d3806b2021-09-22 11:42:45 -070098 mStatsManagerProxy, rootDirectory);
Rui Qiuac8b5ba2021-08-25 16:04:17 -070099 mDataBroker = new DataBrokerImpl(mContext, mPublisherFactory, mResultStore);
100 mSystemMonitor = SystemMonitor.create(mContext, mTelemetryHandler);
Rui Qiu3d3806b2021-09-22 11:42:45 -0700101 // controller starts metrics collection after boot complete
102 mDataBrokerController = new DataBrokerController(mDataBroker, mTelemetryHandler,
103 mMetricsConfigStore, mSystemMonitor,
104 systemInterface.getSystemStateInterface());
Rui Qiua3418982021-08-20 11:21:12 -0700105 });
Max Dashoukff9ffbc2021-02-16 11:36:39 -0800106 }
107
108 @Override
109 public void release() {
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700110 // TODO(b/197969149): prevent threading issue, block main thread
111 mTelemetryHandler.post(() -> mResultStore.flushToDisk());
Max Dashoukff9ffbc2021-02-16 11:36:39 -0800112 }
113
114 @Override
115 public void dump(IndentingPrintWriter writer) {
116 writer.println("Car Telemetry service");
117 }
Rui Qiuf6668202021-04-08 15:37:20 -0700118
119 /**
120 * Registers a listener with CarTelemetryService for the service to send data to cloud app.
121 */
122 @Override
123 public void setListener(@NonNull ICarTelemetryServiceListener listener) {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700124 // TODO(b/184890506): verify that only a hardcoded app can set the listener
Rui Qiuf6668202021-04-08 15:37:20 -0700125 mContext.enforceCallingOrSelfPermission(
126 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
Rui Qiu9b9341c2021-08-25 10:39:04 -0700127 mTelemetryHandler.post(() -> {
128 if (DEBUG) {
129 Slog.d(CarLog.TAG_TELEMETRY, "Setting the listener for car telemetry service");
130 }
131 mListener = listener;
132 });
Rui Qiuf6668202021-04-08 15:37:20 -0700133 }
134
135 /**
136 * Clears the listener registered with CarTelemetryService.
137 */
138 @Override
139 public void clearListener() {
140 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700141 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "clearListener");
142 mTelemetryHandler.post(() -> {
143 if (DEBUG) {
144 Slog.d(CarLog.TAG_TELEMETRY, "Clearing the listener for car telemetry service");
145 }
146 mListener = null;
147 });
Rui Qiuf6668202021-04-08 15:37:20 -0700148 }
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700149
150 /**
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700151 * Send a telemetry metrics config to the service. This method assumes
152 * {@link #setListener(ICarTelemetryServiceListener)} is called. Otherwise it does nothing.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700153 *
Rui Qiu9b9341c2021-08-25 10:39:04 -0700154 * @param key the unique key to identify the MetricsConfig.
155 * @param config the serialized bytes of a MetricsConfig object.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700156 */
157 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700158 public void addMetricsConfig(@NonNull MetricsConfigKey key, @NonNull byte[] config) {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700159 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700160 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "addMetricsConfig");
Rui Qiu9b9341c2021-08-25 10:39:04 -0700161 mTelemetryHandler.post(() -> {
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700162 if (mListener == null) {
163 Slog.w(CarLog.TAG_TELEMETRY, "ICarTelemetryServiceListener is not set");
164 return;
165 }
Rui Qiuf721b132021-09-10 12:41:09 -0700166 Slog.d(CarLog.TAG_TELEMETRY, "Adding metrics config " + key.getName()
167 + " to car telemetry service");
Rui Qiuf721b132021-09-10 12:41:09 -0700168 TelemetryProto.MetricsConfig metricsConfig = null;
169 int status = ERROR_METRICS_CONFIG_UNKNOWN;
Rui Qiu9b9341c2021-08-25 10:39:04 -0700170 try {
171 metricsConfig = TelemetryProto.MetricsConfig.parseFrom(config);
Rui Qiu9b9341c2021-08-25 10:39:04 -0700172 } catch (InvalidProtocolBufferException e) {
173 Slog.e(CarLog.TAG_TELEMETRY, "Failed to parse MetricsConfig.", e);
174 status = ERROR_METRICS_CONFIG_PARSE_FAILED;
175 }
Rui Qiuf721b132021-09-10 12:41:09 -0700176 // if config can be parsed, add it to persistent storage
177 if (metricsConfig != null) {
178 status = mMetricsConfigStore.addMetricsConfig(metricsConfig);
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700179 // TODO(b/199410900): update logic once metrics configs have expiration dates
180 mDataBroker.addMetricsConfiguration(metricsConfig);
Rui Qiuf721b132021-09-10 12:41:09 -0700181 }
182 // If no error (a config is successfully added), script results from an older version
183 // should be deleted
184 if (status == ERROR_METRICS_CONFIG_NONE) {
185 mResultStore.deleteResult(key.getName());
186 }
Rui Qiu9b9341c2021-08-25 10:39:04 -0700187 try {
188 mListener.onAddMetricsConfigStatus(key, status);
189 } catch (RemoteException e) {
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700190 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
Rui Qiu9b9341c2021-08-25 10:39:04 -0700191 }
192 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700193 }
194
195 /**
Rui Qiuf721b132021-09-10 12:41:09 -0700196 * Removes a metrics config based on the key. This will also remove outputs produced by the
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700197 * MetricsConfig. This method assumes {@link #setListener(ICarTelemetryServiceListener)} is
198 * called. Otherwise it does nothing.
199 *
200 * @param key the unique identifier of a MetricsConfig.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700201 */
202 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700203 public void removeMetricsConfig(@NonNull MetricsConfigKey key) {
Rui Qiu9b9341c2021-08-25 10:39:04 -0700204 mTelemetryHandler.post(() -> {
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700205 if (mListener == null) {
206 Slog.w(CarLog.TAG_TELEMETRY, "ICarTelemetryServiceListener is not set");
207 return;
208 }
Rui Qiuf721b132021-09-10 12:41:09 -0700209 Slog.d(CarLog.TAG_TELEMETRY, "Removing metrics config " + key.getName()
210 + " from car telemetry service");
Rui Qiu9b9341c2021-08-25 10:39:04 -0700211 // TODO(b/198792767): Check both config name and config version for deletion
Rui Qiuf721b132021-09-10 12:41:09 -0700212 // TODO(b/199540952): Stop and remove config from data broker
213 mResultStore.deleteResult(key.getName()); // delete the config's script results
Rui Qiu9b9341c2021-08-25 10:39:04 -0700214 boolean success = mMetricsConfigStore.deleteMetricsConfig(key.getName());
215 try {
216 mListener.onRemoveMetricsConfigStatus(key, success);
217 } catch (RemoteException e) {
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700218 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
Rui Qiu9b9341c2021-08-25 10:39:04 -0700219 }
220 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700221 }
222
223 /**
Rui Qiuf721b132021-09-10 12:41:09 -0700224 * Removes all MetricsConfigs. This will also remove all MetricsConfig outputs.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700225 */
226 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700227 public void removeAllMetricsConfigs() {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700228 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700229 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "removeAllMetricsConfig");
230 mTelemetryHandler.post(() -> {
Rui Qiuf721b132021-09-10 12:41:09 -0700231 // TODO(b/199540952): Stop and remove all configs from DataBroker
232 Slog.d(CarLog.TAG_TELEMETRY,
233 "Removing all metrics config from car telemetry service");
234 mMetricsConfigStore.deleteAllMetricsConfigs();
235 mResultStore.deleteAllResults();
Rui Qiu9b9341c2021-08-25 10:39:04 -0700236 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700237 }
238
239 /**
240 * Sends script results associated with the given key using the
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700241 * {@link ICarTelemetryServiceListener}. This method assumes listener is set. Otherwise it
242 * does nothing.
243 *
244 * @param key the unique identifier of a MetricsConfig.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700245 */
246 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700247 public void sendFinishedReports(@NonNull MetricsConfigKey key) {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700248 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700249 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "sendFinishedReports");
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700250 mTelemetryHandler.post(() -> {
251 if (mListener == null) {
252 Slog.w(CarLog.TAG_TELEMETRY, "ICarTelemetryServiceListener is not set");
253 return;
254 }
255 if (DEBUG) {
256 Slog.d(CarLog.TAG_TELEMETRY,
257 "Flushing reports for metrics config " + key.getName());
258 }
259 PersistableBundle result = mResultStore.getFinalResult(key.getName(), true);
260 TelemetryProto.TelemetryError error = mResultStore.getError(key.getName(), true);
261 if (result != null) {
262 sendFinalResult(key, result);
263 } else if (error != null) {
264 sendError(key, error);
265 } else {
266 Slog.w(CarLog.TAG_TELEMETRY, "config " + key.getName()
267 + " did not produce any results");
268 }
269 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700270 }
271
272 /**
Rui Qiu9b9341c2021-08-25 10:39:04 -0700273 * Sends all script results or errors using the {@link ICarTelemetryServiceListener}.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700274 */
275 @Override
276 public void sendAllFinishedReports() {
277 // TODO(b/184087869): Implement
278 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700279 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "sendAllFinishedReports");
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700280 if (DEBUG) {
Rui Qiu9b9341c2021-08-25 10:39:04 -0700281 Slog.d(CarLog.TAG_TELEMETRY, "Flushing all reports");
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700282 }
283 }
284
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700285 private void sendFinalResult(MetricsConfigKey key, PersistableBundle result) {
286 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
287 result.writeToStream(bos);
288 mListener.onResult(key, bos.toByteArray());
289 } catch (RemoteException e) {
290 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
291 } catch (IOException e) {
292 Slog.w(CarLog.TAG_TELEMETRY, "failed to write bundle to output stream", e);
293 }
294 }
295
296 private void sendError(MetricsConfigKey key, TelemetryProto.TelemetryError error) {
297 try {
298 mListener.onError(key, error.toByteArray());
299 } catch (RemoteException e) {
300 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
301 }
302 }
303
Rui Qiu9b9341c2021-08-25 10:39:04 -0700304 @VisibleForTesting
305 Handler getTelemetryHandler() {
306 return mTelemetryHandler;
Rui Qiuebfbcfb2021-05-18 17:01:27 -0700307 }
Rui Qiuf721b132021-09-10 12:41:09 -0700308
309 @VisibleForTesting
310 ResultStore getResultStore() {
311 return mResultStore;
312 }
313
314 @VisibleForTesting
315 MetricsConfigStore getMetricsConfigStore() {
316 return mMetricsConfigStore;
317 }
Max Dashoukff9ffbc2021-02-16 11:36:39 -0800318}