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