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 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 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 117 public int onStartCommand(Intent intent, int flags, int startId) { 118 // keep it alive. 119 return START_STICKY; 120 } 121 122 @Override 123 public IBinder onBind(Intent intent) { 124 return mICarImpl; 125 } 126 127 @Override 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 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 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 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 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 */ 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 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