blob: 509ecdd2b1e1e950d48d89c17c2c831d5873ea86 [file] [log] [blame]
keunyoungca515072015-07-10 12:21:47 -07001/*
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 */
Ram Periathiruvadibf0eab72018-02-06 12:32:43 -080016
keunyoungca515072015-07-10 12:21:47 -070017package com.android.car;
18
Pavel Maltsev0d07c762016-11-03 16:40:15 -070019import static android.os.SystemClock.elapsedRealtime;
20
21import android.annotation.Nullable;
keunyoungca515072015-07-10 12:21:47 -070022import android.app.Service;
keunyoungca515072015-07-10 12:21:47 -070023import android.content.Intent;
Pavel Maltsevcfe93102017-02-02 12:38:08 -080024import android.hardware.automotive.vehicle.V2_0.IVehicle;
Pavel Maltsevec83b632017-01-05 15:10:55 -080025import android.os.Build;
keunyoungca515072015-07-10 12:21:47 -070026import android.os.IBinder;
Pavel Maltsevec83b632017-01-05 15:10:55 -080027import android.os.IHwBinder.DeathRecipient;
Steven Moreland15522042017-01-05 09:44:59 -080028import android.os.RemoteException;
Steve Paikb40de192018-03-01 18:07:38 -080029import android.os.ServiceManager;
Pavel Maltsevec83b632017-01-05 15:10:55 -080030import android.os.SystemClock;
Vitalii Tomkiv973d2a22016-04-13 17:05:38 -070031import android.os.SystemProperties;
keunyoungca515072015-07-10 12:21:47 -070032import android.util.Log;
33
Enrico Granatab19bc322017-10-12 12:25:06 -070034import com.android.car.systeminterface.SystemInterface;
Pavel Maltsevec83b632017-01-05 15:10:55 -080035import com.android.internal.annotations.VisibleForTesting;
36import com.android.internal.util.RingBufferIndices;
37
Yao Chene33f07e2016-07-26 12:02:51 -070038import java.io.FileDescriptor;
39import java.io.PrintWriter;
Pavel Maltsevec83b632017-01-05 15:10:55 -080040import java.util.NoSuchElementException;
Yao Chene33f07e2016-07-26 12:02:51 -070041
keunyoungca515072015-07-10 12:21:47 -070042public class CarService extends Service {
43
Pavel Maltsev0d07c762016-11-03 16:40:15 -070044 private static final long WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS = 10_000;
45
Pavel Maltsevec83b632017-01-05 15:10:55 -080046 private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
47
48 private CanBusErrorNotifier mCanBusErrorNotifier;
keunyoungca515072015-07-10 12:21:47 -070049 private ICarImpl mICarImpl;
Pavel Maltsevec83b632017-01-05 15:10:55 -080050 private IVehicle mVehicle;
51
Pavel Maltsev2cc76d02017-02-14 12:28:33 -080052 private String mVehicleInterfaceName;
53
Pavel Maltsevec83b632017-01-05 15:10:55 -080054 // If 10 crashes of Vehicle HAL occurred within 10 minutes then thrown an exception in
55 // Car Service.
56 private final CrashTracker mVhalCrashTracker = new CrashTracker(
57 10, // Max crash count.
58 10 * 60 * 1000, // 10 minutes - sliding time window.
59 () -> {
60 if (IS_USER_BUILD) {
61 Log.e(CarLog.TAG_SERVICE, "Vehicle HAL keeps crashing, notifying user...");
62 mCanBusErrorNotifier.reportFailure(CarService.this);
63 } else {
64 throw new RuntimeException(
65 "Vehicle HAL crashed too many times in a given time frame");
66 }
67 }
68 );
69
70 private final VehicleDeathRecipient mVehicleDeathRecipient = new VehicleDeathRecipient();
keunyoungca515072015-07-10 12:21:47 -070071
72 @Override
73 public void onCreate() {
74 Log.i(CarLog.TAG_SERVICE, "Service onCreate");
Pavel Maltsevec83b632017-01-05 15:10:55 -080075 mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);
Pavel Maltsev99e1a752017-08-24 15:15:05 -070076 mVehicle = getVehicle();
Pavel Maltsev2cc76d02017-02-14 12:28:33 -080077
Pavel Maltsevec83b632017-01-05 15:10:55 -080078 if (mVehicle == null) {
Pavel Maltsev0d07c762016-11-03 16:40:15 -070079 throw new IllegalStateException("Vehicle HAL service is not available.");
80 }
Pavel Maltsev2cc76d02017-02-14 12:28:33 -080081 try {
82 mVehicleInterfaceName = mVehicle.interfaceDescriptor();
83 } catch (RemoteException e) {
84 throw new IllegalStateException("Unable to get Vehicle HAL interface descriptor", e);
85 }
86
87 Log.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);
Pavel Maltsev0d07c762016-11-03 16:40:15 -070088
Enrico Granatab19bc322017-10-12 12:25:06 -070089 mICarImpl = new ICarImpl(this,
90 mVehicle,
91 SystemInterface.Builder.defaultSystemInterface(this).build(),
Enrico Granatae8056ca2018-04-03 13:19:52 -070092 mCanBusErrorNotifier,
93 mVehicleInterfaceName);
Pavel Maltsev0d07c762016-11-03 16:40:15 -070094 mICarImpl.init();
Pavel Maltsevec83b632017-01-05 15:10:55 -080095
96 linkToDeath(mVehicle, mVehicleDeathRecipient);
97
Steve Paikb40de192018-03-01 18:07:38 -080098 ServiceManager.addService("car_service", mICarImpl);
Keun young Park14762262019-03-21 18:51:37 -070099 SystemProperties.set("boot.car_service_created", "1");
keunyoungca515072015-07-10 12:21:47 -0700100 super.onCreate();
101 }
102
Enrico Granata9a916d72017-09-19 14:33:08 -0700103 // onDestroy is best-effort and might not get called on shutdown/reboot. As such it is not
104 // suitable for permanently saving state or other need-to-happen operation. If you have a
105 // cleanup task that you want to make sure happens on shutdown/reboot, see OnShutdownReboot.
keunyoungca515072015-07-10 12:21:47 -0700106 @Override
107 public void onDestroy() {
108 Log.i(CarLog.TAG_SERVICE, "Service onDestroy");
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700109 mICarImpl.release();
Pavel Maltsevec83b632017-01-05 15:10:55 -0800110 mCanBusErrorNotifier.removeFailureReport(this);
111
112 if (mVehicle != null) {
113 try {
114 mVehicle.unlinkToDeath(mVehicleDeathRecipient);
115 mVehicle = null;
116 } catch (RemoteException e) {
117 // Ignore errors on shutdown path.
118 }
119 }
120
keunyoungca515072015-07-10 12:21:47 -0700121 super.onDestroy();
122 }
123
124 @Override
125 public int onStartCommand(Intent intent, int flags, int startId) {
126 // keep it alive.
127 return START_STICKY;
128 }
129
130 @Override
131 public IBinder onBind(Intent intent) {
132 return mICarImpl;
133 }
134
135 @Override
136 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
Enrico Granatae8056ca2018-04-03 13:19:52 -0700137 // historically, the way to get a dumpsys from CarService has been to use
138 // "dumpsys activity service com.android.car/.CarService" - leaving this
139 // as a forward to car_service makes the previously well-known command still work
140 mICarImpl.dump(fd, writer, args);
keunyoungca515072015-07-10 12:21:47 -0700141 }
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700142
143 @Nullable
Pavel Maltsev99e1a752017-08-24 15:15:05 -0700144 private IVehicle getVehicleWithTimeout(long waitMilliseconds) {
145 IVehicle vehicle = getVehicle();
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700146 long start = elapsedRealtime();
147 while (vehicle == null && (start + waitMilliseconds) > elapsedRealtime()) {
148 try {
149 Thread.sleep(100);
150 } catch (InterruptedException e) {
151 throw new RuntimeException("Sleep was interrupted", e);
152 }
153
Pavel Maltsev99e1a752017-08-24 15:15:05 -0700154 vehicle = getVehicle();
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700155 }
Pavel Maltsevec83b632017-01-05 15:10:55 -0800156
157 if (vehicle != null) {
158 mCanBusErrorNotifier.removeFailureReport(this);
159 }
160
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700161 return vehicle;
162 }
163
164 @Nullable
Pavel Maltsev99e1a752017-08-24 15:15:05 -0700165 private static IVehicle getVehicle() {
Steven Moreland15522042017-01-05 09:44:59 -0800166 try {
Pavel Maltsev99e1a752017-08-24 15:15:05 -0700167 return android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
Steven Moreland15522042017-01-05 09:44:59 -0800168 } catch (RemoteException e) {
Pavel Maltsevec83b632017-01-05 15:10:55 -0800169 Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);
170 } catch (NoSuchElementException e) {
171 Log.e(CarLog.TAG_SERVICE, "IVehicle service not registered yet");
172 }
173 return null;
174 }
175
176 private class VehicleDeathRecipient implements DeathRecipient {
177 private int deathCount = 0;
178
179 @Override
180 public void serviceDied(long cookie) {
181 Log.w(CarLog.TAG_SERVICE, "Vehicle HAL died.");
182
183 try {
184 mVehicle.unlinkToDeath(this);
185 } catch (RemoteException e) {
186 Log.e(CarLog.TAG_SERVICE, "Failed to unlinkToDeath", e); // Log and continue.
187 }
188 mVehicle = null;
189
190 mVhalCrashTracker.crashDetected();
191
Pavel Maltsev2cc76d02017-02-14 12:28:33 -0800192 Log.i(CarLog.TAG_SERVICE, "Trying to reconnect to Vehicle HAL: " +
193 mVehicleInterfaceName);
Pavel Maltsev99e1a752017-08-24 15:15:05 -0700194 mVehicle = getVehicleWithTimeout(WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS);
Pavel Maltsevec83b632017-01-05 15:10:55 -0800195 if (mVehicle == null) {
196 throw new IllegalStateException("Failed to reconnect to Vehicle HAL");
197 }
198
199 linkToDeath(mVehicle, this);
200
201 Log.i(CarLog.TAG_SERVICE, "Notifying car service Vehicle HAL reconnected...");
202 mICarImpl.vehicleHalReconnected(mVehicle);
203 }
204 }
205
206 private static void linkToDeath(IVehicle vehicle, DeathRecipient recipient) {
207 try {
208 vehicle.linkToDeath(recipient, 0);
209 } catch (RemoteException e) {
210 throw new IllegalStateException("Failed to linkToDeath Vehicle HAL");
211 }
212 }
213
214 @VisibleForTesting
215 static class CrashTracker {
216 private final int mMaxCrashCountLimit;
217 private final int mSlidingWindowMillis;
218
219 private final long[] mCrashTimestamps;
220 private final RingBufferIndices mCrashTimestampsIndices;
221 private final Runnable mCallback;
222
223 /**
224 * If maxCrashCountLimit number of crashes occurred within slidingWindowMillis time
225 * frame then call provided callback function.
226 */
227 CrashTracker(int maxCrashCountLimit, int slidingWindowMillis, Runnable callback) {
228 mMaxCrashCountLimit = maxCrashCountLimit;
229 mSlidingWindowMillis = slidingWindowMillis;
230 mCallback = callback;
231
232 mCrashTimestamps = new long[maxCrashCountLimit];
233 mCrashTimestampsIndices = new RingBufferIndices(mMaxCrashCountLimit);
234 }
235
236 void crashDetected() {
237 long lastCrash = SystemClock.elapsedRealtime();
238 mCrashTimestamps[mCrashTimestampsIndices.add()] = lastCrash;
239
240 if (mCrashTimestampsIndices.size() == mMaxCrashCountLimit) {
241 long firstCrash = mCrashTimestamps[mCrashTimestampsIndices.indexOf(0)];
242
243 if (lastCrash - firstCrash < mSlidingWindowMillis) {
244 mCallback.run();
245 }
246 }
Steven Moreland15522042017-01-05 09:44:59 -0800247 }
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700248 }
Chris Phoenix92044492017-01-11 18:36:39 -0800249}