blob: 6f5ca1a2be49d07fac0421c6cdfd9a549ca0001d [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) {
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700185 mResultStore.removeResult(key.getName());
Rui Qiuf721b132021-09-10 12:41:09 -0700186 }
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 Qiu9f80c7c2021-09-28 15:15:36 -0700197 * MetricsConfig.
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700198 *
199 * @param key the unique identifier of a MetricsConfig.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700200 */
201 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700202 public void removeMetricsConfig(@NonNull MetricsConfigKey key) {
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700203 mContext.enforceCallingOrSelfPermission(
204 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "removeMetricsConfig");
Rui Qiu9b9341c2021-08-25 10:39:04 -0700205 mTelemetryHandler.post(() -> {
Rui Qiuf721b132021-09-10 12:41:09 -0700206 Slog.d(CarLog.TAG_TELEMETRY, "Removing metrics config " + key.getName()
207 + " from car telemetry service");
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700208 // TODO(b/198792767): Check both config name and config version for removal
209 mDataBroker.removeMetricsConfiguration(key.getName());
210 mResultStore.removeResult(key.getName());
211 mMetricsConfigStore.removeMetricsConfig(key.getName());
Rui Qiu9b9341c2021-08-25 10:39:04 -0700212 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700213 }
214
215 /**
Rui Qiuf721b132021-09-10 12:41:09 -0700216 * Removes all MetricsConfigs. This will also remove all MetricsConfig outputs.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700217 */
218 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700219 public void removeAllMetricsConfigs() {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700220 mContext.enforceCallingOrSelfPermission(
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700221 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "removeAllMetricsConfigs");
Rui Qiu9b9341c2021-08-25 10:39:04 -0700222 mTelemetryHandler.post(() -> {
Rui Qiuf721b132021-09-10 12:41:09 -0700223 Slog.d(CarLog.TAG_TELEMETRY,
224 "Removing all metrics config from car telemetry service");
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700225 mDataBroker.removeAllMetricsConfigurations();
226 mMetricsConfigStore.removeAllMetricsConfigs();
227 mResultStore.removeAllResults();
Rui Qiu9b9341c2021-08-25 10:39:04 -0700228 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700229 }
230
231 /**
232 * Sends script results associated with the given key using the
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700233 * {@link ICarTelemetryServiceListener}. This method assumes listener is set. Otherwise it
234 * does nothing.
235 *
236 * @param key the unique identifier of a MetricsConfig.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700237 */
238 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700239 public void sendFinishedReports(@NonNull MetricsConfigKey key) {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700240 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700241 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "sendFinishedReports");
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700242 mTelemetryHandler.post(() -> {
243 if (mListener == null) {
244 Slog.w(CarLog.TAG_TELEMETRY, "ICarTelemetryServiceListener is not set");
245 return;
246 }
247 if (DEBUG) {
248 Slog.d(CarLog.TAG_TELEMETRY,
249 "Flushing reports for metrics config " + key.getName());
250 }
251 PersistableBundle result = mResultStore.getFinalResult(key.getName(), true);
252 TelemetryProto.TelemetryError error = mResultStore.getError(key.getName(), true);
253 if (result != null) {
254 sendFinalResult(key, result);
255 } else if (error != null) {
256 sendError(key, error);
257 } else {
258 Slog.w(CarLog.TAG_TELEMETRY, "config " + key.getName()
259 + " did not produce any results");
260 }
261 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700262 }
263
264 /**
Rui Qiu9b9341c2021-08-25 10:39:04 -0700265 * Sends all script results or errors using the {@link ICarTelemetryServiceListener}.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700266 */
267 @Override
268 public void sendAllFinishedReports() {
269 // TODO(b/184087869): Implement
270 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700271 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "sendAllFinishedReports");
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700272 if (DEBUG) {
Rui Qiu9b9341c2021-08-25 10:39:04 -0700273 Slog.d(CarLog.TAG_TELEMETRY, "Flushing all reports");
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700274 }
275 }
276
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700277 private void sendFinalResult(MetricsConfigKey key, PersistableBundle result) {
278 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
279 result.writeToStream(bos);
280 mListener.onResult(key, bos.toByteArray());
281 } catch (RemoteException e) {
282 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
283 } catch (IOException e) {
284 Slog.w(CarLog.TAG_TELEMETRY, "failed to write bundle to output stream", e);
285 }
286 }
287
288 private void sendError(MetricsConfigKey key, TelemetryProto.TelemetryError error) {
289 try {
290 mListener.onError(key, error.toByteArray());
291 } catch (RemoteException e) {
292 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
293 }
294 }
295
Rui Qiu9b9341c2021-08-25 10:39:04 -0700296 @VisibleForTesting
297 Handler getTelemetryHandler() {
298 return mTelemetryHandler;
Rui Qiuebfbcfb2021-05-18 17:01:27 -0700299 }
Rui Qiuf721b132021-09-10 12:41:09 -0700300
301 @VisibleForTesting
302 ResultStore getResultStore() {
303 return mResultStore;
304 }
305
306 @VisibleForTesting
307 MetricsConfigStore getMetricsConfigStore() {
308 return mMetricsConfigStore;
309 }
Max Dashoukff9ffbc2021-02-16 11:36:39 -0800310}