/*
 * Copyright (C) 2015 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;


import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;

import android.car.builtin.os.ServiceManagerHelper;
import android.car.builtin.os.SystemPropertiesHelper;
import android.car.builtin.os.TraceHelper;
import android.car.builtin.util.EventLogHelper;
import android.car.builtin.util.Slogf;
import android.car.builtin.util.TimingsTraceLog;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;

import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
import com.android.car.internal.ProxiedService;
import com.android.car.systeminterface.SystemInterface;
import com.android.internal.annotations.Keep;

import java.io.FileDescriptor;
import java.io.PrintWriter;

/** Implementation of CarService */
@Keep
public class CarServiceImpl extends ProxiedService {
    public static final String CAR_SERVICE_INIT_TIMING_TAG = "CAR.InitTiming";
    public static final int CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS = 5;

    private ICarImpl mICarImpl;
    private VehicleStub mVehicle;

    private String mVehicleInterfaceName;

    private final VehicleDeathRecipient mVehicleDeathRecipient = new VehicleDeathRecipient();

    @Override
    public void onCreate() {
        TimingsTraceLog initTiming = new TimingsTraceLog(CAR_SERVICE_INIT_TIMING_TAG,
                TraceHelper.TRACE_TAG_CAR_SERVICE, CAR_SERVICE_INIT_TIMING_MIN_DURATION_MS);
        initTiming.traceBegin("CarService.onCreate");

        initTiming.traceBegin("getVehicle");
        mVehicle = VehicleStub.newVehicleStub();
        initTiming.traceEnd(); // "getVehicle"

        EventLogHelper.writeCarServiceCreate(/* hasVhal= */ mVehicle.isValid());

        mVehicleInterfaceName = mVehicle.getInterfaceDescriptor();

        Slogf.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);
        EventLogHelper.writeCarServiceConnected(mVehicleInterfaceName);

        mICarImpl = new ICarImpl.Builder()
                .setServiceContext(this)
                .setBuiltInContext(getBuiltinPackageContext())
                .setVehicle(mVehicle)
                .setSystemInterface(
                        SystemInterface.Builder.defaultSystemInterface(this).build())
                .setVehicleInterfaceName(mVehicleInterfaceName)
                .build();
        mICarImpl.init();

        mVehicle.linkToDeath(mVehicleDeathRecipient);

        ServiceManagerHelper.addService("car_service", mICarImpl);
        SystemPropertiesHelper.set("boot.car_service_created", "1");

        super.onCreate();

        initTiming.traceEnd(); // "CarService.onCreate"
    }

    // onDestroy is best-effort and might not get called on shutdown/reboot. As such it is not
    // suitable for permanently saving state or other need-to-happen operation. If you have a
    // cleanup task that you want to make sure happens on shutdown/reboot, see OnShutdownReboot.
    @Override
    public void onDestroy() {
        EventLogHelper.writeCarServiceDestroy(/* hasVhal= */ mVehicle.isValid());
        Slogf.i(CarLog.TAG_SERVICE, "Service onDestroy");
        mICarImpl.release();

        mVehicle.unlinkToDeath(mVehicleDeathRecipient);
        mVehicle = null;

        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // keep it alive.
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mICarImpl;
    }

    @Override
    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
        // historically, the way to get a dumpsys from CarService has been to use
        // "dumpsys activity service com.android.car/.CarService" - leaving this
        // as a forward to car_service makes the previously well-known command still work
        mICarImpl.dump(fd, writer, args);
    }

    private static class VehicleDeathRecipient implements IVehicleDeathRecipient {

        @Override
        public void serviceDied(long cookie) {
            EventLogHelper.writeCarServiceVhalDied(cookie);
            Slogf.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died. Car service will restart***");
            Process.killProcess(Process.myPid());
        }

        @Override
        public void binderDied() {
            EventLogHelper.writeCarServiceVhalDied(/*cookie=*/ 0);
            Slogf.wtf(CarLog.TAG_SERVICE, "***Vehicle HAL died. Car service will restart***");
            Process.killProcess(Process.myPid());
        }
    }
}
