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