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