blob: 1557e6eb6a0ff0fdbf21938e5a6c72f43161032d [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.car.drivingstate;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.car.Car;
import android.car.CarManagerBase;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* API to register and get the User Experience restrictions imposed based on the car's driving
* state.
*/
public final class CarUxRestrictionsManager extends CarManagerBase {
private static final String TAG = "CarUxRManager";
private static final boolean DBG = false;
private static final boolean VDBG = false;
private static final int MSG_HANDLE_UX_RESTRICTIONS_CHANGE = 0;
/**
* Baseline restriction mode is the default UX restrictions used for driving state.
*
* @hide
*/
public static final String UX_RESTRICTION_MODE_BASELINE = "baseline";
private int mDisplayId = Display.INVALID_DISPLAY;
private final ICarUxRestrictionsManager mUxRService;
private final EventCallbackHandler mEventCallbackHandler;
@GuardedBy("this")
private OnUxRestrictionsChangedListener mUxRListener;
private CarUxRestrictionsChangeListenerToService mListenerToService;
/** @hide */
public CarUxRestrictionsManager(Car car, IBinder service) {
super(car);
mUxRService = ICarUxRestrictionsManager.Stub.asInterface(service);
mEventCallbackHandler = new EventCallbackHandler(this,
getEventHandler().getLooper());
}
/** @hide */
@Override
public void onCarDisconnected() {
mListenerToService = null;
synchronized (this) {
mUxRListener = null;
}
}
/**
* Listener Interface for clients to implement to get updated on driving state related
* changes.
*/
public interface OnUxRestrictionsChangedListener {
/**
* Called when the UX restrictions due to a car's driving state changes.
*
* @param restrictionInfo The new UX restriction information
*/
void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo);
}
/**
* Registers a {@link OnUxRestrictionsChangedListener} for listening to changes in the
* UX Restrictions to adhere to.
* <p>
* If a listener has already been registered, it has to be unregistered before registering
* the new one.
*
* @param listener {@link OnUxRestrictionsChangedListener}
*/
public void registerListener(@NonNull OnUxRestrictionsChangedListener listener) {
registerListener(listener, getDisplayId());
}
/**
* @hide
*/
public void registerListener(@NonNull OnUxRestrictionsChangedListener listener, int displayId) {
synchronized (this) {
// Check if the listener has been already registered.
if (mUxRListener != null) {
if (DBG) {
Log.d(TAG, "Listener already registered listener");
}
return;
}
mUxRListener = listener;
}
try {
if (mListenerToService == null) {
mListenerToService = new CarUxRestrictionsChangeListenerToService(this);
}
// register to the Service to listen for changes.
mUxRService.registerUxRestrictionsChangeListener(mListenerToService, displayId);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
}
/**
* Unregisters the registered {@link OnUxRestrictionsChangedListener}
*/
public void unregisterListener() {
synchronized (this) {
if (mUxRListener == null) {
if (DBG) {
Log.d(TAG, "Listener was not previously registered");
}
return;
}
mUxRListener = null;
}
try {
mUxRService.unregisterUxRestrictionsChangeListener(mListenerToService);
} catch (RemoteException e) {
handleRemoteExceptionFromCarService(e);
}
}
/**
* Sets new {@link CarUxRestrictionsConfiguration}s for next trip.
* <p>
* Saving new configurations does not affect current configuration. The new configuration will
* only be used after UX Restrictions service restarts when the vehicle is parked.
* <p>
* Input configurations must be one-to-one mapped to displays, namely each display must have
* exactly one configuration.
* See {@link CarUxRestrictionsConfiguration.Builder#setDisplayAddress(DisplayAddress)}.
*
* @param configs Map of display Id to UX restrictions configurations to be persisted.
* @return {@code true} if input config was successfully saved; {@code false} otherwise.
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
public boolean saveUxRestrictionsConfigurationForNextBoot(
List<CarUxRestrictionsConfiguration> configs) {
try {
return mUxRService.saveUxRestrictionsConfigurationForNextBoot(configs);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, false);
}
}
/**
* Gets the current UX restrictions ({@link CarUxRestrictions}) in place.
*
* @return current UX restrictions that is in effect.
*/
@Nullable
public CarUxRestrictions getCurrentCarUxRestrictions() {
return getCurrentCarUxRestrictions(getDisplayId());
}
/**
* @hide
*/
@Nullable
public CarUxRestrictions getCurrentCarUxRestrictions(int displayId) {
try {
return mUxRService.getCurrentUxRestrictions(displayId);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, null);
}
}
/**
* Sets restriction mode. Returns {@code true} if the operation succeeds.
*
* <p>The default mode is {@link #UX_RESTRICTION_MODE_BASELINE}.
*
* <p>If a new {@link CarUxRestrictions} is available upon mode transition, it'll
* be immediately dispatched to listeners.
*
* <p>If the given mode is not configured for current driving state, it
* will fall back to the default value.
*
* <p>If a configuration was set for a passenger mode before an upgrade to Android R, that
* passenger configuration is now called "passenger".
*
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
public boolean setRestrictionMode(@NonNull String mode) {
Objects.requireNonNull(mode, "mode must not be null");
try {
return mUxRService.setRestrictionMode(mode);
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, false);
}
}
/**
* Returns the current restriction mode.
*
* <p>The default mode is {@link #UX_RESTRICTION_MODE_BASELINE}.
*
* <p>If a configuration was set for a passenger mode before an upgrade to Android R, that
* passenger configuration is now called "passenger".
*
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
@NonNull
public String getRestrictionMode() {
try {
return mUxRService.getRestrictionMode();
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, null);
}
}
/**
* Sets a new {@link CarUxRestrictionsConfiguration} for next trip.
* <p>
* Saving a new configuration does not affect current configuration. The new configuration will
* only be used after UX Restrictions service restarts when the vehicle is parked.
*
* @param config UX restrictions configuration to be persisted.
* @return {@code true} if input config was successfully saved; {@code false} otherwise.
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
public boolean saveUxRestrictionsConfigurationForNextBoot(
CarUxRestrictionsConfiguration config) {
return saveUxRestrictionsConfigurationForNextBoot(Arrays.asList(config));
}
/**
* Gets the staged configurations.
* <p>
* Configurations set by {@link #saveUxRestrictionsConfigurationForNextBoot(List)} do not
* immediately affect current drive. Instead, they are staged to take effect when car service
* boots up the next time.
* <p>
* This methods is only for test purpose, please do not use in production.
*
* @return current staged configuration, {@code null} if it's not available
* @hide
*/
@Nullable
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
public List<CarUxRestrictionsConfiguration> getStagedConfigs() {
try {
return mUxRService.getStagedConfigs();
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, null);
}
}
/**
* Gets the current configurations.
*
* @return current configurations that is in effect.
* @hide
*/
@RequiresPermission(value = Car.PERMISSION_CAR_UX_RESTRICTIONS_CONFIGURATION)
public List<CarUxRestrictionsConfiguration> getConfigs() {
try {
return mUxRService.getConfigs();
} catch (RemoteException e) {
return handleRemoteExceptionFromCarService(e, null);
}
}
/**
* Class that implements the listener interface and gets called back from the
* {@link com.android.car.CarDrivingStateService} across the binder interface.
*/
private static class CarUxRestrictionsChangeListenerToService extends
ICarUxRestrictionsChangeListener.Stub {
private final WeakReference<CarUxRestrictionsManager> mUxRestrictionsManager;
public CarUxRestrictionsChangeListenerToService(CarUxRestrictionsManager manager) {
mUxRestrictionsManager = new WeakReference<>(manager);
}
@Override
public void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo) {
CarUxRestrictionsManager manager = mUxRestrictionsManager.get();
if (manager != null) {
manager.handleUxRestrictionsChanged(restrictionInfo);
}
}
}
/**
* Gets the {@link CarUxRestrictions} from the service listener
* {@link CarUxRestrictionsChangeListenerToService} and dispatches it to a handler provided
* to the manager.
*
* @param restrictionInfo {@link CarUxRestrictions} that has been registered to listen on
*/
private void handleUxRestrictionsChanged(CarUxRestrictions restrictionInfo) {
// send a message to the handler
mEventCallbackHandler.sendMessage(mEventCallbackHandler.obtainMessage(
MSG_HANDLE_UX_RESTRICTIONS_CHANGE, restrictionInfo));
}
/**
* Callback Handler to handle dispatching the UX restriction changes to the corresponding
* listeners.
*/
private static final class EventCallbackHandler extends Handler {
private final WeakReference<CarUxRestrictionsManager> mUxRestrictionsManager;
public EventCallbackHandler(CarUxRestrictionsManager manager, Looper looper) {
super(looper);
mUxRestrictionsManager = new WeakReference<>(manager);
}
@Override
public void handleMessage(Message msg) {
CarUxRestrictionsManager mgr = mUxRestrictionsManager.get();
if (mgr != null) {
mgr.dispatchUxRChangeToClient((CarUxRestrictions) msg.obj);
}
}
}
/**
* Checks for the listeners to list of {@link CarUxRestrictions} and calls them back
* in the callback handler thread.
*
* @param restrictionInfo {@link CarUxRestrictions}
*/
private void dispatchUxRChangeToClient(CarUxRestrictions restrictionInfo) {
if (restrictionInfo == null) {
return;
}
synchronized (this) {
if (mUxRListener != null) {
mUxRListener.onUxRestrictionsChanged(restrictionInfo);
}
}
}
private int getDisplayId() {
if (mDisplayId != Display.INVALID_DISPLAY) {
return mDisplayId;
}
mDisplayId = getContext().getDisplayId();
Log.i(TAG, "Context returns display ID " + mDisplayId);
if (mDisplayId == Display.INVALID_DISPLAY) {
mDisplayId = Display.DEFAULT_DISPLAY;
Log.e(TAG, "Could not retrieve display id. Using default: " + mDisplayId);
}
return mDisplayId;
}
}