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.systemui.car.users; 18 19 import static android.car.CarOccupantZoneManager.DISPLAY_TYPE_MAIN; 20 import static android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS; 21 22 import static com.android.systemui.car.users.CarSystemUIUserUtil.isCurrentSystemUIDisplay; 23 import static com.android.systemui.car.users.CarSystemUIUserUtil.isMUMDSystemUI; 24 25 import android.car.Car; 26 import android.car.CarOccupantZoneManager; 27 import android.content.Context; 28 import android.hardware.display.DisplayManager; 29 import android.os.Handler; 30 import android.view.Display; 31 32 import androidx.annotation.GuardedBy; 33 import androidx.annotation.WorkerThread; 34 35 import com.android.systemui.car.CarServiceProvider; 36 import com.android.systemui.settings.DisplayTracker; 37 import com.android.systemui.settings.UserTracker; 38 import com.android.systemui.util.Assert; 39 40 import java.lang.ref.WeakReference; 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.concurrent.Executor; 44 45 /** 46 * Custom {@link DisplayTracker} for CarSystemUI. This class utilizes the 47 * {@link CarOccupantZoneManager} to provide the relevant displays and callbacks for a particular 48 * SystemUI instance running for a particular user. 49 */ 50 public class CarDisplayTrackerImpl implements DisplayTracker { 51 private final Context mContext; 52 private final DisplayManager mDisplayManager; 53 private final UserTracker mUserTracker; 54 private final Handler mHandler; 55 private CarOccupantZoneManager mCarOccupantZoneManager; 56 private CarOccupantZoneManager.OccupantZoneInfo mOccupantZone; 57 @GuardedBy("mDisplayCallbacks") 58 private final List<DisplayTrackerCallbackData> mDisplayCallbacks = new ArrayList<>(); 59 @GuardedBy("mBrightnessCallbacks") 60 private final List<DisplayTrackerCallbackData> mBrightnessCallbacks = new ArrayList<>(); 61 62 private final CarOccupantZoneManager.OccupantZoneConfigChangeListener mConfigChangeListener = 63 new CarOccupantZoneManager.OccupantZoneConfigChangeListener() { 64 @Override 65 public void onOccupantZoneConfigChanged(int changeFlags) { 66 mOccupantZone = mCarOccupantZoneManager.getOccupantZoneForUser( 67 mUserTracker.getUserHandle()); 68 } 69 }; 70 71 private final DisplayManager.DisplayListener mDisplayListener = 72 new DisplayManager.DisplayListener() { 73 @Override 74 public void onDisplayAdded(int displayId) { 75 List<DisplayTrackerCallbackData> callbacks; 76 synchronized (mDisplayCallbacks) { 77 callbacks = List.copyOf(mDisplayCallbacks); 78 } 79 CarDisplayTrackerImpl.this.onDisplayAdded(displayId, callbacks); 80 } 81 82 @Override 83 public void onDisplayRemoved(int displayId) { 84 List<DisplayTrackerCallbackData> callbacks; 85 synchronized (mDisplayCallbacks) { 86 callbacks = List.copyOf(mDisplayCallbacks); 87 } 88 CarDisplayTrackerImpl.this.onDisplayRemoved(displayId, callbacks); 89 } 90 91 @Override 92 public void onDisplayChanged(int displayId) { 93 List<DisplayTrackerCallbackData> callbacks; 94 synchronized (mDisplayCallbacks) { 95 callbacks = List.copyOf(mDisplayCallbacks); 96 } 97 CarDisplayTrackerImpl.this.onDisplayChanged(displayId, callbacks); 98 } 99 }; 100 101 private final DisplayManager.DisplayListener mBrightnessChangedListener = 102 new DisplayManager.DisplayListener() { 103 @Override 104 public void onDisplayAdded(int displayId) { 105 } 106 107 @Override 108 public void onDisplayRemoved(int displayId) { 109 } 110 111 @Override 112 public void onDisplayChanged(int displayId) { 113 List<DisplayTrackerCallbackData> callbacks; 114 synchronized (mBrightnessCallbacks) { 115 callbacks = List.copyOf(mBrightnessCallbacks); 116 } 117 CarDisplayTrackerImpl.this.onDisplayChanged(displayId, callbacks); 118 } 119 }; 120 CarDisplayTrackerImpl(Context context, UserTracker userTracker, CarServiceProvider carServiceProvider, Handler backgroundHandler)121 public CarDisplayTrackerImpl(Context context, UserTracker userTracker, 122 CarServiceProvider carServiceProvider, Handler backgroundHandler) { 123 mContext = context; 124 mDisplayManager = mContext.getSystemService(DisplayManager.class); 125 mUserTracker = userTracker; 126 mHandler = backgroundHandler; 127 carServiceProvider.addListener(mCarServiceOnConnectedListener); 128 } 129 130 @Override getDefaultDisplayId()131 public int getDefaultDisplayId() { 132 if (!isMUMDSystemUI()) { 133 return Display.DEFAULT_DISPLAY; 134 } 135 if (mOccupantZone != null) { 136 Display display = mCarOccupantZoneManager.getDisplayForOccupant(mOccupantZone, 137 DISPLAY_TYPE_MAIN); 138 if (display != null) { 139 return display.getDisplayId(); 140 } 141 } 142 return mContext.getDisplayId(); 143 } 144 145 @Override getAllDisplays()146 public Display[] getAllDisplays() { 147 if (!isMUMDSystemUI()) { 148 return mDisplayManager.getDisplays(); 149 } 150 if (mOccupantZone != null) { 151 return mCarOccupantZoneManager.getAllDisplaysForOccupant(mOccupantZone) 152 .toArray(Display[]::new); 153 } 154 return new Display[]{mDisplayManager.getDisplay(mContext.getDisplayId())}; 155 } 156 157 @Override getDisplay(int displayId)158 public Display getDisplay(int displayId) { 159 return mDisplayManager.getDisplay(displayId); 160 } 161 162 @Override addDisplayChangeCallback(Callback callback, Executor executor)163 public void addDisplayChangeCallback(Callback callback, Executor executor) { 164 synchronized (mDisplayCallbacks) { 165 if (mDisplayCallbacks.isEmpty()) { 166 mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); 167 } 168 mDisplayCallbacks.add(new DisplayTrackerCallbackData(callback, executor)); 169 } 170 } 171 172 @Override addBrightnessChangeCallback(Callback callback, Executor executor)173 public void addBrightnessChangeCallback(Callback callback, Executor executor) { 174 synchronized (mBrightnessCallbacks) { 175 if (mBrightnessCallbacks.isEmpty()) { 176 mDisplayManager.registerDisplayListener(mBrightnessChangedListener, mHandler, 177 EVENT_FLAG_DISPLAY_BRIGHTNESS); 178 } 179 mBrightnessCallbacks.add(new DisplayTrackerCallbackData(callback, executor)); 180 } 181 } 182 183 @Override removeCallback(Callback callback)184 public void removeCallback(Callback callback) { 185 synchronized (mDisplayCallbacks) { 186 boolean changed = mDisplayCallbacks.removeIf(it -> it.sameOrEmpty(callback)); 187 if (changed && mDisplayCallbacks.isEmpty()) { 188 mDisplayManager.unregisterDisplayListener(mDisplayListener); 189 } 190 } 191 192 synchronized (mBrightnessCallbacks) { 193 boolean changed = mBrightnessCallbacks.removeIf(it -> it.sameOrEmpty(callback)); 194 if (changed && mBrightnessCallbacks.isEmpty()) { 195 mDisplayManager.unregisterDisplayListener(mBrightnessChangedListener); 196 } 197 } 198 } 199 200 @WorkerThread onDisplayAdded(int displayId, List<DisplayTrackerCallbackData> callbacks)201 private void onDisplayAdded(int displayId, List<DisplayTrackerCallbackData> callbacks) { 202 Assert.isNotMainThread(); 203 if (!shouldExecuteDisplayCallback(displayId)) { 204 return; 205 } 206 207 callbacks.forEach(it -> { 208 DisplayTracker.Callback callback = it.mCallback.get(); 209 if (callback != null) { 210 it.mExecutor.execute(() -> callback.onDisplayAdded(displayId)); 211 } 212 }); 213 } 214 215 @WorkerThread onDisplayRemoved(int displayId, List<DisplayTrackerCallbackData> callbacks)216 private void onDisplayRemoved(int displayId, List<DisplayTrackerCallbackData> callbacks) { 217 Assert.isNotMainThread(); 218 if (!shouldExecuteDisplayCallback(displayId)) { 219 return; 220 } 221 222 callbacks.forEach(it -> { 223 DisplayTracker.Callback callback = it.mCallback.get(); 224 if (callback != null) { 225 it.mExecutor.execute(() -> callback.onDisplayRemoved(displayId)); 226 } 227 }); 228 } 229 230 @WorkerThread onDisplayChanged(int displayId, List<DisplayTrackerCallbackData> callbacks)231 private void onDisplayChanged(int displayId, List<DisplayTrackerCallbackData> callbacks) { 232 Assert.isNotMainThread(); 233 if (!shouldExecuteDisplayCallback(displayId)) { 234 return; 235 } 236 237 callbacks.forEach(it -> { 238 DisplayTracker.Callback callback = it.mCallback.get(); 239 if (callback != null) { 240 it.mExecutor.execute(() -> callback.onDisplayChanged(displayId)); 241 } 242 }); 243 } 244 shouldExecuteDisplayCallback(int displayId)245 private boolean shouldExecuteDisplayCallback(int displayId) { 246 if (!isMUMDSystemUI()) { 247 return true; 248 } 249 return mOccupantZone != null && isCurrentSystemUIDisplay(mCarOccupantZoneManager, 250 mOccupantZone, displayId); 251 } 252 253 private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceOnConnectedListener = 254 new CarServiceProvider.CarServiceOnConnectedListener() { 255 @Override 256 public void onConnected(Car car) { 257 mCarOccupantZoneManager = 258 (CarOccupantZoneManager) car.getCarManager( 259 Car.CAR_OCCUPANT_ZONE_SERVICE); 260 if (mCarOccupantZoneManager != null) { 261 mOccupantZone = mCarOccupantZoneManager.getOccupantZoneForUser( 262 mUserTracker.getUserHandle()); 263 mCarOccupantZoneManager.registerOccupantZoneConfigChangeListener( 264 mConfigChangeListener); 265 } 266 } 267 }; 268 269 private static class DisplayTrackerCallbackData { 270 final WeakReference<Callback> mCallback; 271 final Executor mExecutor; 272 DisplayTrackerCallbackData(Callback callback, Executor executor)273 DisplayTrackerCallbackData(Callback callback, Executor executor) { 274 mCallback = new WeakReference<>(callback); 275 mExecutor = executor; 276 } 277 sameOrEmpty(DisplayTracker.Callback other)278 boolean sameOrEmpty(DisplayTracker.Callback other) { 279 DisplayTracker.Callback callback = mCallback.get(); 280 if (callback == null) { 281 return true; 282 } 283 return callback.equals(other); 284 } 285 } 286 } 287