1 /* 2 * Copyright (C) 2022 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 android.car.oem; 18 19 import android.annotation.CallSuper; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.app.Service; 24 import android.car.CarVersion; 25 import android.car.builtin.util.Slogf; 26 import android.content.Intent; 27 import android.content.pm.PackageManager; 28 import android.os.Binder; 29 import android.os.IBinder; 30 import android.os.RemoteException; 31 import android.util.ArrayMap; 32 import android.util.Log; 33 34 import com.android.internal.annotations.GuardedBy; 35 36 import java.io.FileDescriptor; 37 import java.io.PrintWriter; 38 import java.util.Map; 39 40 /** 41 * This code will be running as part of the OEM Service. This provides basic implementation for OEM 42 * Service. OEMs should extend this class and override relevant methods. 43 * 44 * <p> 45 * OEM service implementation should have {@code android.car.permission.BIND_OEM_CAR_SERVICE} as 46 * required permission in manifest to connect to the OEM service. 47 * 48 * @hide 49 */ 50 @SystemApi 51 public abstract class OemCarService extends Service { 52 53 private static final String TAG = OemCarService.class.getSimpleName(); 54 private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); 55 private static final String PERMISSION_BIND_OEM_CAR_SERVICE = 56 "android.car.permission.BIND_OEM_CAR_SERVICE"; 57 58 // OEM Service components 59 // Note: Change the size as more components are added. 60 @GuardedBy("mLock") 61 private final ArrayMap<Class<?>, OemCarServiceComponent> mOemCarServiceComponents = 62 new ArrayMap<Class<?>, OemCarServiceComponent>(1); 63 64 private final Object mLock = new Object(); 65 66 private final IOemCarService mInterface = new IOemCarService.Stub() { 67 // Component services 68 @Override 69 public IOemCarAudioFocusService getOemAudioFocusService() { 70 assertPermission(); 71 synchronized (mLock) { 72 return (IOemCarAudioFocusService) mOemCarServiceComponents 73 .getOrDefault(IOemCarAudioFocusService.class, null); 74 } 75 } 76 77 @Override 78 public IOemCarAudioVolumeService getOemAudioVolumeService() { 79 assertPermission(); 80 synchronized (mLock) { 81 return (IOemCarAudioVolumeService) mOemCarServiceComponents 82 .getOrDefault(IOemCarAudioVolumeService.class, null); 83 } 84 } 85 86 @Override 87 public IOemCarAudioDuckingService getOemAudioDuckingService() { 88 assertPermission(); 89 synchronized (mLock) { 90 return (IOemCarAudioDuckingService) mOemCarServiceComponents 91 .getOrDefault(IOemCarAudioDuckingService.class, null); 92 } 93 } 94 95 @Override 96 public void onCarServiceReady(IOemCarServiceCallback callback) throws RemoteException { 97 assertPermission(); 98 OemCarService.this.onCarServiceReady(); 99 synchronized (mLock) { 100 for (int i = 0; i < mOemCarServiceComponents.size(); i++) { 101 if (DBG) { 102 Slogf.d(TAG, "Calling onCarServiceReady for %s\n", 103 mOemCarServiceComponents.keyAt(i).getSimpleName()); 104 } 105 mOemCarServiceComponents.valueAt(i).onCarServiceReady(); 106 } 107 } 108 callback.sendOemCarServiceReady(); 109 } 110 111 @Override 112 public CarVersion getSupportedCarVersion() { 113 assertPermission(); 114 return OemCarService.this.getSupportedCarVersion(); 115 } 116 117 @Override 118 public String getAllStackTraces() { 119 assertPermission(); 120 Map<Thread, StackTraceElement[]> tracesMap = Thread.getAllStackTraces(); 121 StringBuilder sb = new StringBuilder(); 122 sb.append("OemService stack trace:\n"); 123 int i = 0; 124 for (Map.Entry<Thread, StackTraceElement[]> entry : tracesMap.entrySet()) { 125 sb.append("Thread: ").append(i++).append('\n'); 126 StackTraceElement[] stack = entry.getValue(); 127 for (int j = 0; j < stack.length; j++) { 128 sb.append(stack[j].toString()).append('\n'); 129 } 130 } 131 132 return sb.toString(); 133 } 134 135 private void assertPermission() { 136 if (checkCallingPermission( 137 PERMISSION_BIND_OEM_CAR_SERVICE) != PackageManager.PERMISSION_GRANTED) { 138 String errorMsg = "Caller with uid:" + Binder.getCallingUid() 139 + " doesn't have permission " + PERMISSION_BIND_OEM_CAR_SERVICE; 140 Slogf.e(TAG, errorMsg); 141 throw new SecurityException(errorMsg); 142 } 143 } 144 }; 145 146 147 /** 148 * {@inheritDoc} 149 * 150 * <p> 151 * OEM should override this method and do the initialization. OEM should also call super after 152 * initialization as this method would call {@link OemCarServiceComponent#init()} for each 153 * component implemented by OEM. 154 * 155 * <p> 156 * Car Service will not be available at the time of this initialization. If the OEM needs 157 * anything from CarService, they should wait for the CarServiceReady() call. It is expected 158 * that most of the initialization will finish in this call. 159 */ 160 @Override 161 @CallSuper onCreate()162 public void onCreate() { 163 if (DBG) { 164 Slogf.d(TAG, "OnCreate"); 165 } 166 167 // Create all components 168 OemCarAudioFocusService oemCarAudioFocusService = getOemAudioFocusService(); 169 OemCarAudioVolumeService oemCarAudioVolumeService = getOemAudioVolumeService(); 170 OemCarAudioDuckingService oemCarAudioDuckingService = getOemAudioDuckingService(); 171 synchronized (mLock) { 172 if (oemCarAudioFocusService != null) { 173 mOemCarServiceComponents.put(IOemCarAudioFocusService.class, 174 new OemCarAudioFocusServiceImpl(oemCarAudioFocusService)); 175 } 176 if (oemCarAudioVolumeService != null) { 177 mOemCarServiceComponents.put(IOemCarAudioVolumeService.class, 178 new OemCarAudioVolumeServiceImpl(oemCarAudioVolumeService)); 179 } 180 if (oemCarAudioDuckingService != null) { 181 mOemCarServiceComponents.put(IOemCarAudioDuckingService.class, 182 new OemCarAudioDuckingServiceImpl(oemCarAudioDuckingService)); 183 } 184 185 // Initialize them 186 for (int i = 0; i < mOemCarServiceComponents.size(); i++) { 187 if (DBG) { 188 Slogf.d(TAG, "Initializing %s\n", 189 mOemCarServiceComponents.keyAt(i).getSimpleName()); 190 } 191 mOemCarServiceComponents.valueAt(i).init(); 192 } 193 } 194 super.onCreate(); 195 } 196 197 /** 198 * {@inheritDoc} 199 * 200 * <p> 201 * OEM should override this method and do all the resources deallocation. OEM should also call 202 * super after resource deallocation as this method would call 203 * {@link OemCarServiceComponent#release()} for each component implemented by OEM. 204 */ 205 @Override 206 @CallSuper onDestroy()207 public void onDestroy() { 208 if (DBG) { 209 Slogf.d(TAG, "OnDestroy"); 210 } 211 212 // Destroy all components and release the resources 213 synchronized (mLock) { 214 for (int i = 0; i < mOemCarServiceComponents.size(); i++) { 215 mOemCarServiceComponents.valueAt(i).release(); 216 } 217 } 218 219 super.onDestroy(); 220 } 221 222 @Override onStartCommand(@ullable Intent intent, int flags, int startId)223 public final int onStartCommand(@Nullable Intent intent, int flags, int startId) { 224 if (DBG) { 225 Slogf.d(TAG, "onStartCommand"); 226 } 227 return START_STICKY; 228 } 229 230 @NonNull 231 @Override onBind(@ullable Intent intent)232 public final IBinder onBind(@Nullable Intent intent) { 233 if (DBG) { 234 Slogf.d(TAG, "onBind"); 235 } 236 return mInterface.asBinder(); 237 } 238 239 /** 240 * Gets Audio Focus Service implemented by OEM Service. 241 * 242 * @return audio focus service if implemented by OEM service, else return {@code null}. 243 */ 244 @Nullable 245 @SuppressWarnings("[OnNameExpected]") getOemAudioFocusService()246 public OemCarAudioFocusService getOemAudioFocusService() { 247 if (DBG) { 248 Slogf.d(TAG, "getOemUserService"); 249 } 250 return null; 251 } 252 253 /** 254 * Gets Audio Volume implemented by OEM Service. 255 * 256 * @return audio volume service if implemented by OEM service, else return {@code null}. 257 */ 258 @Nullable 259 @SuppressWarnings("OnNameExpected") getOemAudioVolumeService()260 public OemCarAudioVolumeService getOemAudioVolumeService() { 261 if (DBG) { 262 Slogf.d(TAG, "getOemAudioVolumeService"); 263 } 264 return null; 265 } 266 267 /** 268 * Gets Audio Ducking implemented by OEM Service. 269 * 270 * @return audio ducking service if implemented by OEM service, else return {@code null}. 271 */ 272 @Nullable 273 @SuppressWarnings("OnNameExpected") getOemAudioDuckingService()274 public OemCarAudioDuckingService getOemAudioDuckingService() { 275 if (DBG) { 276 Slogf.d(TAG, "getOemAudioDuckingService"); 277 } 278 return null; 279 } 280 281 @CallSuper 282 @Override dump(@ullable FileDescriptor fd, @Nullable PrintWriter writer, @Nullable String[] args)283 public void dump(@Nullable FileDescriptor fd, @Nullable PrintWriter writer, 284 @Nullable String[] args) { 285 writer.println("**** Dump OemCarService ****"); 286 synchronized (mLock) { 287 for (int i = 0; i < mOemCarServiceComponents.size(); i++) { 288 mOemCarServiceComponents.valueAt(i).dump(writer, args); 289 } 290 } 291 } 292 293 /** 294 * Checks the supported CarVersion by the OEM service. 295 */ 296 @SuppressWarnings("[OnNameExpected]") 297 @NonNull getSupportedCarVersion()298 public abstract CarVersion getSupportedCarVersion(); 299 300 /** 301 * Informs OEM service that CarService is now ready for communication. 302 * 303 * <p> 304 * OEM should override this method if there is any initialization depending on CarService. 305 */ onCarServiceReady()306 public abstract void onCarServiceReady(); 307 } 308