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 */ sendInitialUser(UserHandle user)202 public void sendInitialUser(UserHandle user) { 203 try { 204 waitForCarServiceHelper().sendInitialUser(user); 205 } catch (RemoteException e) { 206 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 207 } 208 } 209 210 /** 211 * See {@code ICarServiceHelper}. 212 */ setProcessGroup(int pid, int group)213 public void setProcessGroup(int pid, int group) { 214 try { 215 waitForCarServiceHelper().setProcessGroup(pid, group); 216 } catch (RemoteException e) { 217 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 218 } 219 } 220 221 /** 222 * See {@code ICarServiceHelper}. 223 */ getProcessGroup(int pid)224 public int getProcessGroup(int pid) { 225 try { 226 return waitForCarServiceHelper().getProcessGroup(pid); 227 } catch (RemoteException e) { 228 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 229 } 230 return -1; // invalid id 231 } 232 233 /** 234 * See {@code ICarServiceHelper}. 235 */ getMainDisplayAssignedToUser(int userId)236 public int getMainDisplayAssignedToUser(int userId) { 237 try { 238 return waitForCarServiceHelper().getMainDisplayAssignedToUser(userId); 239 } catch (RemoteException e) { 240 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 241 } 242 return Display.INVALID_DISPLAY; 243 } 244 245 /** 246 * See {@code ICarServiceHelper}. 247 */ getUserAssignedToDisplay(int displayId)248 public int getUserAssignedToDisplay(int displayId) { 249 try { 250 return waitForCarServiceHelper().getUserAssignedToDisplay(displayId); 251 } catch (RemoteException e) { 252 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 253 } 254 return UserManagerHelper.USER_NULL; 255 } 256 257 /** 258 * See {@code ICarServiceHelper}. 259 */ startUserInBackgroundVisibleOnDisplay(int userId, int displayId)260 public boolean startUserInBackgroundVisibleOnDisplay(int userId, int displayId) { 261 try { 262 return waitForCarServiceHelper().startUserInBackgroundVisibleOnDisplay(userId, 263 displayId); 264 } catch (RemoteException e) { 265 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 266 } 267 return false; 268 } 269 270 /** 271 * See {@code ICarServiceHelper}. 272 */ setProcessProfile(int pid, int uid, @NonNull String profile)273 public void setProcessProfile(int pid, int uid, @NonNull String profile) { 274 try { 275 waitForCarServiceHelper().setProcessProfile(pid, uid, profile); 276 } catch (RemoteException e) { 277 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 278 } 279 } 280 281 /** 282 * See {@code ICarServiceHelper}. 283 */ fetchAidlVhalPid()284 public int fetchAidlVhalPid() { 285 try { 286 return waitForCarServiceHelper().fetchAidlVhalPid(); 287 } catch (RemoteException e) { 288 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 289 } 290 return INVALID_PID; 291 } 292 293 /** 294 * @return true if a package requires launching in automotive compatibility mode. 295 */ requiresDisplayCompat(String packageName)296 public boolean requiresDisplayCompat(String packageName) { 297 try { 298 return waitForCarServiceHelper().requiresDisplayCompat(packageName); 299 } catch (RemoteException e) { 300 Slogf.e(TAG, REMOTE_EXCEPTION_STR, e); 301 } 302 return false; 303 } 304 CarServiceHelperWrapper(long carServiceHelperWaitTimeoutMs)305 private CarServiceHelperWrapper(long carServiceHelperWaitTimeoutMs) { 306 mCarServiceHelperWaitTimeoutMs = carServiceHelperWaitTimeoutMs; 307 } 308 309 @SuppressWarnings("WaitNotInLoop") waitForCarServiceHelper()310 private ICarServiceHelper waitForCarServiceHelper() { 311 synchronized (mLock) { 312 if (mICarServiceHelper == null) { 313 try { 314 // This only wait once as timeout will crash the car service with exception. 315 mLock.wait(mCarServiceHelperWaitTimeoutMs); 316 } catch (InterruptedException e) { 317 Thread.currentThread().interrupt(); 318 Slogf.w(TAG, "Thread was interrupted while waiting for CarServiceHelper", 319 e); 320 // just continue as we cannot return null instance. 321 } 322 if (mICarServiceHelper == null) { 323 throw new IllegalStateException("CarServiceHelper did not connect"); 324 } 325 } 326 return mICarServiceHelper; 327 } 328 } 329 } 330