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 android.car.Car;
19 import android.car.test.ICarTest;
20 import android.content.Context;
21 import android.os.IBinder;
22 import android.os.RemoteException;
23 import android.util.Log;
24 
25 import com.android.internal.annotations.GuardedBy;
26 
27 import java.io.PrintWriter;
28 import java.util.Arrays;
29 import java.util.HashMap;
30 import java.util.Map;
31 
32 /**
33  * Service to allow testing / mocking vehicle HAL.
34  * This service uses Vehicle HAL APIs directly (one exception) as vehicle HAL mocking anyway
35  * requires accessing that level directly.
36  */
37 class CarTestService extends ICarTest.Stub implements CarServiceBase {
38 
39     private static final String TAG = CarTestService.class.getSimpleName();
40 
41     private final Context mContext;
42     private final ICarImpl mICarImpl;
43 
44     private final Object mLock = new Object();
45 
46     @GuardedBy("mLock")
47     private final Map<IBinder, TokenDeathRecipient> mTokens = new HashMap<>();
48 
CarTestService(Context context, ICarImpl carImpl)49     CarTestService(Context context, ICarImpl carImpl) {
50         mContext = context;
51         mICarImpl = carImpl;
52     }
53 
54     @Override
init()55     public void init() {
56         // nothing to do.
57         // This service should not reset anything for init / release to maintain mocking.
58     }
59 
60     @Override
release()61     public void release() {
62         // nothing to do
63         // This service should not reset anything for init / release to maintain mocking.
64     }
65 
66     @Override
dump(PrintWriter writer)67     public void dump(PrintWriter writer) {
68         writer.println("*CarTestService*");
69         writer.println(" mTokens:" + Arrays.toString(mTokens.entrySet().toArray()));
70     }
71 
72     @Override
stopCarService(IBinder token)73     public void stopCarService(IBinder token) throws RemoteException {
74         Log.d(TAG, "stopCarService, token: " + token);
75         ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE);
76 
77         synchronized (mLock) {
78             if (mTokens.containsKey(token)) {
79                 Log.w(TAG, "Calling stopCarService twice with the same token.");
80                 return;
81             }
82 
83             TokenDeathRecipient deathRecipient = new TokenDeathRecipient(token);
84             mTokens.put(token, deathRecipient);
85             token.linkToDeath(deathRecipient, 0);
86 
87             if (mTokens.size() == 1) {
88                 CarServiceUtils.runOnMainSync(mICarImpl::release);
89             }
90         }
91     }
92 
93     @Override
startCarService(IBinder token)94     public void startCarService(IBinder token) throws RemoteException {
95         Log.d(TAG, "startCarService, token: " + token);
96         ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE);
97         releaseToken(token);
98     }
99 
releaseToken(IBinder token)100     private void releaseToken(IBinder token) {
101         Log.d(TAG, "releaseToken, token: " + token);
102         synchronized (mLock) {
103             DeathRecipient deathRecipient = mTokens.remove(token);
104             if (deathRecipient != null) {
105                 token.unlinkToDeath(deathRecipient, 0);
106             }
107 
108             if (mTokens.size() == 0) {
109                 CarServiceUtils.runOnMainSync(mICarImpl::init);
110             }
111         }
112     }
113 
114     private class TokenDeathRecipient implements DeathRecipient {
115         private final IBinder mToken;
116 
TokenDeathRecipient(IBinder token)117         TokenDeathRecipient(IBinder token) throws RemoteException {
118             mToken = token;
119         }
120 
121         @Override
binderDied()122         public void binderDied() {
123             releaseToken(mToken);
124         }
125     }
126 }
127