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