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