• 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;
18 
19 import static com.android.car.internal.common.CommonConstants.INVALID_PID;
20 import static com.android.car.internal.util.VersionUtils.isPlatformVersionAtLeastU;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.UserIdInt;
25 import android.car.app.CarActivityManager;
26 import android.car.builtin.os.UserManagerHelper;
27 import android.car.builtin.util.Slogf;
28 import android.content.ComponentName;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.os.UserHandle;
32 import android.view.Display;
33 
34 import com.android.car.internal.ICarServiceHelper;
35 import com.android.internal.annotations.GuardedBy;
36 import com.android.internal.annotations.VisibleForTesting;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Objects;
41 
42 /**
43  * Utility wrapper to access {@code ICarServiceHelper} inside car service
44  */
45 public final class CarServiceHelperWrapper {
46 
47     private static final String TAG = CarServiceHelperWrapper.class.getSimpleName();
48 
49     // If car service helper is not available for more than this time, we will throw exception.
50     private static final long CAR_SERVICE_HELPER_WAIT_TIME_MS = 20_000;
51 
52     private static final String REMOTE_EXCEPTION_STR = "CarServiceHelper threw RemoteException";
53 
54     private final long mCarServiceHelperWaitTimeoutMs;
55 
56     private final Object mLock = new Object();
57 
58     @GuardedBy("mLock")
59     @Nullable
60     private ICarServiceHelper mICarServiceHelper;
61 
62     @GuardedBy("mLock")
63     @Nullable
64     private ArrayList<Runnable> mConnectionRunnables;
65 
66     /**
67      * Factory method for {@code ICarImpl}.
68      */
create()69     public static CarServiceHelperWrapper create() {
70         return create(CAR_SERVICE_HELPER_WAIT_TIME_MS);
71     }
72 
73     /** Factory method to create with non-default wait timeout. This is for testing only. */
74     @VisibleForTesting
create(long carServiceHelperWaitTimeoutMs)75     public static CarServiceHelperWrapper create(long carServiceHelperWaitTimeoutMs) {
76         CarServiceHelperWrapper wrapper = new CarServiceHelperWrapper(
77                 carServiceHelperWaitTimeoutMs);
78         CarLocalServices.addService(CarServiceHelperWrapper.class, wrapper);
79         return wrapper;
80     }
81 
82     /**
83      * Notifies CarServiceHelper connection. Only for {@code ICarImpl}.
84      */
setCarServiceHelper(@onNull ICarServiceHelper carServiceHelper)85     public void setCarServiceHelper(@NonNull ICarServiceHelper carServiceHelper) {
86         Objects.requireNonNull(carServiceHelper);
87 
88         ArrayList<Runnable> connectionRunnables;
89         synchronized (mLock) {
90             mICarServiceHelper = carServiceHelper;
91             // This is thread safe as mConnectionRunnables is no longer accessed after connection.
92             connectionRunnables = mConnectionRunnables;
93             mConnectionRunnables = null;
94             mLock.notifyAll();
95         }
96         if (connectionRunnables != null) {
97             for (int i = 0; i < connectionRunnables.size(); i++) {
98                 connectionRunnables.get(i).run();
99             }
100         }
101     }
102 
103     /**
104      * Returns a singleton instance.
105      */
getInstance()106     public static CarServiceHelperWrapper getInstance() {
107         return CarLocalServices.getService(CarServiceHelperWrapper.class);
108     }
109 
110     /**
111      * Runs the passed {@code Runnable} when {@code CarServiceHelper} is connected. If it is already
112      * connected, it will run inside the call.
113      */
runOnConnection(Runnable r)114     public void runOnConnection(Runnable r) {
115         boolean alreadyConnected;
116         synchronized (mLock) {
117             alreadyConnected = mICarServiceHelper != null;
118             if (!alreadyConnected) {
119                 if (mConnectionRunnables == null) {
120                     mConnectionRunnables = new ArrayList<>();
121                 }
122                 mConnectionRunnables.add(r);
123             }
124         }
125         if (alreadyConnected) {
126             r.run();
127         }
128     }
129 
130     /**
131      * See {@code ICarServiceHelper}.
132      */
setDisplayAllowlistForUser(@serIdInt int userId, int[] displayIds)133     public void setDisplayAllowlistForUser(@UserIdInt int userId, int[] displayIds) {
134         try {
135             waitForCarServiceHelper().setDisplayAllowlistForUser(userId, displayIds);
136         } catch (RemoteException e) {
137             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
138         }
139     }
140 
141     /**
142      * See {@code ICarServiceHelper}.
143      */
setPassengerDisplays(int[] displayIds)144     public void setPassengerDisplays(int[] displayIds) {
145         try {
146             waitForCarServiceHelper().setPassengerDisplays(displayIds);
147         } catch (RemoteException e) {
148             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
149         }
150     }
151 
152     /**
153      * See {@code ICarServiceHelper}.
154      */
setSourcePreferredComponents( boolean enableSourcePreferred, List<ComponentName> sourcePreferredComponents)155     public void setSourcePreferredComponents(
156             boolean enableSourcePreferred, List<ComponentName> sourcePreferredComponents) {
157         try {
158             waitForCarServiceHelper().setSourcePreferredComponents(enableSourcePreferred,
159                     sourcePreferredComponents);
160         } catch (RemoteException e) {
161             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
162         }
163     }
164 
165     /**
166      * See {@code ICarServiceHelper}.
167      */
setSafetyMode(boolean safe)168     public void setSafetyMode(boolean safe) {
169         try {
170             waitForCarServiceHelper().setSafetyMode(safe);
171         } catch (RemoteException e) {
172             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
173         }
174     }
175 
176     /**
177      * See {@code ICarServiceHelper}.
178      */
179     @Nullable
createUserEvenWhenDisallowed(String name, String userType, int flags)180     public UserHandle createUserEvenWhenDisallowed(String name, String userType, int flags) {
181         try {
182             return waitForCarServiceHelper().createUserEvenWhenDisallowed(name, userType, flags);
183         } catch (RemoteException e) {
184             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
185         }
186         return null;
187     }
188 
189     /**
190      * See {@code ICarServiceHelper}.
191      */
setPersistentActivity(ComponentName activity, int displayId, int featureId)192     public int setPersistentActivity(ComponentName activity, int displayId, int featureId) {
193         try {
194             return waitForCarServiceHelper().setPersistentActivity(activity, displayId, featureId);
195         } catch (RemoteException e) {
196             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
197         }
198         return CarActivityManager.RESULT_FAILURE;
199     }
200 
201     /**
202      * See {@code ICarServiceHelper}.
203      */
setPersistentActivitiesOnRootTask(List<ComponentName> activities, IBinder rootTaskToken)204     public void setPersistentActivitiesOnRootTask(List<ComponentName> activities,
205             IBinder rootTaskToken) {
206         try {
207             waitForCarServiceHelper().setPersistentActivitiesOnRootTask(activities, rootTaskToken);
208         } catch (RemoteException e) {
209             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
210         }
211     }
212 
213     /**
214      * See {@code ICarServiceHelper}.
215      */
sendInitialUser(UserHandle user)216     public void sendInitialUser(UserHandle user) {
217         try {
218             waitForCarServiceHelper().sendInitialUser(user);
219         } catch (RemoteException e) {
220             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
221         }
222     }
223 
224     /**
225      * See {@code ICarServiceHelper}.
226      */
setProcessGroup(int pid, int group)227     public void setProcessGroup(int pid, int group) {
228         try {
229             waitForCarServiceHelper().setProcessGroup(pid, group);
230         } catch (RemoteException e) {
231             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
232         }
233     }
234 
235     /**
236      * See {@code ICarServiceHelper}.
237      */
getProcessGroup(int pid)238     public int getProcessGroup(int pid) {
239         try {
240             return waitForCarServiceHelper().getProcessGroup(pid);
241         } catch (RemoteException e) {
242             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
243         }
244         return -1; // invalid id
245     }
246 
247     /**
248      * See {@code ICarServiceHelper}.
249      */
getMainDisplayAssignedToUser(int userId)250     public int getMainDisplayAssignedToUser(int userId) {
251         try {
252             return waitForCarServiceHelper().getMainDisplayAssignedToUser(userId);
253         } catch (RemoteException e) {
254             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
255         }
256         return Display.INVALID_DISPLAY;
257     }
258 
259     /**
260      * See {@code ICarServiceHelper}.
261      */
getUserAssignedToDisplay(int displayId)262     public int getUserAssignedToDisplay(int displayId) {
263         try {
264             return waitForCarServiceHelper().getUserAssignedToDisplay(displayId);
265         } catch (RemoteException e) {
266             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
267         }
268         return UserManagerHelper.USER_NULL;
269     }
270 
271     /**
272      * See {@code ICarServiceHelper}.
273      */
startUserInBackgroundVisibleOnDisplay(int userId, int displayId)274     public boolean startUserInBackgroundVisibleOnDisplay(int userId, int displayId) {
275         try {
276             return waitForCarServiceHelper().startUserInBackgroundVisibleOnDisplay(userId,
277                     displayId);
278         } catch (RemoteException e) {
279             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
280         }
281         return false;
282     }
283 
284     /**
285      * See {@code ICarServiceHelper}.
286      */
setProcessProfile(int pid, int uid, @NonNull String profile)287     public void setProcessProfile(int pid, int uid, @NonNull String profile) {
288         try {
289             waitForCarServiceHelper().setProcessProfile(pid, uid, profile);
290         } catch (RemoteException e) {
291             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
292         }
293     }
294 
295     /**
296      * See {@code ICarServiceHelper}.
297      */
fetchAidlVhalPid()298     public int fetchAidlVhalPid() {
299         if (!isPlatformVersionAtLeastU()) {
300             return INVALID_PID;
301         }
302         try {
303             return waitForCarServiceHelper().fetchAidlVhalPid();
304         } catch (RemoteException e) {
305             Slogf.e(TAG, REMOTE_EXCEPTION_STR, e);
306         }
307         return INVALID_PID;
308     }
309 
CarServiceHelperWrapper(long carServiceHelperWaitTimeoutMs)310     private CarServiceHelperWrapper(long carServiceHelperWaitTimeoutMs) {
311         mCarServiceHelperWaitTimeoutMs = carServiceHelperWaitTimeoutMs;
312     }
313 
314     @SuppressWarnings("WaitNotInLoop")
waitForCarServiceHelper()315     private ICarServiceHelper waitForCarServiceHelper() {
316         synchronized (mLock) {
317             if (mICarServiceHelper == null) {
318                 try {
319                     // This only wait once as timeout will crash the car service with exception.
320                     mLock.wait(mCarServiceHelperWaitTimeoutMs);
321                 } catch (InterruptedException e) {
322                     Thread.currentThread().interrupt();
323                     Slogf.w(TAG, "Thread was interrupted while waiting for CarServiceHelper",
324                             e);
325                     // just continue as we cannot return null instance.
326                 }
327                 if (mICarServiceHelper == null) {
328                     throw new IllegalStateException("CarServiceHelper did not connect");
329                 }
330             }
331             return mICarServiceHelper;
332         }
333     }
334 }
335