blob: 695e25e772f03e481deb8d440833b95e1fe54fa9 [file] [log] [blame]
/*
* Copyright (C) 2021 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.
*/
package com.android.car.telemetry;
import static android.car.telemetry.CarTelemetryManager.ERROR_NEWER_MANIFEST_EXISTS;
import static android.car.telemetry.CarTelemetryManager.ERROR_NONE;
import static android.car.telemetry.CarTelemetryManager.ERROR_PARSE_MANIFEST_FAILED;
import static android.car.telemetry.CarTelemetryManager.ERROR_SAME_MANIFEST_EXISTS;
import android.annotation.NonNull;
import android.car.Car;
import android.car.telemetry.CarTelemetryManager.AddManifestError;
import android.car.telemetry.ICarTelemetryService;
import android.car.telemetry.ICarTelemetryServiceListener;
import android.car.telemetry.ManifestKey;
import android.content.Context;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.car.CarServiceBase;
import com.android.internal.annotations.GuardedBy;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.HashMap;
import java.util.Map;
/**
* CarTelemetryService manages OEM telemetry collection, processing and communication
* with a data upload service.
*/
public class CarTelemetryService extends ICarTelemetryService.Stub implements CarServiceBase {
// TODO(b/189340793): Rename Manifest to MetricsConfig
private static final boolean DEBUG = false;
private static final int DEFAULT_VERSION = 0;
private static final String TAG = CarTelemetryService.class.getSimpleName();
private final Context mContext;
@GuardedBy("mLock")
private final Map<String, Integer> mNameVersionMap = new HashMap<>();
private final Object mLock = new Object();
@GuardedBy("mLock")
private ICarTelemetryServiceListener mListener;
public CarTelemetryService(Context context) {
mContext = context;
}
@Override
public void init() {
// nothing to do
}
@Override
public void release() {
// nothing to do
}
@Override
public void dump(IndentingPrintWriter writer) {
writer.println("Car Telemetry service");
}
/**
* Registers a listener with CarTelemetryService for the service to send data to cloud app.
*/
@Override
public void setListener(@NonNull ICarTelemetryServiceListener listener) {
// TODO(b/184890506): verify that only a hardcoded app can set the listener
mContext.enforceCallingOrSelfPermission(
Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
synchronized (mLock) {
setListenerLocked(listener);
}
}
/**
* Clears the listener registered with CarTelemetryService.
*/
@Override
public void clearListener() {
mContext.enforceCallingOrSelfPermission(
Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
synchronized (mLock) {
clearListenerLocked();
}
}
/**
* Allows client to send telemetry manifests.
*
* @param key the unique key to identify the manifest.
* @param config the serialized bytes of a Manifest object.
* @return {@link AddManifestError} the error code.
*/
@Override
public @AddManifestError int addManifest(@NonNull ManifestKey key, @NonNull byte[] config) {
mContext.enforceCallingOrSelfPermission(
Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
synchronized (mLock) {
return addManifestLocked(key, config);
}
}
/**
* Removes a manifest based on the key.
*/
@Override
public boolean removeManifest(@NonNull ManifestKey key) {
mContext.enforceCallingOrSelfPermission(
Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
synchronized (mLock) {
return removeManifestLocked(key);
}
}
/**
* Removes all manifests.
*/
@Override
public void removeAllManifests() {
mContext.enforceCallingOrSelfPermission(
Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
synchronized (mLock) {
removeAllManifestsLocked();
}
}
/**
* Sends script results associated with the given key using the
* {@link ICarTelemetryServiceListener}.
*/
@Override
public void sendFinishedReports(@NonNull ManifestKey key) {
// TODO(b/184087869): Implement
mContext.enforceCallingOrSelfPermission(
Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
if (DEBUG) {
Slog.d(TAG, "Flushing reports for a manifest");
}
}
/**
* Sends all script results associated using the {@link ICarTelemetryServiceListener}.
*/
@Override
public void sendAllFinishedReports() {
// TODO(b/184087869): Implement
mContext.enforceCallingOrSelfPermission(
Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
if (DEBUG) {
Slog.d(TAG, "Flushing all reports");
}
}
/**
* Sends all errors using the {@link ICarTelemetryServiceListener}.
*/
@Override
public void sendScriptExecutionErrors() {
// TODO(b/184087869): Implement
mContext.enforceCallingOrSelfPermission(
Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
if (DEBUG) {
Slog.d(TAG, "Flushing script execution errors");
}
}
@GuardedBy("mLock")
private void setListenerLocked(@NonNull ICarTelemetryServiceListener listener) {
if (DEBUG) {
Slog.d(TAG, "Setting the listener for car telemetry service");
}
mListener = listener;
}
@GuardedBy("mLock")
private void clearListenerLocked() {
if (DEBUG) {
Slog.d(TAG, "Clearing listener");
}
mListener = null;
}
@GuardedBy("mLock")
private @AddManifestError int addManifestLocked(ManifestKey key, byte[] configProto) {
if (DEBUG) {
Slog.d(TAG, "Adding MetricsConfig to car telemetry service");
}
int currentVersion = mNameVersionMap.getOrDefault(key.getName(), DEFAULT_VERSION);
if (currentVersion > key.getVersion()) {
return ERROR_NEWER_MANIFEST_EXISTS;
} else if (currentVersion == key.getVersion()) {
return ERROR_SAME_MANIFEST_EXISTS;
}
TelemetryProto.MetricsConfig metricsConfig;
try {
metricsConfig = TelemetryProto.MetricsConfig.parseFrom(configProto);
} catch (InvalidProtocolBufferException e) {
Slog.e(TAG, "Failed to parse MetricsConfig.", e);
return ERROR_PARSE_MANIFEST_FAILED;
}
mNameVersionMap.put(key.getName(), key.getVersion());
// TODO(b/186047142): Store the MetricsConfig to disk
// TODO(b/186047142): Send metricsConfig to a script manager or a queue
return ERROR_NONE;
}
@GuardedBy("mLock")
private boolean removeManifestLocked(@NonNull ManifestKey key) {
Integer version = mNameVersionMap.remove(key.getName());
if (version == null) {
return false;
}
// TODO(b/186047142): Delete manifest from disk and remove it from queue
return true;
}
@GuardedBy("mLock")
private void removeAllManifestsLocked() {
if (DEBUG) {
Slog.d(TAG, "Removing all manifest from car telemetry service");
}
mNameVersionMap.clear();
// TODO(b/186047142): Delete all manifests from disk & queue
}
}