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