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