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