• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.car.remoteaccess.hal;
18 
19 import static android.car.Car.getPlatformVersion;
20 
21 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU;
22 
23 import android.annotation.Nullable;
24 import android.car.builtin.os.ServiceManagerHelper;
25 import android.car.builtin.os.TraceHelper;
26 import android.car.builtin.util.Slogf;
27 import android.car.builtin.util.TimingsTraceLog;
28 import android.hardware.automotive.remoteaccess.ApState;
29 import android.hardware.automotive.remoteaccess.IRemoteAccess;
30 import android.hardware.automotive.remoteaccess.IRemoteTaskCallback;
31 import android.os.IBinder;
32 import android.os.RemoteException;
33 import android.util.Log;
34 
35 import com.android.car.CarLog;
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.internal.annotations.VisibleForTesting;
38 
39 import java.lang.ref.WeakReference;
40 import java.util.Objects;
41 import java.util.concurrent.atomic.AtomicBoolean;
42 
43 /**
44  * Class to mediate communication between CarRemoteAccessSerevice and remote access HAL.
45  */
46 public final class RemoteAccessHalWrapper implements IBinder.DeathRecipient {
47 
48     static final String TAG = CarLog.tagFor(RemoteAccessHalWrapper.class);
49     private static final boolean DEBUG = Slogf.isLoggable(TAG, Log.DEBUG);
50 
51     private final Object mLock = new Object();
52     // Callback which is given by the instance creator.
53     private final RemoteAccessHalCallback mRemoteAccessHalCallback;
54     // Callback which is registered to remote access HAL.
55     private final IRemoteTaskCallback mRemoteTaskCallback = new RemoteTaskCallbackImpl(this);
56 
57     private final AtomicBoolean mConnecting = new AtomicBoolean();
58     @GuardedBy("mLock")
59     private IBinder mBinder;
60     @GuardedBy("mLock")
61     private IRemoteAccess mRemoteAccessHal;
62 
63     private IRemoteAccess mTestRemoteAccessHal;
64 
RemoteAccessHalWrapper(RemoteAccessHalCallback callback)65     public RemoteAccessHalWrapper(RemoteAccessHalCallback callback) {
66         this(callback, /* testRemoteAccessHal= */ null);
67     }
68 
69     /* For car service test. */
70     @VisibleForTesting
RemoteAccessHalWrapper(RemoteAccessHalCallback callback, IRemoteAccess testRemoteAccessHal)71     public RemoteAccessHalWrapper(RemoteAccessHalCallback callback,
72             IRemoteAccess testRemoteAccessHal) {
73         mRemoteAccessHalCallback = Objects.requireNonNull(callback, "Callback cannot be null");
74         mTestRemoteAccessHal = testRemoteAccessHal;
75     }
76 
77     /** Initializes connection to remote task HAL service. */
init()78     public void init() {
79         try {
80             connectToHal();
81         } catch (Exception e) {
82             Slogf.wtf(TAG, e, "Cannot connect to remote access HAL");
83         }
84     }
85 
86     /** Releases the internal resources. */
release()87     public void release() {
88         IRemoteAccess remoteAccessHal;
89         synchronized (mLock) {
90             remoteAccessHal = mRemoteAccessHal;
91             mRemoteAccessHal = null;
92         }
93         try {
94             if (remoteAccessHal != null) {
95                 remoteAccessHal.clearRemoteTaskCallback();
96             }
97         } catch (RemoteException e) {
98             Slogf.w(TAG, e, "Failed to clear remote task callback");
99         }
100         clearBinder();
101     }
102 
103     @Override
binderDied()104     public void binderDied() {
105         Slogf.w(TAG, "Remote access HAL service died");
106         synchronized (mLock) {
107             mRemoteAccessHal = null;
108             mBinder = null;
109         }
110         try {
111             connectToHal();
112         } catch (Exception e) {
113             Slogf.wtf(TAG, e, "Cannot connect to remote access HAL");
114         }
115     }
116 
117     /** Check {@link IRemoteAccess#getVehicleId()}. */
getVehicleId()118     public String getVehicleId() {
119         IRemoteAccess remoteAccessHal = getRemoteAccessHal();
120         try {
121             return remoteAccessHal.getVehicleId();
122         } catch (RemoteException | RuntimeException e) {
123             throw new IllegalStateException("Failed to get vehicle ID", e);
124         }
125     }
126 
127     /** Check {@link IRemoteAccess#getWakeupServiceName()}. */
getWakeupServiceName()128     public String getWakeupServiceName() {
129         IRemoteAccess remoteAccessHal = getRemoteAccessHal();
130         try {
131             return remoteAccessHal.getWakeupServiceName();
132         } catch (RemoteException | RuntimeException e) {
133             throw new IllegalStateException("Failed to get wakeup service name", e);
134         }
135     }
136 
137     /** Check {@link IRemoteAccess#getProcessorId()}. */
getProcessorId()138     public String getProcessorId() {
139         IRemoteAccess remoteAccessHal = getRemoteAccessHal();
140         try {
141             return remoteAccessHal.getProcessorId();
142         } catch (RemoteException | RuntimeException e) {
143             throw new IllegalStateException("Failed to get processor ID", e);
144         }
145     }
146 
147     /** Check {@link IRemoteAccess#notifyApStateChange(ApState)}. */
notifyApStateChange(boolean isReadyForRemoteTask, boolean isWakeupRequired)148     public boolean notifyApStateChange(boolean isReadyForRemoteTask, boolean isWakeupRequired) {
149         try {
150             IRemoteAccess remoteAccessHal = getRemoteAccessHal();
151             ApState state = new ApState();
152             state.isReadyForRemoteTask = isReadyForRemoteTask;
153             state.isWakeupRequired = isWakeupRequired;
154             remoteAccessHal.notifyApStateChange(state);
155         } catch (RemoteException | RuntimeException e) {
156             Slogf.w(TAG, e, "Failed to notify power state change: isReadyForRemoteTask=%b, "
157                     + "isWakeupRequired=%b", isReadyForRemoteTask, isWakeupRequired);
158             return false;
159         }
160         return true;
161     }
162 
connectToHal()163     private void connectToHal() {
164         if (!mConnecting.compareAndSet(/* expect= */ false, /* update= */ true)) {
165             Slogf.w(TAG, "Connecting to remote access HAL is in progress");
166             return;
167         }
168         TimingsTraceLog t = new TimingsTraceLog(TAG, TraceHelper.TRACE_TAG_CAR_SERVICE);
169 
170         IRemoteAccess remoteAccessHal;
171         if (mTestRemoteAccessHal != null) {
172             remoteAccessHal = mTestRemoteAccessHal;
173         } else {
174             t.traceBegin("connect-to-remote-access-hal");
175             IBinder binder = getRemoteAccessHalService();
176             t.traceEnd();
177             if (binder == null) {
178                 mConnecting.set(/* newValue= */ false);
179                 throw new IllegalStateException("Remote access HAL not found");
180             }
181 
182             try {
183                 binder.linkToDeath(this, /* flags= */ 0);
184             } catch (RemoteException e) {
185                 mConnecting.set(/* newValue= */ false);
186                 throw new IllegalStateException(
187                         "Failed to link a death recipient to remote access HAL", e);
188             }
189 
190             synchronized (mLock) {
191                 if (mBinder != null) {
192                     Slogf.w(TAG, "Remote access HAL is already connected");
193                     binder.unlinkToDeath(this, /* flags= */ 0);
194                     mConnecting.set(/* newValue= */ false);
195                     return;
196                 }
197                 mBinder = binder;
198                 mRemoteAccessHal = IRemoteAccess.Stub.asInterface(mBinder);
199                 remoteAccessHal = mRemoteAccessHal;
200             }
201             mConnecting.set(/* newValue= */ false);
202         }
203         try {
204             remoteAccessHal.setRemoteTaskCallback(mRemoteTaskCallback);
205         } catch (RemoteException e) {
206             throw new IllegalStateException("Failed to set remote task callback", e);
207         }
208         Slogf.i(TAG, "Connected to remote access HAL");
209     }
210 
clearBinder()211     private void clearBinder() {
212         synchronized (mLock) {
213             if (mBinder == null) {
214                 return;
215             }
216             mBinder.unlinkToDeath(this, /* flags= */ 0);
217             mBinder = null;
218         }
219     }
220 
getRemoteAccessHal()221     private IRemoteAccess getRemoteAccessHal() {
222         synchronized (mLock) {
223             if (mTestRemoteAccessHal != null) {
224                 return mTestRemoteAccessHal;
225             }
226             if (mRemoteAccessHal == null) {
227                 throw new IllegalStateException("Remote access HAL is not ready");
228             }
229             return mRemoteAccessHal;
230         }
231     }
232 
onRemoteTaskRequested(String clientId, byte[] data)233     private void onRemoteTaskRequested(String clientId, byte[] data) {
234         mRemoteAccessHalCallback.onRemoteTaskRequested(clientId, data);
235     }
236 
237     /**
238      * Connects to remote access HAL.
239      *
240      * <p>If there are multiple service implementations, connection is made in an order of service
241      * declaration.
242      */
243     @VisibleForTesting
244     @Nullable
getRemoteAccessHalService()245     public static IBinder getRemoteAccessHalService() {
246         if (!isPlatformVersionAtLeastU()) {
247             Slogf.w(TAG, "The platform does not support getRemoteAccessHalService."
248                     + " Platform version: %s", getPlatformVersion());
249             return null;
250         }
251         String[] instances = ServiceManagerHelper.getDeclaredInstances(IRemoteAccess.DESCRIPTOR);
252         if (instances == null || instances.length == 0) {
253             Slogf.e(TAG, "No remote access HAL service found");
254             return null;
255         }
256         // If there are many remote access HAL instances, they are traversed in a declaring order.
257         for (int i = 0; i < instances.length; i++) {
258             String fqName = IRemoteAccess.DESCRIPTOR + "/" + instances[i];
259             IBinder binder = ServiceManagerHelper.waitForDeclaredService(fqName);
260             if (binder != null) {
261                 Slogf.i(TAG, "Connected to remote access HAL instance(%s)", fqName);
262                 return binder;
263             }
264         }
265         return null;
266     }
267 
268     private static final class RemoteTaskCallbackImpl extends IRemoteTaskCallback.Stub {
269         private final WeakReference<RemoteAccessHalWrapper> mHalWrapper;
270 
RemoteTaskCallbackImpl(RemoteAccessHalWrapper halWrapper)271         RemoteTaskCallbackImpl(RemoteAccessHalWrapper halWrapper) {
272             mHalWrapper = new WeakReference<>(halWrapper);
273         }
274 
275         @Override
onRemoteTaskRequested(String clientId, byte[] data)276         public void onRemoteTaskRequested(String clientId, byte[] data) {
277             if (DEBUG) {
278                 Slogf.d(TAG, "onRemoteTaskRequested is called: clientId = %s, data size = %d",
279                         clientId, data == null ? 0 : data.length);
280             }
281             RemoteAccessHalWrapper halWrapper = mHalWrapper.get();
282             if (halWrapper == null) {
283                 Slogf.w(TAG, "RemoteAccessHalWrapper is not available: clientId = %s", clientId);
284                 return;
285             }
286             halWrapper.onRemoteTaskRequested(clientId, data);
287         }
288 
289         @Override
getInterfaceHash()290         public String getInterfaceHash() {
291             return IRemoteTaskCallback.HASH;
292         }
293 
294         @Override
getInterfaceVersion()295         public int getInterfaceVersion() {
296             return IRemoteTaskCallback.VERSION;
297         }
298     }
299 }
300