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 17package android.car.drivingstate; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.car.Car; 22import android.car.CarManagerBase; 23import android.car.CarNotConnectedException; 24import android.car.drivingstate.ICarUxRestrictionsManager; 25import android.content.Context; 26import android.os.Handler; 27import android.os.IBinder; 28import android.os.Looper; 29import android.os.Message; 30import android.os.RemoteException; 31import android.util.Log; 32 33import java.lang.ref.WeakReference; 34 35/** 36 * API to register and get the User Experience restrictions imposed based on the car's driving 37 * state. 38 */ 39public final class CarUxRestrictionsManager implements CarManagerBase { 40 private static final String TAG = "CarUxRManager"; 41 private static final boolean DBG = false; 42 private static final boolean VDBG = false; 43 private static final int MSG_HANDLE_UX_RESTRICTIONS_CHANGE = 0; 44 45 private final Context mContext; 46 private final ICarUxRestrictionsManager mUxRService; 47 private final EventCallbackHandler mEventCallbackHandler; 48 private OnUxRestrictionsChangedListener mUxRListener; 49 private CarUxRestrictionsChangeListenerToService mListenerToService; 50 51 52 /** @hide */ 53 public CarUxRestrictionsManager(IBinder service, Context context, Handler handler) { 54 mContext = context; 55 mUxRService = ICarUxRestrictionsManager.Stub.asInterface(service); 56 mEventCallbackHandler = new EventCallbackHandler(this, handler.getLooper()); 57 } 58 59 /** @hide */ 60 @Override 61 public synchronized void onCarDisconnected() { 62 mListenerToService = null; 63 mUxRListener = null; 64 } 65 66 /** 67 * Listener Interface for clients to implement to get updated on driving state related 68 * changes. 69 */ 70 public interface OnUxRestrictionsChangedListener { 71 /** 72 * Called when the UX restrictions due to a car's driving state changes. 73 * 74 * @param restrictionInfo The new UX restriction information 75 */ 76 void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo); 77 } 78 79 /** 80 * Register a {@link OnUxRestrictionsChangedListener} for listening to changes in the 81 * UX Restrictions to adhere to. 82 * <p> 83 * If a listener has already been registered, it has to be unregistered before registering 84 * the new one. 85 * 86 * @param listener {@link OnUxRestrictionsChangedListener} 87 */ 88 public synchronized void registerListener(@NonNull OnUxRestrictionsChangedListener listener) 89 throws CarNotConnectedException, IllegalArgumentException { 90 if (listener == null) { 91 if (VDBG) { 92 Log.v(TAG, "registerListener(): null listener"); 93 } 94 throw new IllegalArgumentException("Listener is null"); 95 } 96 // Check if the listener has been already registered. 97 if (mUxRListener != null) { 98 if (DBG) { 99 Log.d(TAG, "Listener already registered listener"); 100 } 101 return; 102 } 103 mUxRListener = listener; 104 try { 105 if (mListenerToService == null) { 106 mListenerToService = new CarUxRestrictionsChangeListenerToService(this); 107 } 108 // register to the Service to listen for changes. 109 mUxRService.registerUxRestrictionsChangeListener(mListenerToService); 110 } catch (RemoteException e) { 111 Log.e(TAG, "Could not register a listener to CarUxRestrictionsManagerService " + e); 112 throw new CarNotConnectedException(e); 113 } catch (IllegalStateException e) { 114 Log.e(TAG, "Could not register a listener to CarUxRestrictionsManagerService " + e); 115 Car.checkCarNotConnectedExceptionFromCarService(e); 116 } 117 } 118 119 /** 120 * Unregister the registered {@link OnUxRestrictionsChangedListener} 121 */ 122 public synchronized void unregisterListener() 123 throws CarNotConnectedException { 124 if (mUxRListener == null) { 125 if (DBG) { 126 Log.d(TAG, "Listener was not previously registered"); 127 } 128 return; 129 } 130 try { 131 mUxRService.unregisterUxRestrictionsChangeListener(mListenerToService); 132 mUxRListener = null; 133 } catch (RemoteException e) { 134 Log.e(TAG, "Could not unregister listener from Driving State Service " + e); 135 throw new CarNotConnectedException(e); 136 } 137 } 138 139 /** 140 * Get the current UX restrictions {@link CarUxRestrictions} in place. 141 * 142 * @return current UX restrictions that is in effect. 143 */ 144 @Nullable 145 public CarUxRestrictions getCurrentCarUxRestrictions() 146 throws CarNotConnectedException { 147 try { 148 return mUxRService.getCurrentUxRestrictions(); 149 } catch (RemoteException e) { 150 Log.e(TAG, "Could not get current UX restrictions " + e); 151 throw new CarNotConnectedException(e); 152 } 153 } 154 155 /** 156 * Class that implements the listener interface and gets called back from the 157 * {@link com.android.car.CarDrivingStateService} across the binder interface. 158 */ 159 private static class CarUxRestrictionsChangeListenerToService extends 160 ICarUxRestrictionsChangeListener.Stub { 161 private final WeakReference<CarUxRestrictionsManager> mUxRestrictionsManager; 162 163 public CarUxRestrictionsChangeListenerToService(CarUxRestrictionsManager manager) { 164 mUxRestrictionsManager = new WeakReference<>(manager); 165 } 166 167 @Override 168 public void onUxRestrictionsChanged(CarUxRestrictions restrictionInfo) { 169 CarUxRestrictionsManager manager = mUxRestrictionsManager.get(); 170 if (manager != null) { 171 manager.handleUxRestrictionsChanged(restrictionInfo); 172 } 173 } 174 } 175 176 /** 177 * Gets the {@link CarUxRestrictions} from the service listener 178 * {@link CarUxRestrictionsChangeListenerToService} and dispatches it to a handler provided 179 * to the manager 180 * 181 * @param restrictionInfo {@link CarUxRestrictions} that has been registered to listen on 182 */ 183 private void handleUxRestrictionsChanged(CarUxRestrictions restrictionInfo) { 184 // send a message to the handler 185 mEventCallbackHandler.sendMessage(mEventCallbackHandler.obtainMessage( 186 MSG_HANDLE_UX_RESTRICTIONS_CHANGE, restrictionInfo)); 187 } 188 189 /** 190 * Callback Handler to handle dispatching the UX restriction changes to the corresponding 191 * listeners 192 */ 193 private static final class EventCallbackHandler extends Handler { 194 private final WeakReference<CarUxRestrictionsManager> mUxRestrictionsManager; 195 196 public EventCallbackHandler(CarUxRestrictionsManager manager, Looper looper) { 197 super(looper); 198 mUxRestrictionsManager = new WeakReference<>(manager); 199 } 200 201 @Override 202 public void handleMessage(Message msg) { 203 CarUxRestrictionsManager mgr = mUxRestrictionsManager.get(); 204 if (mgr != null) { 205 mgr.dispatchUxRChangeToClient((CarUxRestrictions) msg.obj); 206 } 207 } 208 209 } 210 211 /** 212 * Checks for the listeners to list of {@link CarUxRestrictions} and calls them back 213 * in the callback handler thread 214 * 215 * @param restrictionInfo {@link CarUxRestrictions} 216 */ 217 private void dispatchUxRChangeToClient(CarUxRestrictions restrictionInfo) { 218 if (restrictionInfo == null) { 219 return; 220 } 221 OnUxRestrictionsChangedListener listener; 222 synchronized (this) { 223 listener = mUxRListener; 224 } 225 if (listener != null) { 226 listener.onUxRestrictionsChanged(restrictionInfo); 227 } 228 } 229} 230