1 /* 2 * Copyright (C) 2018 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 android.car.drivingstate; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SystemApi; 23 import android.car.Car; 24 import android.car.CarManagerBase; 25 import android.car.builtin.content.ContextHelper; 26 import android.car.builtin.os.UserManagerHelper; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.RemoteException; 32 import android.os.UserManager; 33 import android.util.Slog; 34 import android.view.Display; 35 36 import com.android.internal.annotations.GuardedBy; 37 38 import java.lang.ref.WeakReference; 39 import java.util.Arrays; 40 import java.util.List; 41 import java.util.Objects; 42 43 /** 44 * API to register and get the User Experience restrictions imposed based on the car's driving 45 * state. 46 */ 47 public final class CarUxRestrictionsManager extends CarManagerBase { 48 private static final String TAG = "CarUxRManager"; 49 private static final boolean DBG = false; 50 private static final boolean VDBG = false; 51 private static final int MSG_HANDLE_UX_RESTRICTIONS_CHANGE = 0; 52 53 /** 54 * Baseline restriction mode is the default UX restrictions used for driving state. 55 * 56 * @hide 57 */ 58 @SystemApi 59 public static final String UX_RESTRICTION_MODE_BASELINE = "baseline"; 60 61 private final Object mLock = new Object(); 62 63 private int mDisplayId = Display.INVALID_DISPLAY; 64 private final ICarUxRestrictionsManager mUxRService; 65 private final EventCallbackHandler mEventCallbackHandler; 66 private final UserManager mUserManager; 67 @GuardedBy("mLock") 68 private OnUxRestrictionsChangedListener mUxRListener; 69 @GuardedBy("mLock") 70 private CarUxRestrictionsChangeListenerToService mListenerToService; 71 72 /** @hide */ CarUxRestrictionsManager(@onNull Car car, @NonNull IBinder service)73 public CarUxRestrictionsManager(@NonNull Car car, @NonNull IBinder service) { 74 super(car); 75 mUxRService = ICarUxRestrictionsManager.Stub.asInterface(service); 76 mEventCallbackHandler = new EventCallbackHandler(this, 77 getEventHandler().getLooper()); 78 mUserManager = getContext().getSystemService(UserManager.class); 79 } 80 81 /** @hide */ 82 @Override 83 @SystemApi onCarDisconnected()84 public void onCarDisconnected() { 85 synchronized (mLock) { 86 mListenerToService = null; 87 mUxRListener = null; 88 } 89 } 90 91 /** 92 * Listener Interface for clients to implement to get updated on driving state related 93 * changes. 94 */ 95 public interface OnUxRestrictionsChangedListener { 96 /** 97 * Called when the UX restrictions due to a car's driving state changes. 98 * 99 * @param restrictionInfo The new UX restriction information 100 */ onUxRestrictionsChanged(CarUxRestrictions restrictionInfo)101 void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo); 102 } 103 104 /** 105 * Registers a {@link OnUxRestrictionsChangedListener} for listening to changes in the 106 * UX Restrictions to adhere to. 107 * <p> 108 * If a listener has already been registered, it has to be unregistered before registering 109 * the new one. 110 * 111 * @param listener {@link OnUxRestrictionsChangedListener} 112 */ registerListener(@onNull OnUxRestrictionsChangedListener listener)113 public void registerListener(@NonNull OnUxRestrictionsChangedListener listener) { 114 registerListener(listener, getDisplayId()); 115 } 116 117 /** 118 * @hide 119 */ 120 @Deprecated registerListener(@onNull OnUxRestrictionsChangedListener listener, int displayId)121 public void registerListener(@NonNull OnUxRestrictionsChangedListener listener, int displayId) { 122 setListener(displayId, listener); 123 } 124 125 /** 126 * @hide 127 * @deprecated use {@link CarUxRestrictionsManager#registerListener} instead. 128 */ 129 @SystemApi 130 @Deprecated setListener(int displayId, @NonNull OnUxRestrictionsChangedListener listener)131 public void setListener(int displayId, @NonNull OnUxRestrictionsChangedListener listener) { 132 CarUxRestrictionsChangeListenerToService serviceListener; 133 synchronized (mLock) { 134 // Check if the listener has been already registered. 135 if (mUxRListener != null) { 136 if (DBG) { 137 Slog.d(TAG, "Listener already registered listener"); 138 } 139 return; 140 } 141 mUxRListener = listener; 142 if (mListenerToService == null) { 143 mListenerToService = new CarUxRestrictionsChangeListenerToService(this); 144 } 145 serviceListener = mListenerToService; 146 } 147 148 try { 149 // register to the Service to listen for changes. 150 mUxRService.registerUxRestrictionsChangeListener(serviceListener, displayId); 151 } catch (RemoteException e) { 152 handleRemoteExceptionFromCarService(e); 153 } 154 } 155 156 /** 157 * Unregisters the registered {@link OnUxRestrictionsChangedListener} 158 */ unregisterListener()159 public void unregisterListener() { 160 CarUxRestrictionsChangeListenerToService serviceListener; 161 synchronized (mLock) { 162 if (mUxRListener == null) { 163 if (DBG) { 164 Slog.d(TAG, "Listener was not previously registered"); 165 } 166 return; 167 } 168 mUxRListener = null; 169 serviceListener = mListenerToService; 170 } 171 try { 172 if (serviceListener != null) { 173 mUxRService.unregisterUxRestrictionsChangeListener(serviceListener); 174 } 175 } catch (RemoteException e) { 176 handleRemoteExceptionFromCarService(e); 177 } 178 } 179 180 /** 181 * Sets new {@link CarUxRestrictionsConfiguration}s for next trip. 182 * <p> 183 * Saving new configurations does not affect current configuration. The new configuration will 184 * only be used after UX Restrictions service restarts when the vehicle is parked. 185 * <p> 186 * Input configurations must be one-to-one mapped to displays, namely each display must have 187 * exactly one configuration. 188 * See {@link CarUxRestrictionsConfiguration.Builder#setDisplayAddress(DisplayAddress)}. 189 * 190 * @param configs Map of display Id to UX restrictions configurations to be persisted. 191 * @return {@code true} if input config was successfully saved; {@code false} otherwise. 192 * @hide 193 */ 194 @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION) 195 @SystemApi saveUxRestrictionsConfigurationForNextBoot( @onNull List<CarUxRestrictionsConfiguration> configs)196 public boolean saveUxRestrictionsConfigurationForNextBoot( 197 @NonNull List<CarUxRestrictionsConfiguration> configs) { 198 try { 199 return mUxRService.saveUxRestrictionsConfigurationForNextBoot(configs); 200 } catch (RemoteException e) { 201 return handleRemoteExceptionFromCarService(e, false); 202 } 203 } 204 205 /** 206 * Gets the current UX restrictions ({@link CarUxRestrictions}) in place. 207 * 208 * @return current UX restrictions that is in effect. 209 */ 210 @Nullable getCurrentCarUxRestrictions()211 public CarUxRestrictions getCurrentCarUxRestrictions() { 212 return getCurrentCarUxRestrictions(getDisplayId()); 213 } 214 215 /** 216 * @hide 217 */ 218 @Nullable 219 @SystemApi getCurrentCarUxRestrictions(int displayId)220 public CarUxRestrictions getCurrentCarUxRestrictions(int displayId) { 221 try { 222 return mUxRService.getCurrentUxRestrictions(displayId); 223 } catch (RemoteException e) { 224 return handleRemoteExceptionFromCarService(e, null); 225 } 226 } 227 228 /** 229 * Sets restriction mode. Returns {@code true} if the operation succeeds. 230 * 231 * <p>The default mode is {@link #UX_RESTRICTION_MODE_BASELINE}. 232 * 233 * <p>If a new {@link CarUxRestrictions} is available upon mode transition, it'll 234 * be immediately dispatched to listeners. 235 * 236 * <p>If the given mode is not configured for current driving state, it 237 * will fall back to the default value. 238 * 239 * <p>If a configuration was set for a passenger mode before an upgrade to Android R, that 240 * passenger configuration is now called "passenger". 241 * 242 * @hide 243 */ 244 @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION) 245 @SystemApi setRestrictionMode(@onNull String mode)246 public boolean setRestrictionMode(@NonNull String mode) { 247 Objects.requireNonNull(mode, "mode must not be null"); 248 try { 249 return mUxRService.setRestrictionMode(mode); 250 } catch (RemoteException e) { 251 return handleRemoteExceptionFromCarService(e, false); 252 } 253 } 254 255 /** 256 * Returns the current restriction mode. 257 * 258 * <p>The default mode is {@link #UX_RESTRICTION_MODE_BASELINE}. 259 * 260 * <p>If a configuration was set for a passenger mode before an upgrade to Android R, that 261 * passenger configuration is now called "passenger". 262 * 263 * @hide 264 */ 265 @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION) 266 @NonNull 267 @SystemApi getRestrictionMode()268 public String getRestrictionMode() { 269 try { 270 return mUxRService.getRestrictionMode(); 271 } catch (RemoteException e) { 272 return handleRemoteExceptionFromCarService(e, null); 273 } 274 } 275 276 /** 277 * Sets a new {@link CarUxRestrictionsConfiguration} for next trip. 278 * <p> 279 * Saving a new configuration does not affect current configuration. The new configuration will 280 * only be used after UX Restrictions service restarts when the vehicle is parked. 281 * 282 * @param config UX restrictions configuration to be persisted. 283 * @return {@code true} if input config was successfully saved; {@code false} otherwise. 284 * @hide 285 */ 286 @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION) 287 @SystemApi saveUxRestrictionsConfigurationForNextBoot( @onNull CarUxRestrictionsConfiguration config)288 public boolean saveUxRestrictionsConfigurationForNextBoot( 289 @NonNull CarUxRestrictionsConfiguration config) { 290 return saveUxRestrictionsConfigurationForNextBoot(Arrays.asList(config)); 291 } 292 293 /** 294 * Gets the staged configurations. 295 * <p> 296 * Configurations set by {@link #saveUxRestrictionsConfigurationForNextBoot(List)} do not 297 * immediately affect current drive. Instead, they are staged to take effect when car service 298 * boots up the next time. 299 * <p> 300 * This methods is only for test purpose, please do not use in production. 301 * 302 * @return current staged configuration, {@code null} if it's not available 303 * @hide 304 */ 305 @Nullable 306 @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION) 307 @SystemApi getStagedConfigs()308 public List<CarUxRestrictionsConfiguration> getStagedConfigs() { 309 try { 310 return mUxRService.getStagedConfigs(); 311 } catch (RemoteException e) { 312 return handleRemoteExceptionFromCarService(e, null); 313 } 314 } 315 316 /** 317 * Gets the current configurations. 318 * 319 * @return current configurations that is in effect. 320 * @hide 321 */ 322 @Nullable 323 @RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION) 324 @SystemApi getConfigs()325 public List<CarUxRestrictionsConfiguration> getConfigs() { 326 try { 327 return mUxRService.getConfigs(); 328 } catch (RemoteException e) { 329 return handleRemoteExceptionFromCarService(e, null); 330 } 331 } 332 333 /** 334 * Class that implements the listener interface and gets called back from the 335 * {@link com.android.car.CarDrivingStateService} across the binder interface. 336 */ 337 private static class CarUxRestrictionsChangeListenerToService extends 338 ICarUxRestrictionsChangeListener.Stub { 339 private final WeakReference<CarUxRestrictionsManager> mUxRestrictionsManager; 340 CarUxRestrictionsChangeListenerToService(CarUxRestrictionsManager manager)341 CarUxRestrictionsChangeListenerToService(CarUxRestrictionsManager manager) { 342 mUxRestrictionsManager = new WeakReference<>(manager); 343 } 344 345 @Override onUxRestrictionsChanged(CarUxRestrictions restrictionInfo)346 public void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo) { 347 CarUxRestrictionsManager manager = mUxRestrictionsManager.get(); 348 if (manager != null) { 349 manager.handleUxRestrictionsChanged(restrictionInfo); 350 } 351 } 352 } 353 354 /** 355 * Gets the {@link CarUxRestrictions} from the service listener 356 * {@link CarUxRestrictionsChangeListenerToService} and dispatches it to a handler provided 357 * to the manager. 358 * 359 * @param restrictionInfo {@link CarUxRestrictions} that has been registered to listen on 360 */ handleUxRestrictionsChanged(CarUxRestrictions restrictionInfo)361 private void handleUxRestrictionsChanged(CarUxRestrictions restrictionInfo) { 362 // send a message to the handler 363 mEventCallbackHandler.sendMessage(mEventCallbackHandler.obtainMessage( 364 MSG_HANDLE_UX_RESTRICTIONS_CHANGE, restrictionInfo)); 365 } 366 367 /** 368 * Callback Handler to handle dispatching the UX restriction changes to the corresponding 369 * listeners. 370 */ 371 private static final class EventCallbackHandler extends Handler { 372 private final WeakReference<CarUxRestrictionsManager> mUxRestrictionsManager; 373 EventCallbackHandler(CarUxRestrictionsManager manager, Looper looper)374 EventCallbackHandler(CarUxRestrictionsManager manager, Looper looper) { 375 super(looper); 376 mUxRestrictionsManager = new WeakReference<>(manager); 377 } 378 379 @Override handleMessage(Message msg)380 public void handleMessage(Message msg) { 381 CarUxRestrictionsManager mgr = mUxRestrictionsManager.get(); 382 if (mgr != null) { 383 mgr.dispatchUxRChangeToClient((CarUxRestrictions) msg.obj); 384 } 385 } 386 } 387 388 /** 389 * Checks for the listeners to list of {@link CarUxRestrictions} and calls them back 390 * in the callback handler thread. 391 * 392 * @param restrictionInfo {@link CarUxRestrictions} 393 */ dispatchUxRChangeToClient(CarUxRestrictions restrictionInfo)394 private void dispatchUxRChangeToClient(CarUxRestrictions restrictionInfo) { 395 if (restrictionInfo == null) { 396 return; 397 } 398 OnUxRestrictionsChangedListener listener; 399 synchronized (mLock) { 400 listener = mUxRListener; 401 } 402 if (listener != null) { 403 listener.onUxRestrictionsChanged(restrictionInfo); 404 } 405 } 406 getDisplayId()407 private int getDisplayId() { 408 if (mDisplayId != Display.INVALID_DISPLAY) { 409 return mDisplayId; 410 } 411 412 // First, attempt to get the id of the display asssociated with the context. 413 // For example, if it is an Activity context, a valid display id will already be obtained 414 // here. But if it is an Application context, it will return invalid display id. 415 mDisplayId = ContextHelper.getAssociatedDisplayId(getContext()); 416 Slog.d(TAG, "Context returns associated display ID " + mDisplayId); 417 418 if (mDisplayId == Display.INVALID_DISPLAY) { 419 // If there is no display id associated with the context, further obtain the display 420 // id by mapping the user to display id. 421 mDisplayId = UserManagerHelper.getMainDisplayIdAssignedToUser(mUserManager); 422 Slog.d(TAG, "Display ID assigned to user is display " + mDisplayId); 423 } 424 425 if (mDisplayId == Display.INVALID_DISPLAY) { 426 mDisplayId = Display.DEFAULT_DISPLAY; 427 Slog.e(TAG, "Could not retrieve display id. Using default: " + mDisplayId); 428 } 429 430 return mDisplayId; 431 } 432 } 433