1 /* 2 * Copyright (C) 2016 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.car; 18 19 import static android.car.settings.CarSettings.Global.FORCED_DAY_NIGHT_MODE; 20 21 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 22 23 import android.annotation.IntDef; 24 import android.app.UiModeManager; 25 import android.car.builtin.util.Slogf; 26 import android.car.feature.Flags; 27 import android.car.hardware.CarPropertyValue; 28 import android.car.hardware.property.CarPropertyEvent; 29 import android.car.hardware.property.ICarPropertyEventListener; 30 import android.content.Context; 31 import android.database.ContentObserver; 32 import android.hardware.automotive.vehicle.VehicleProperty; 33 import android.os.RemoteException; 34 import android.provider.Settings; 35 import android.util.Log; 36 import android.util.proto.ProtoOutputStream; 37 38 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 39 import com.android.car.internal.util.IndentingPrintWriter; 40 import com.android.internal.annotations.GuardedBy; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.util.List; 45 46 /** 47 * Class used to handle events used to set vehicle in night mode. 48 */ 49 public class CarNightService implements CarServiceBase { 50 51 public static final boolean DBG = Slogf.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG); 52 53 @IntDef({FORCED_SENSOR_MODE, FORCED_DAY_MODE, FORCED_NIGHT_MODE}) 54 @Retention(RetentionPolicy.SOURCE) 55 public @interface DayNightSensorMode {} 56 57 public static final int FORCED_SENSOR_MODE = 0; 58 public static final int FORCED_DAY_MODE = 1; 59 public static final int FORCED_NIGHT_MODE = 2; 60 61 private final Object mLock = new Object(); 62 @GuardedBy("mLock") 63 private int mNightSetting = UiModeManager.MODE_NIGHT_YES; 64 @GuardedBy("mLock") 65 private int mForcedMode = FORCED_SENSOR_MODE; 66 @GuardedBy("mLock") 67 private long mLastSensorEventTime = -1; 68 private final Context mContext; 69 @GuardedBy("mLock") 70 private final UiModeManager mUiModeManager; 71 private final CarPropertyService mCarPropertyService; 72 73 private final ICarPropertyEventListener mICarPropertyEventListener = 74 new ICarPropertyEventListener.Stub() { 75 @Override 76 public void onEvent(List<CarPropertyEvent> events) throws RemoteException { 77 synchronized (mLock) { 78 for (CarPropertyEvent event : events) { 79 onNightModeCarPropertyEventLocked(event); 80 } 81 } 82 } 83 }; 84 85 private final ContentObserver mForcedNightModeObserver = 86 new ContentObserver(null /* handler */) { 87 @Override 88 public void onChange(boolean selfChange) { 89 synchronized (mLock) { 90 forceDayNightModeInternal(getForcedDayNightModeSetting()); 91 } 92 } 93 }; 94 95 /** 96 * Acts on {@link CarPropertyEvent} events marked with {@link 97 * CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE} and marked with {@link 98 * VehicleProperty.NIGHT_MODE} by setting the vehicle in night mode. 99 * <p> 100 * This method does nothing if the event parameter is {@code null}. 101 * 102 * @param event the car property event to be handled 103 */ 104 @GuardedBy("mLock") onNightModeCarPropertyEventLocked(CarPropertyEvent event)105 private void onNightModeCarPropertyEventLocked(CarPropertyEvent event) { 106 if (event == null) { 107 return; 108 } 109 if (event.getEventType() == CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) { 110 // Only handle onChange events 111 CarPropertyValue value = event.getCarPropertyValue(); 112 if (value.getPropertyId() == VehicleProperty.NIGHT_MODE 113 && value.getTimestamp() > mLastSensorEventTime) { 114 mLastSensorEventTime = value.getTimestamp(); 115 boolean nightMode = (Boolean) value.getValue(); 116 Slogf.i(CarLog.TAG_SENSOR, "Set dayNight Mode as " 117 + nightMode + " at timestamp: " + mLastSensorEventTime); 118 setNightModeLocked(nightMode); 119 } 120 } 121 } 122 123 @GuardedBy("mLock") setNightModeLocked(boolean nightMode)124 private void setNightModeLocked(boolean nightMode) { 125 if (nightMode) { 126 mNightSetting = UiModeManager.MODE_NIGHT_YES; 127 if (DBG) Slogf.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent NIGHT"); 128 } else { 129 mNightSetting = UiModeManager.MODE_NIGHT_NO; 130 if (DBG) Slogf.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent DAY"); 131 } 132 if (mUiModeManager != null && (mForcedMode == FORCED_SENSOR_MODE)) { 133 mUiModeManager.setNightMode(mNightSetting); 134 if (DBG) Slogf.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent APPLIED"); 135 } else { 136 if (DBG) Slogf.d(CarLog.TAG_SENSOR, "CAR dayNight handleSensorEvent IGNORED"); 137 } 138 } 139 140 @DayNightSensorMode getForcedDayNightModeSetting()141 private int getForcedDayNightModeSetting() { 142 return Settings.Global.getInt(mContext.getContentResolver(), 143 FORCED_DAY_NIGHT_MODE, FORCED_SENSOR_MODE); 144 } 145 146 /** 147 * Sets {@link UiModeManager} to night mode according to the {@link DayNightSensorMode} passed 148 * as parameter. 149 * 150 * @param mode the sensor mode used to set vehicle in night mode 151 * @return the current night mode, or {@code -1} on error 152 */ forceDayNightMode(@ayNightSensorMode int mode)153 public int forceDayNightMode(@DayNightSensorMode int mode) { 154 synchronized (mLock) { 155 return forceDayNightModeInternal(mode); 156 } 157 } 158 159 @GuardedBy("mLock") forceDayNightModeInternal(@ayNightSensorMode int mode)160 private int forceDayNightModeInternal(@DayNightSensorMode int mode) { 161 if (mUiModeManager == null) { 162 return -1; 163 } 164 int resultMode; 165 switch (mode) { 166 case FORCED_SENSOR_MODE: 167 resultMode = mNightSetting; 168 mForcedMode = FORCED_SENSOR_MODE; 169 break; 170 case FORCED_DAY_MODE: 171 resultMode = UiModeManager.MODE_NIGHT_NO; 172 mForcedMode = FORCED_DAY_MODE; 173 break; 174 case FORCED_NIGHT_MODE: 175 resultMode = UiModeManager.MODE_NIGHT_YES; 176 mForcedMode = FORCED_NIGHT_MODE; 177 break; 178 default: 179 Slogf.e(CarLog.TAG_SENSOR, "Unknown forced day/night mode " + mode); 180 return -1; 181 } 182 mUiModeManager.setNightMode(resultMode); 183 return mUiModeManager.getNightMode(); 184 } 185 CarNightService(Context context, CarPropertyService propertyService)186 CarNightService(Context context, CarPropertyService propertyService) { 187 mContext = context; 188 mCarPropertyService = propertyService; 189 mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE); 190 if (mUiModeManager == null) { 191 Slogf.w(CarLog.TAG_SENSOR, "Failed to get UI_MODE_SERVICE"); 192 } 193 } 194 195 @Override init()196 public void init() { 197 if (DBG) { 198 Slogf.d(CarLog.TAG_SENSOR, "CAR dayNight init."); 199 } 200 synchronized (mLock) { 201 if (Flags.carNightGlobalSetting()) { 202 mContext.getContentResolver().registerContentObserver( 203 Settings.Global.getUriFor(FORCED_DAY_NIGHT_MODE), 204 false /* notifyForDescendants */, 205 mForcedNightModeObserver); 206 mForcedMode = getForcedDayNightModeSetting(); 207 if (mForcedMode != FORCED_SENSOR_MODE) { 208 // Only update the mode if it's not currently sensor mode. If it is sensor mode, 209 // it will be updated by the setNightModeLocked call below. 210 forceDayNightModeInternal(mForcedMode); 211 } 212 } 213 214 mCarPropertyService.registerListenerSafe(VehicleProperty.NIGHT_MODE, 0, 215 mICarPropertyEventListener); 216 CarPropertyValue propertyValue = mCarPropertyService.getPropertySafe( 217 VehicleProperty.NIGHT_MODE, 0); 218 if (propertyValue != null && propertyValue.getTimestamp() != 0) { 219 mLastSensorEventTime = propertyValue.getTimestamp(); 220 setNightModeLocked((Boolean) propertyValue.getValue()); 221 } else { 222 Slogf.w(CarLog.TAG_SENSOR, "Failed to get value of NIGHT_MODE"); 223 setNightModeLocked(true); 224 } 225 } 226 } 227 228 @Override release()229 public void release() { 230 } 231 232 @Override 233 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)234 public void dump(IndentingPrintWriter writer) { 235 synchronized (mLock) { 236 writer.println("*DAY NIGHT POLICY*"); 237 writer.println( 238 "Mode:" + ((mNightSetting == UiModeManager.MODE_NIGHT_YES) ? "night" : "day")); 239 writer.println("Forced Mode? " + (mForcedMode == FORCED_SENSOR_MODE 240 ? "false, timestamp of dayNight sensor is: " + mLastSensorEventTime 241 : (mForcedMode == FORCED_DAY_MODE ? "day" : "night"))); 242 } 243 } 244 245 @Override 246 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProto(ProtoOutputStream proto)247 public void dumpProto(ProtoOutputStream proto) {} 248 } 249