• 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 
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
onCreate()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
onDestroy()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
onStartCommand(Intent intent, int flags, int startId)117     public int onStartCommand(Intent intent, int flags, int startId) {
118         // keep it alive.
119         return START_STICKY;
120     }
121 
122     @Override
onBind(Intent intent)123     public IBinder onBind(Intent intent) {
124         return mICarImpl;
125     }
126 
127     @Override
dump(FileDescriptor fd, PrintWriter writer, String[] args)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
getVehicleWithTimeout(long waitMilliseconds)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
getVehicle()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
serviceDied(long cookie)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 
linkToDeath(IVehicle vehicle, DeathRecipient recipient)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          */
CrashTracker(int maxCrashCountLimit, int slidingWindowMillis, Runnable callback)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 
crashDetected()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