Home | History | Annotate | Download | only in car
      1 /*
      2  * Copyright (C) 2015 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  */
     16 
     17 package com.android.car;
     18 
     19 import static android.os.SystemClock.elapsedRealtime;
     20 
     21 import android.annotation.Nullable;
     22 import android.app.Service;
     23 import android.content.Intent;
     24 import android.content.pm.PackageManager;
     25 import android.hardware.automotive.vehicle.V2_0.IVehicle;
     26 import android.os.Binder;
     27 import android.os.Build;
     28 import android.os.IBinder;
     29 import android.os.IHwBinder.DeathRecipient;
     30 import android.os.RemoteException;
     31 import android.os.ServiceManager;
     32 import android.os.SystemClock;
     33 import android.os.SystemProperties;
     34 import android.util.Log;
     35 
     36 import com.android.car.systeminterface.SystemInterface;
     37 
     38 import com.android.internal.annotations.VisibleForTesting;
     39 import com.android.internal.util.RingBufferIndices;
     40 
     41 import java.io.FileDescriptor;
     42 import java.io.PrintWriter;
     43 import java.util.NoSuchElementException;
     44 
     45 public class CarService extends Service {
     46 
     47     private static final long WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS = 10_000;
     48 
     49     private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
     50 
     51     private CanBusErrorNotifier mCanBusErrorNotifier;
     52     private ICarImpl mICarImpl;
     53     private IVehicle mVehicle;
     54 
     55     private String mVehicleInterfaceName;
     56 
     57     // If 10 crashes of Vehicle HAL occurred within 10 minutes then thrown an exception in
     58     // Car Service.
     59     private final CrashTracker mVhalCrashTracker = new CrashTracker(
     60             10,  // Max crash count.
     61             10 * 60 * 1000,  // 10 minutes - sliding time window.
     62             () -> {
     63                 if (IS_USER_BUILD) {
     64                     Log.e(CarLog.TAG_SERVICE, "Vehicle HAL keeps crashing, notifying user...");
     65                     mCanBusErrorNotifier.reportFailure(CarService.this);
     66                 } else {
     67                     throw new RuntimeException(
     68                             "Vehicle HAL crashed too many times in a given time frame");
     69                 }
     70             }
     71     );
     72 
     73     private final VehicleDeathRecipient mVehicleDeathRecipient = new VehicleDeathRecipient();
     74 
     75     @Override
     76     public void onCreate() {
     77         Log.i(CarLog.TAG_SERVICE, "Service onCreate");
     78         mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);
     79         mVehicle = getVehicle();
     80 
     81         if (mVehicle == null) {
     82             throw new IllegalStateException("Vehicle HAL service is not available.");
     83         }
     84         try {
     85             mVehicleInterfaceName = mVehicle.interfaceDescriptor();
     86         } catch (RemoteException e) {
     87             throw new IllegalStateException("Unable to get Vehicle HAL interface descriptor", e);
     88         }
     89 
     90         Log.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);
     91 
     92         mICarImpl = new ICarImpl(this,
     93                 mVehicle,
     94                 SystemInterface.Builder.defaultSystemInterface(this).build(),
     95                 mCanBusErrorNotifier,
     96                 mVehicleInterfaceName);
     97         mICarImpl.init();
     98         SystemProperties.set("boot.car_service_created", "1");
     99 
    100         linkToDeath(mVehicle, mVehicleDeathRecipient);
    101 
    102         ServiceManager.addService("car_service", mICarImpl);
    103         super.onCreate();
    104     }
    105 
    106     // onDestroy is best-effort and might not get called on shutdown/reboot. As such it is not
    107     // suitable for permanently saving state or other need-to-happen operation. If you have a
    108     // cleanup task that you want to make sure happens on shutdown/reboot, see OnShutdownReboot.
    109     @Override
    110     public void onDestroy() {
    111         Log.i(CarLog.TAG_SERVICE, "Service onDestroy");
    112         mICarImpl.release();
    113         mCanBusErrorNotifier.removeFailureReport(this);
    114 
    115         if (mVehicle != null) {
    116             try {
    117                 mVehicle.unlinkToDeath(mVehicleDeathRecipient);
    118                 mVehicle = null;
    119             } catch (RemoteException e) {
    120                 // Ignore errors on shutdown path.
    121             }
    122         }
    123 
    124         super.onDestroy();
    125     }
    126 
    127     @Override
    128     public int onStartCommand(Intent intent, int flags, int startId) {
    129         // keep it alive.
    130         return START_STICKY;
    131     }
    132 
    133     @Override
    134     public IBinder onBind(Intent intent) {
    135         return mICarImpl;
    136     }
    137 
    138     @Override
    139     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
    140         // historically, the way to get a dumpsys from CarService has been to use
    141         // "dumpsys activity service com.android.car/.CarService" - leaving this
    142         // as a forward to car_service makes the previously well-known command still work
    143         mICarImpl.dump(fd, writer, args);
    144     }
    145 
    146     @Nullable
    147     private IVehicle getVehicleWithTimeout(long waitMilliseconds) {
    148         IVehicle vehicle = getVehicle();
    149         long start = elapsedRealtime();
    150         while (vehicle == null && (start + waitMilliseconds) > elapsedRealtime()) {
    151             try {
    152                 Thread.sleep(100);
    153             } catch (InterruptedException e) {
    154                 throw new RuntimeException("Sleep was interrupted", e);
    155             }
    156 
    157             vehicle = getVehicle();
    158         }
    159 
    160         if (vehicle != null) {
    161             mCanBusErrorNotifier.removeFailureReport(this);
    162         }
    163 
    164         return vehicle;
    165     }
    166 
    167     @Nullable
    168     private static IVehicle getVehicle() {
    169         try {
    170             return android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
    171         } catch (RemoteException e) {
    172             Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);
    173         } catch (NoSuchElementException e) {
    174             Log.e(CarLog.TAG_SERVICE, "IVehicle service not registered yet");
    175         }
    176         return null;
    177     }
    178 
    179     private class VehicleDeathRecipient implements DeathRecipient {
    180         private int deathCount = 0;
    181 
    182         @Override
    183         public void serviceDied(long cookie) {
    184             Log.w(CarLog.TAG_SERVICE, "Vehicle HAL died.");
    185 
    186             try {
    187                 mVehicle.unlinkToDeath(this);
    188             } catch (RemoteException e) {
    189                 Log.e(CarLog.TAG_SERVICE, "Failed to unlinkToDeath", e);  // Log and continue.
    190             }
    191             mVehicle = null;
    192 
    193             mVhalCrashTracker.crashDetected();
    194 
    195             Log.i(CarLog.TAG_SERVICE, "Trying to reconnect to Vehicle HAL: " +
    196                     mVehicleInterfaceName);
    197             mVehicle = getVehicleWithTimeout(WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS);
    198             if (mVehicle == null) {
    199                 throw new IllegalStateException("Failed to reconnect to Vehicle HAL");
    200             }
    201 
    202             linkToDeath(mVehicle, this);
    203 
    204             Log.i(CarLog.TAG_SERVICE, "Notifying car service Vehicle HAL reconnected...");
    205             mICarImpl.vehicleHalReconnected(mVehicle);
    206         }
    207     }
    208 
    209     private static void linkToDeath(IVehicle vehicle, DeathRecipient recipient) {
    210         try {
    211             vehicle.linkToDeath(recipient, 0);
    212         } catch (RemoteException e) {
    213             throw new IllegalStateException("Failed to linkToDeath Vehicle HAL");
    214         }
    215     }
    216 
    217     @VisibleForTesting
    218     static class CrashTracker {
    219         private final int mMaxCrashCountLimit;
    220         private final int mSlidingWindowMillis;
    221 
    222         private final long[] mCrashTimestamps;
    223         private final RingBufferIndices mCrashTimestampsIndices;
    224         private final Runnable mCallback;
    225 
    226         /**
    227          * If maxCrashCountLimit number of crashes occurred within slidingWindowMillis time
    228          * frame then call provided callback function.
    229          */
    230         CrashTracker(int maxCrashCountLimit, int slidingWindowMillis, Runnable callback) {
    231             mMaxCrashCountLimit = maxCrashCountLimit;
    232             mSlidingWindowMillis = slidingWindowMillis;
    233             mCallback = callback;
    234 
    235             mCrashTimestamps = new long[maxCrashCountLimit];
    236             mCrashTimestampsIndices = new RingBufferIndices(mMaxCrashCountLimit);
    237         }
    238 
    239         void crashDetected() {
    240             long lastCrash = SystemClock.elapsedRealtime();
    241             mCrashTimestamps[mCrashTimestampsIndices.add()] = lastCrash;
    242 
    243             if (mCrashTimestampsIndices.size() == mMaxCrashCountLimit) {
    244                 long firstCrash = mCrashTimestamps[mCrashTimestampsIndices.indexOf(0)];
    245 
    246                 if (lastCrash - firstCrash < mSlidingWindowMillis) {
    247                     mCallback.run();
    248                 }
    249             }
    250         }
    251     }
    252 }
    253