blob: 16d3509657210fba0a0624a107c565a5195541af [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 Qiuac8b5ba2021-08-25 16:04:17 -070029import android.content.SharedPreferences;
Rui Qiua3418982021-08-20 11:21:12 -070030import android.os.Handler;
31import android.os.HandlerThread;
Rui Qiud5b4b0b2021-09-14 15:51:50 -070032import android.os.PersistableBundle;
Rui Qiu9b9341c2021-08-25 10:39:04 -070033import android.os.RemoteException;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080034import android.util.IndentingPrintWriter;
Rui Qiuf6668202021-04-08 15:37:20 -070035import android.util.Slog;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080036
Rui Qiua3418982021-08-20 11:21:12 -070037import com.android.car.CarLocalServices;
Rui Qiu9b9341c2021-08-25 10:39:04 -070038import com.android.car.CarLog;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070039import com.android.car.CarPropertyService;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080040import com.android.car.CarServiceBase;
Rui Qiua3418982021-08-20 11:21:12 -070041import com.android.car.CarServiceUtils;
42import com.android.car.systeminterface.SystemInterface;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070043import com.android.car.telemetry.databroker.DataBroker;
44import com.android.car.telemetry.databroker.DataBrokerController;
45import com.android.car.telemetry.databroker.DataBrokerImpl;
46import com.android.car.telemetry.publisher.PublisherFactory;
47import com.android.car.telemetry.publisher.StatsManagerImpl;
48import com.android.car.telemetry.publisher.StatsManagerProxy;
49import com.android.car.telemetry.systemmonitor.SystemMonitor;
Rui Qiu9b9341c2021-08-25 10:39:04 -070050import com.android.internal.annotations.VisibleForTesting;
Max Dashoukff9ffbc2021-02-16 11:36:39 -080051
Rui Qiu8c415de2021-05-03 15:52:16 -070052import com.google.protobuf.InvalidProtocolBufferException;
53
Rui Qiud5b4b0b2021-09-14 15:51:50 -070054import java.io.ByteArrayOutputStream;
Rui Qiua3418982021-08-20 11:21:12 -070055import java.io.File;
Rui Qiud5b4b0b2021-09-14 15:51:50 -070056import java.io.IOException;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070057import java.util.List;
Rui Qiu8c415de2021-05-03 15:52:16 -070058
Max Dashoukff9ffbc2021-02-16 11:36:39 -080059/**
60 * CarTelemetryService manages OEM telemetry collection, processing and communication
61 * with a data upload service.
62 */
Rui Qiuf6668202021-04-08 15:37:20 -070063public class CarTelemetryService extends ICarTelemetryService.Stub implements CarServiceBase {
Max Dashoukff9ffbc2021-02-16 11:36:39 -080064
Rui Qiuf6668202021-04-08 15:37:20 -070065 private static final boolean DEBUG = false;
Rui Qiua3418982021-08-20 11:21:12 -070066 public static final String TELEMETRY_DIR = "telemetry";
Rui Qiuf6668202021-04-08 15:37:20 -070067
68 private final Context mContext;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070069 private final CarPropertyService mCarPropertyService;
Rui Qiua3418982021-08-20 11:21:12 -070070 private final File mRootDirectory;
71 private final HandlerThread mTelemetryThread = CarServiceUtils.getHandlerThread(
72 CarTelemetryService.class.getSimpleName());
73 private final Handler mTelemetryHandler = new Handler(mTelemetryThread.getLooper());
Rui Qiuf6668202021-04-08 15:37:20 -070074
75 private ICarTelemetryServiceListener mListener;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070076 private DataBroker mDataBroker;
77 private DataBrokerController mDataBrokerController;
Rui Qiua3418982021-08-20 11:21:12 -070078 private MetricsConfigStore mMetricsConfigStore;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070079 private PublisherFactory mPublisherFactory;
80 private ResultStore mResultStore;
81 private SharedPreferences mSharedPrefs;
82 private StatsManagerProxy mStatsManagerProxy;
83 private SystemMonitor mSystemMonitor;
Rui Qiuf6668202021-04-08 15:37:20 -070084
Rui Qiuac8b5ba2021-08-25 16:04:17 -070085 public CarTelemetryService(Context context, CarPropertyService carPropertyService) {
Rui Qiuf6668202021-04-08 15:37:20 -070086 mContext = context;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070087 mCarPropertyService = carPropertyService;
Rui Qiua3418982021-08-20 11:21:12 -070088 SystemInterface systemInterface = CarLocalServices.getService(SystemInterface.class);
89 // full root directory path is /data/system/car/telemetry
90 mRootDirectory = new File(systemInterface.getSystemCarDir(), TELEMETRY_DIR);
Max Dashoukff9ffbc2021-02-16 11:36:39 -080091 }
92
93 @Override
94 public void init() {
Rui Qiua3418982021-08-20 11:21:12 -070095 mTelemetryHandler.post(() -> {
Rui Qiuac8b5ba2021-08-25 16:04:17 -070096 // initialize all necessary components
Rui Qiua3418982021-08-20 11:21:12 -070097 mMetricsConfigStore = new MetricsConfigStore(mRootDirectory);
Rui Qiuac8b5ba2021-08-25 16:04:17 -070098 mResultStore = new ResultStore(mRootDirectory);
99 mStatsManagerProxy = new StatsManagerImpl(
100 mContext.getSystemService(StatsManager.class));
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700101 mPublisherFactory = new PublisherFactory(mCarPropertyService, mTelemetryHandler,
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700102 mStatsManagerProxy, mRootDirectory);
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700103 mDataBroker = new DataBrokerImpl(mContext, mPublisherFactory, mResultStore);
104 mSystemMonitor = SystemMonitor.create(mContext, mTelemetryHandler);
105 mDataBrokerController = new DataBrokerController(mDataBroker, mSystemMonitor);
106
107 // start collecting data. once data is sent by publisher, scripts will be able to run
108 List<TelemetryProto.MetricsConfig> activeConfigs =
109 mMetricsConfigStore.getActiveMetricsConfigs();
110 for (TelemetryProto.MetricsConfig config : activeConfigs) {
111 mDataBroker.addMetricsConfiguration(config);
112 }
Rui Qiua3418982021-08-20 11:21:12 -0700113 });
Max Dashoukff9ffbc2021-02-16 11:36:39 -0800114 }
115
116 @Override
117 public void release() {
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700118 // TODO(b/197969149): prevent threading issue, block main thread
119 mTelemetryHandler.post(() -> mResultStore.flushToDisk());
Max Dashoukff9ffbc2021-02-16 11:36:39 -0800120 }
121
122 @Override
123 public void dump(IndentingPrintWriter writer) {
124 writer.println("Car Telemetry service");
125 }
Rui Qiuf6668202021-04-08 15:37:20 -0700126
127 /**
128 * Registers a listener with CarTelemetryService for the service to send data to cloud app.
129 */
130 @Override
131 public void setListener(@NonNull ICarTelemetryServiceListener listener) {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700132 // TODO(b/184890506): verify that only a hardcoded app can set the listener
Rui Qiuf6668202021-04-08 15:37:20 -0700133 mContext.enforceCallingOrSelfPermission(
134 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
Rui Qiu9b9341c2021-08-25 10:39:04 -0700135 mTelemetryHandler.post(() -> {
136 if (DEBUG) {
137 Slog.d(CarLog.TAG_TELEMETRY, "Setting the listener for car telemetry service");
138 }
139 mListener = listener;
140 });
Rui Qiuf6668202021-04-08 15:37:20 -0700141 }
142
143 /**
144 * Clears the listener registered with CarTelemetryService.
145 */
146 @Override
147 public void clearListener() {
148 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700149 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "clearListener");
150 mTelemetryHandler.post(() -> {
151 if (DEBUG) {
152 Slog.d(CarLog.TAG_TELEMETRY, "Clearing the listener for car telemetry service");
153 }
154 mListener = null;
155 });
Rui Qiuf6668202021-04-08 15:37:20 -0700156 }
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700157
158 /**
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700159 * Send a telemetry metrics config to the service. This method assumes
160 * {@link #setListener(ICarTelemetryServiceListener)} is called. Otherwise it does nothing.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700161 *
Rui Qiu9b9341c2021-08-25 10:39:04 -0700162 * @param key the unique key to identify the MetricsConfig.
163 * @param config the serialized bytes of a MetricsConfig object.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700164 */
165 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700166 public void addMetricsConfig(@NonNull MetricsConfigKey key, @NonNull byte[] config) {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700167 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700168 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "addMetricsConfig");
Rui Qiu9b9341c2021-08-25 10:39:04 -0700169 mTelemetryHandler.post(() -> {
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700170 if (mListener == null) {
171 Slog.w(CarLog.TAG_TELEMETRY, "ICarTelemetryServiceListener is not set");
172 return;
173 }
Rui Qiuf721b132021-09-10 12:41:09 -0700174 Slog.d(CarLog.TAG_TELEMETRY, "Adding metrics config " + key.getName()
175 + " to car telemetry service");
Rui Qiuf721b132021-09-10 12:41:09 -0700176 TelemetryProto.MetricsConfig metricsConfig = null;
177 int status = ERROR_METRICS_CONFIG_UNKNOWN;
Rui Qiu9b9341c2021-08-25 10:39:04 -0700178 try {
179 metricsConfig = TelemetryProto.MetricsConfig.parseFrom(config);
Rui Qiu9b9341c2021-08-25 10:39:04 -0700180 } catch (InvalidProtocolBufferException e) {
181 Slog.e(CarLog.TAG_TELEMETRY, "Failed to parse MetricsConfig.", e);
182 status = ERROR_METRICS_CONFIG_PARSE_FAILED;
183 }
Rui Qiuf721b132021-09-10 12:41:09 -0700184 // if config can be parsed, add it to persistent storage
185 if (metricsConfig != null) {
186 status = mMetricsConfigStore.addMetricsConfig(metricsConfig);
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700187 // TODO(b/199410900): update logic once metrics configs have expiration dates
188 mDataBroker.addMetricsConfiguration(metricsConfig);
Rui Qiuf721b132021-09-10 12:41:09 -0700189 }
190 // If no error (a config is successfully added), script results from an older version
191 // should be deleted
192 if (status == ERROR_METRICS_CONFIG_NONE) {
193 mResultStore.deleteResult(key.getName());
194 }
Rui Qiu9b9341c2021-08-25 10:39:04 -0700195 try {
196 mListener.onAddMetricsConfigStatus(key, status);
197 } catch (RemoteException e) {
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700198 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
Rui Qiu9b9341c2021-08-25 10:39:04 -0700199 }
200 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700201 }
202
203 /**
Rui Qiuf721b132021-09-10 12:41:09 -0700204 * Removes a metrics config based on the key. This will also remove outputs produced by the
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700205 * MetricsConfig. This method assumes {@link #setListener(ICarTelemetryServiceListener)} is
206 * called. Otherwise it does nothing.
207 *
208 * @param key the unique identifier of a MetricsConfig.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700209 */
210 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700211 public void removeMetricsConfig(@NonNull MetricsConfigKey key) {
Rui Qiu9b9341c2021-08-25 10:39:04 -0700212 mTelemetryHandler.post(() -> {
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700213 if (mListener == null) {
214 Slog.w(CarLog.TAG_TELEMETRY, "ICarTelemetryServiceListener is not set");
215 return;
216 }
Rui Qiuf721b132021-09-10 12:41:09 -0700217 Slog.d(CarLog.TAG_TELEMETRY, "Removing metrics config " + key.getName()
218 + " from car telemetry service");
Rui Qiu9b9341c2021-08-25 10:39:04 -0700219 // TODO(b/198792767): Check both config name and config version for deletion
Rui Qiuf721b132021-09-10 12:41:09 -0700220 // TODO(b/199540952): Stop and remove config from data broker
221 mResultStore.deleteResult(key.getName()); // delete the config's script results
Rui Qiu9b9341c2021-08-25 10:39:04 -0700222 boolean success = mMetricsConfigStore.deleteMetricsConfig(key.getName());
223 try {
224 mListener.onRemoveMetricsConfigStatus(key, success);
225 } catch (RemoteException e) {
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700226 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
Rui Qiu9b9341c2021-08-25 10:39:04 -0700227 }
228 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700229 }
230
231 /**
Rui Qiuf721b132021-09-10 12:41:09 -0700232 * Removes all MetricsConfigs. This will also remove all MetricsConfig outputs.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700233 */
234 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700235 public void removeAllMetricsConfigs() {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700236 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700237 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "removeAllMetricsConfig");
238 mTelemetryHandler.post(() -> {
Rui Qiuf721b132021-09-10 12:41:09 -0700239 // TODO(b/199540952): Stop and remove all configs from DataBroker
240 Slog.d(CarLog.TAG_TELEMETRY,
241 "Removing all metrics config from car telemetry service");
242 mMetricsConfigStore.deleteAllMetricsConfigs();
243 mResultStore.deleteAllResults();
Rui Qiu9b9341c2021-08-25 10:39:04 -0700244 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700245 }
246
247 /**
248 * Sends script results associated with the given key using the
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700249 * {@link ICarTelemetryServiceListener}. This method assumes listener is set. Otherwise it
250 * does nothing.
251 *
252 * @param key the unique identifier of a MetricsConfig.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700253 */
254 @Override
Rui Qiu9b9341c2021-08-25 10:39:04 -0700255 public void sendFinishedReports(@NonNull MetricsConfigKey key) {
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700256 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700257 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "sendFinishedReports");
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700258 mTelemetryHandler.post(() -> {
259 if (mListener == null) {
260 Slog.w(CarLog.TAG_TELEMETRY, "ICarTelemetryServiceListener is not set");
261 return;
262 }
263 if (DEBUG) {
264 Slog.d(CarLog.TAG_TELEMETRY,
265 "Flushing reports for metrics config " + key.getName());
266 }
267 PersistableBundle result = mResultStore.getFinalResult(key.getName(), true);
268 TelemetryProto.TelemetryError error = mResultStore.getError(key.getName(), true);
269 if (result != null) {
270 sendFinalResult(key, result);
271 } else if (error != null) {
272 sendError(key, error);
273 } else {
274 Slog.w(CarLog.TAG_TELEMETRY, "config " + key.getName()
275 + " did not produce any results");
276 }
277 });
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700278 }
279
280 /**
Rui Qiu9b9341c2021-08-25 10:39:04 -0700281 * Sends all script results or errors using the {@link ICarTelemetryServiceListener}.
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700282 */
283 @Override
284 public void sendAllFinishedReports() {
285 // TODO(b/184087869): Implement
286 mContext.enforceCallingOrSelfPermission(
Rui Qiu9b9341c2021-08-25 10:39:04 -0700287 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "sendAllFinishedReports");
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700288 if (DEBUG) {
Rui Qiu9b9341c2021-08-25 10:39:04 -0700289 Slog.d(CarLog.TAG_TELEMETRY, "Flushing all reports");
Rui Qiu9bc1bb92021-04-08 15:41:48 -0700290 }
291 }
292
Rui Qiud5b4b0b2021-09-14 15:51:50 -0700293 private void sendFinalResult(MetricsConfigKey key, PersistableBundle result) {
294 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
295 result.writeToStream(bos);
296 mListener.onResult(key, bos.toByteArray());
297 } catch (RemoteException e) {
298 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
299 } catch (IOException e) {
300 Slog.w(CarLog.TAG_TELEMETRY, "failed to write bundle to output stream", e);
301 }
302 }
303
304 private void sendError(MetricsConfigKey key, TelemetryProto.TelemetryError error) {
305 try {
306 mListener.onError(key, error.toByteArray());
307 } catch (RemoteException e) {
308 Slog.w(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
309 }
310 }
311
Rui Qiu9b9341c2021-08-25 10:39:04 -0700312 @VisibleForTesting
313 Handler getTelemetryHandler() {
314 return mTelemetryHandler;
Rui Qiuebfbcfb2021-05-18 17:01:27 -0700315 }
Rui Qiuf721b132021-09-10 12:41:09 -0700316
317 @VisibleForTesting
318 ResultStore getResultStore() {
319 return mResultStore;
320 }
321
322 @VisibleForTesting
323 MetricsConfigStore getMetricsConfigStore() {
324 return mMetricsConfigStore;
325 }
Max Dashoukff9ffbc2021-02-16 11:36:39 -0800326}