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