| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.car; |
| |
| import android.car.Car; |
| import android.car.test.ICarTest; |
| import android.content.Context; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.util.Log; |
| |
| import com.android.internal.annotations.GuardedBy; |
| |
| import java.io.PrintWriter; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * Service to allow testing / mocking vehicle HAL. |
| * This service uses Vehicle HAL APIs directly (one exception) as vehicle HAL mocking anyway |
| * requires accessing that level directly. |
| */ |
| class CarTestService extends ICarTest.Stub implements CarServiceBase { |
| |
| private static final String TAG = CarTestService.class.getSimpleName(); |
| |
| private final Context mContext; |
| private final ICarImpl mICarImpl; |
| |
| private final Object mLock = new Object(); |
| |
| @GuardedBy("mLock") |
| private final Map<IBinder, TokenDeathRecipient> mTokens = new HashMap<>(); |
| |
| CarTestService(Context context, ICarImpl carImpl) { |
| mContext = context; |
| mICarImpl = carImpl; |
| } |
| |
| @Override |
| public void init() { |
| // nothing to do. |
| // This service should not reset anything for init / release to maintain mocking. |
| } |
| |
| @Override |
| public void release() { |
| // nothing to do |
| // This service should not reset anything for init / release to maintain mocking. |
| } |
| |
| @Override |
| public void dump(PrintWriter writer) { |
| writer.println("*CarTestService*"); |
| writer.println(" mTokens:" + Arrays.toString(mTokens.entrySet().toArray())); |
| } |
| |
| @Override |
| public void stopCarService(IBinder token) throws RemoteException { |
| Log.d(TAG, "stopCarService, token: " + token); |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE); |
| |
| synchronized (mLock) { |
| if (mTokens.containsKey(token)) { |
| Log.w(TAG, "Calling stopCarService twice with the same token."); |
| return; |
| } |
| |
| TokenDeathRecipient deathRecipient = new TokenDeathRecipient(token); |
| mTokens.put(token, deathRecipient); |
| token.linkToDeath(deathRecipient, 0); |
| |
| if (mTokens.size() == 1) { |
| CarServiceUtils.runOnMainSync(mICarImpl::release); |
| } |
| } |
| } |
| |
| @Override |
| public void startCarService(IBinder token) throws RemoteException { |
| Log.d(TAG, "startCarService, token: " + token); |
| ICarImpl.assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE); |
| releaseToken(token); |
| } |
| |
| private void releaseToken(IBinder token) { |
| Log.d(TAG, "releaseToken, token: " + token); |
| synchronized (mLock) { |
| DeathRecipient deathRecipient = mTokens.remove(token); |
| if (deathRecipient != null) { |
| token.unlinkToDeath(deathRecipient, 0); |
| } |
| |
| if (mTokens.size() == 0) { |
| CarServiceUtils.runOnMainSync(mICarImpl::init); |
| } |
| } |
| } |
| |
| private class TokenDeathRecipient implements DeathRecipient { |
| private final IBinder mToken; |
| |
| TokenDeathRecipient(IBinder token) throws RemoteException { |
| mToken = token; |
| } |
| |
| @Override |
| public void binderDied() { |
| releaseToken(mToken); |
| } |
| } |
| } |