• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate()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
onDestroy()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
onStartCommand(Intent intent, int flags, int startId)128     public int onStartCommand(Intent intent, int flags, int startId) {
129         // keep it alive.
130         return START_STICKY;
131     }
132 
133     @Override
onBind(Intent intent)134     public IBinder onBind(Intent intent) {
135         return mICarImpl;
136     }
137 
138     @Override
dump(FileDescriptor fd, PrintWriter writer, String[] args)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
getVehicleWithTimeout(long waitMilliseconds)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
getVehicle()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
serviceDied(long cookie)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 
linkToDeath(IVehicle vehicle, DeathRecipient recipient)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          */
CrashTracker(int maxCrashCountLimit, int slidingWindowMillis, Runnable callback)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 
crashDetected()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