1 /* 2 * Copyright (C) 2020 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.power; 18 19 import static android.car.hardware.power.PowerComponent.BLUETOOTH; 20 import static android.car.hardware.power.PowerComponent.DISPLAY; 21 import static android.car.hardware.power.PowerComponent.VOICE_INTERACTION; 22 import static android.car.hardware.power.PowerComponent.WIFI; 23 import static android.car.hardware.power.PowerComponentUtil.FIRST_POWER_COMPONENT; 24 import static android.car.hardware.power.PowerComponentUtil.INVALID_POWER_COMPONENT; 25 import static android.car.hardware.power.PowerComponentUtil.LAST_POWER_COMPONENT; 26 import static android.car.hardware.power.PowerComponentUtil.powerComponentToString; 27 import static android.car.hardware.power.PowerComponentUtil.toPowerComponent; 28 29 import android.annotation.Nullable; 30 import android.bluetooth.BluetoothAdapter; 31 import android.car.hardware.power.CarPowerPolicy; 32 import android.car.hardware.power.CarPowerPolicyFilter; 33 import android.car.hardware.power.PowerComponent; 34 import android.content.Context; 35 import android.content.pm.PackageManager; 36 import android.net.wifi.WifiManager; 37 import android.os.RemoteException; 38 import android.os.ServiceManager; 39 import android.util.AtomicFile; 40 import android.util.IndentingPrintWriter; 41 import android.util.SparseArray; 42 import android.util.SparseBooleanArray; 43 44 import com.android.car.CarLog; 45 import com.android.car.systeminterface.SystemInterface; 46 import com.android.internal.annotations.GuardedBy; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.app.IVoiceInteractionManagerService; 49 import com.android.server.utils.Slogf; 50 51 import java.io.BufferedReader; 52 import java.io.BufferedWriter; 53 import java.io.File; 54 import java.io.FileNotFoundException; 55 import java.io.FileOutputStream; 56 import java.io.IOException; 57 import java.io.InputStreamReader; 58 import java.io.OutputStreamWriter; 59 import java.nio.charset.StandardCharsets; 60 61 /** 62 * Class that manages power components in the system. A power component mediator corresponding to a 63 * power component is created and registered to this class. A power component mediator encapsulates 64 * the function of powering on/off. 65 */ 66 @VisibleForTesting 67 public final class PowerComponentHandler { 68 private static final String TAG = CarLog.tagFor(PowerComponentHandler.class); 69 private static final String FORCED_OFF_COMPONENTS_FILENAME = 70 "forced_off_components"; 71 72 private final Object mLock = new Object(); 73 private final Context mContext; 74 private final SystemInterface mSystemInterface; 75 private final AtomicFile mOffComponentsByUserFile; 76 private final SparseArray<PowerComponentMediator> mPowerComponentMediators = 77 new SparseArray<>(); 78 @GuardedBy("mLock") 79 private final SparseBooleanArray mComponentStates = 80 new SparseBooleanArray(LAST_POWER_COMPONENT - FIRST_POWER_COMPONENT + 1); 81 @GuardedBy("mLock") 82 private final SparseBooleanArray mComponentsOffByPolicy = new SparseBooleanArray(); 83 @GuardedBy("mLock") 84 private final SparseBooleanArray mLastModifiedComponents = new SparseBooleanArray(); 85 private final IVoiceInteractionManagerService mVoiceInteractionServiceHolder; 86 private final PackageManager mPackageManager; 87 88 @GuardedBy("mLock") 89 private String mCurrentPolicyId = ""; 90 PowerComponentHandler(Context context, SystemInterface systemInterface)91 PowerComponentHandler(Context context, SystemInterface systemInterface) { 92 this(context, systemInterface, /* voiceInteractionService= */ null, 93 new AtomicFile(new File(systemInterface.getSystemCarDir(), 94 FORCED_OFF_COMPONENTS_FILENAME))); 95 } 96 PowerComponentHandler(Context context, SystemInterface systemInterface, IVoiceInteractionManagerService voiceInteractionService, AtomicFile componentStateFile)97 public PowerComponentHandler(Context context, SystemInterface systemInterface, 98 IVoiceInteractionManagerService voiceInteractionService, 99 AtomicFile componentStateFile) { 100 mContext = context; 101 mPackageManager = mContext.getPackageManager(); 102 mSystemInterface = systemInterface; 103 mVoiceInteractionServiceHolder = voiceInteractionService; 104 mOffComponentsByUserFile = componentStateFile; 105 } 106 init()107 void init() { 108 PowerComponentMediatorFactory factory = new PowerComponentMediatorFactory(); 109 synchronized (mLock) { 110 readUserOffComponentsLocked(); 111 for (int component = FIRST_POWER_COMPONENT; component <= LAST_POWER_COMPONENT; 112 component++) { 113 mComponentStates.put(component, false); 114 PowerComponentMediator mediator = factory.createPowerComponent(component); 115 String componentName = powerComponentToString(component); 116 if (mediator == null || !mediator.isComponentAvailable()) { 117 // We don't not associate a mediator with the component. 118 continue; 119 } 120 mPowerComponentMediators.put(component, mediator); 121 } 122 } 123 } 124 getAccumulatedPolicy()125 CarPowerPolicy getAccumulatedPolicy() { 126 synchronized (mLock) { 127 int enabledComponentsCount = 0; 128 int disabledComponentsCount = 0; 129 for (int component = FIRST_POWER_COMPONENT; component <= LAST_POWER_COMPONENT; 130 component++) { 131 if (mComponentStates.get(component, /* valueIfKeyNotFound= */ false)) { 132 enabledComponentsCount++; 133 } else { 134 disabledComponentsCount++; 135 } 136 } 137 int[] enabledComponents = new int[enabledComponentsCount]; 138 int[] disabledComponents = new int[disabledComponentsCount]; 139 int enabledIndex = 0; 140 int disabledIndex = 0; 141 for (int component = FIRST_POWER_COMPONENT; component <= LAST_POWER_COMPONENT; 142 component++) { 143 if (mComponentStates.get(component, /* valueIfKeyNotFound= */ false)) { 144 enabledComponents[enabledIndex++] = component; 145 } else { 146 disabledComponents[disabledIndex++] = component; 147 } 148 } 149 return new CarPowerPolicy(mCurrentPolicyId, enabledComponents, disabledComponents); 150 } 151 } 152 153 /** 154 * Applies the given policy considering user setting. 155 * 156 * <p> If a component is the policy is not applied due to user setting, it is not notified to 157 * listeners. 158 */ applyPowerPolicy(CarPowerPolicy policy)159 void applyPowerPolicy(CarPowerPolicy policy) { 160 int[] enabledComponents = policy.getEnabledComponents(); 161 int[] disabledComponents = policy.getDisabledComponents(); 162 synchronized (mLock) { 163 mLastModifiedComponents.clear(); 164 for (int i = 0; i < enabledComponents.length; i++) { 165 int component = enabledComponents[i]; 166 if (setComponentEnabledLocked(component, /* enabled= */ true)) { 167 mLastModifiedComponents.put(component, /* value= */ true); 168 } 169 } 170 for (int i = 0; i < disabledComponents.length; i++) { 171 int component = disabledComponents[i]; 172 if (setComponentEnabledLocked(component, /* enabled= */ false)) { 173 mLastModifiedComponents.put(component, /* value= */ true); 174 } 175 } 176 mCurrentPolicyId = policy.getPolicyId(); 177 } 178 } 179 isComponentChanged(CarPowerPolicyFilter filter)180 boolean isComponentChanged(CarPowerPolicyFilter filter) { 181 synchronized (mLock) { 182 int[] components = filter.getComponents(); 183 for (int i = 0; i < components.length; i++) { 184 if (mLastModifiedComponents.get(components[i], false)) { 185 return true; 186 } 187 } 188 return false; 189 } 190 } 191 dump(IndentingPrintWriter writer)192 void dump(IndentingPrintWriter writer) { 193 synchronized (mLock) { 194 writer.println("Power components state:"); 195 writer.increaseIndent(); 196 for (int component = FIRST_POWER_COMPONENT; component <= LAST_POWER_COMPONENT; 197 component++) { 198 writer.printf("%s: %s\n", powerComponentToString(component), 199 mComponentStates.get(component, /* valueIfKeyNotFound= */ false) 200 ? "on" : "off"); 201 } 202 writer.decreaseIndent(); 203 writer.println("Components powered off by power policy:"); 204 writer.increaseIndent(); 205 for (int i = 0; i < mComponentsOffByPolicy.size(); i++) { 206 writer.println(powerComponentToString(mComponentsOffByPolicy.keyAt(i))); 207 } 208 writer.decreaseIndent(); 209 writer.print("Components changed by the last policy: "); 210 writer.increaseIndent(); 211 for (int i = 0; i < mLastModifiedComponents.size(); i++) { 212 if (i > 0) writer.print(", "); 213 writer.print(powerComponentToString(mLastModifiedComponents.keyAt(i))); 214 } 215 writer.println(); 216 writer.decreaseIndent(); 217 } 218 } 219 220 /** 221 * Modifies power component's state, considering user setting. 222 * 223 * @return {@code true} if power state is changed. Otherwise, {@code false} 224 */ setComponentEnabledLocked(int component, boolean enabled)225 private boolean setComponentEnabledLocked(int component, boolean enabled) { 226 boolean oldState = mComponentStates.get(component, /* valueIfKeyNotFound= */ false); 227 if (oldState == enabled) { 228 return false; 229 } 230 231 mComponentStates.put(component, enabled); 232 233 PowerComponentMediator mediator = mPowerComponentMediators.get(component); 234 if (mediator == null) { 235 return true; 236 } 237 238 boolean needPowerChange = false; 239 if (mediator.isUserControllable()) { 240 if (!enabled && mediator.isEnabled()) { 241 mComponentsOffByPolicy.put(component, /* value= */ true); 242 needPowerChange = true; 243 } 244 if (enabled && mComponentsOffByPolicy.get(component, /* valueIfKeyNotFound= */ false)) { 245 mComponentsOffByPolicy.delete(component); 246 needPowerChange = true; 247 } 248 if (needPowerChange) { 249 writeUserOffComponentsLocked(); 250 } 251 } else { 252 needPowerChange = true; 253 } 254 255 if (needPowerChange) { 256 mediator.setEnabled(enabled); 257 } 258 return true; 259 } 260 readUserOffComponentsLocked()261 private void readUserOffComponentsLocked() { 262 boolean invalid = false; 263 mComponentsOffByPolicy.clear(); 264 try (BufferedReader reader = new BufferedReader( 265 new InputStreamReader(mOffComponentsByUserFile.openRead(), 266 StandardCharsets.UTF_8))) { 267 String line; 268 while ((line = reader.readLine()) != null) { 269 int component = toPowerComponent(line.trim(), /* prefix= */ false); 270 if (component == INVALID_POWER_COMPONENT) { 271 invalid = true; 272 break; 273 } 274 mComponentsOffByPolicy.put(component, /* value= */ true); 275 } 276 } catch (FileNotFoundException e) { 277 // Behave as if there are no forced-off components. 278 return; 279 } catch (IOException e) { 280 Slogf.w(TAG, "Failed to read %s: %s", FORCED_OFF_COMPONENTS_FILENAME, e); 281 return; 282 } 283 if (invalid) { 284 mOffComponentsByUserFile.delete(); 285 } 286 } 287 writeUserOffComponentsLocked()288 private void writeUserOffComponentsLocked() { 289 FileOutputStream fos; 290 try { 291 fos = mOffComponentsByUserFile.startWrite(); 292 } catch (IOException e) { 293 Slogf.e(TAG, e, "Cannot create %s", FORCED_OFF_COMPONENTS_FILENAME); 294 return; 295 } 296 297 try (BufferedWriter writer = new BufferedWriter( 298 new OutputStreamWriter(fos, StandardCharsets.UTF_8))) { 299 for (int i = 0; i < mComponentsOffByPolicy.size(); i++) { 300 if (!mComponentsOffByPolicy.valueAt(i)) { 301 continue; 302 } 303 writer.write(powerComponentToString(mComponentsOffByPolicy.keyAt(i))); 304 writer.newLine(); 305 } 306 writer.flush(); 307 mOffComponentsByUserFile.finishWrite(fos); 308 } catch (IOException e) { 309 mOffComponentsByUserFile.failWrite(fos); 310 Slogf.e(TAG, e, "Writing %s failed", FORCED_OFF_COMPONENTS_FILENAME); 311 } 312 } 313 314 abstract static class PowerComponentMediator { 315 protected int mComponentId; 316 PowerComponentMediator(int component)317 PowerComponentMediator(int component) { 318 mComponentId = component; 319 } 320 isComponentAvailable()321 public boolean isComponentAvailable() { 322 return false; 323 } 324 isUserControllable()325 public boolean isUserControllable() { 326 return false; 327 } 328 isEnabled()329 public boolean isEnabled() { 330 return false; 331 } 332 setEnabled(boolean enabled)333 public void setEnabled(boolean enabled) {} 334 } 335 336 // TODO(b/178824607): Check if power policy can turn on/off display as quickly as the existing 337 // implementation. 338 private final class DisplayPowerComponentMediator extends PowerComponentMediator { DisplayPowerComponentMediator()339 DisplayPowerComponentMediator() { 340 super(DISPLAY); 341 } 342 343 @Override isComponentAvailable()344 public boolean isComponentAvailable() { 345 // It is assumed that display is supported in all vehicles. 346 return true; 347 } 348 349 @Override isEnabled()350 public boolean isEnabled() { 351 return mSystemInterface.isDisplayEnabled(); 352 } 353 354 @Override setEnabled(boolean enabled)355 public void setEnabled(boolean enabled) { 356 mSystemInterface.setDisplayState(enabled); 357 Slogf.d(TAG, "Display power component is %s", enabled ? "on" : "off"); 358 } 359 } 360 361 private final class WifiPowerComponentMediator extends PowerComponentMediator { 362 private final WifiManager mWifiManager; 363 WifiPowerComponentMediator()364 WifiPowerComponentMediator() { 365 super(WIFI); 366 mWifiManager = mContext.getSystemService(WifiManager.class); 367 } 368 369 @Override isComponentAvailable()370 public boolean isComponentAvailable() { 371 return mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI); 372 } 373 374 @Override isUserControllable()375 public boolean isUserControllable() { 376 return true; 377 } 378 379 @Override isEnabled()380 public boolean isEnabled() { 381 return mWifiManager.isWifiEnabled(); 382 } 383 384 @Override setEnabled(boolean enabled)385 public void setEnabled(boolean enabled) { 386 mWifiManager.setWifiEnabled(enabled); 387 Slogf.d(TAG, "Wifi power component is %s", enabled ? "on" : "off"); 388 } 389 } 390 391 private final class VoiceInteractionPowerComponentMediator extends PowerComponentMediator { 392 private final IVoiceInteractionManagerService mVoiceInteractionManagerService; 393 394 private boolean mIsEnabled = true; 395 VoiceInteractionPowerComponentMediator()396 VoiceInteractionPowerComponentMediator() { 397 super(VOICE_INTERACTION); 398 if (mVoiceInteractionServiceHolder == null) { 399 mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface( 400 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); 401 } else { 402 mVoiceInteractionManagerService = mVoiceInteractionServiceHolder; 403 } 404 } 405 406 @Override isComponentAvailable()407 public boolean isComponentAvailable() { 408 return mVoiceInteractionManagerService != null; 409 } 410 411 @Override isEnabled()412 public boolean isEnabled() { 413 return mIsEnabled; 414 } 415 416 @Override setEnabled(boolean enabled)417 public void setEnabled(boolean enabled) { 418 try { 419 mVoiceInteractionManagerService.setDisabled(!enabled); 420 mIsEnabled = enabled; 421 Slogf.d(TAG, "Voice Interaction power component is %s", enabled ? "on" : "off"); 422 } catch (RemoteException e) { 423 Slogf.w(TAG, e, "IVoiceInteractionManagerService.setDisabled(%b) failed", !enabled); 424 } 425 } 426 } 427 428 private final class BluetoothPowerComponentMediator extends PowerComponentMediator { 429 private final BluetoothAdapter mBluetoothAdapter; 430 BluetoothPowerComponentMediator()431 BluetoothPowerComponentMediator() { 432 super(BLUETOOTH); 433 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 434 } 435 436 @Override isComponentAvailable()437 public boolean isComponentAvailable() { 438 return mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); 439 } 440 441 @Override isEnabled()442 public boolean isEnabled() { 443 return mBluetoothAdapter.isEnabled(); 444 } 445 446 @Override setEnabled(boolean enabled)447 public void setEnabled(boolean enabled) { 448 // No op 449 Slogf.w(TAG, "Bluetooth power is controlled by " 450 + "com.android.car.BluetoothDeviceConnectionPolicy"); 451 } 452 } 453 454 private final class PowerComponentMediatorFactory { 455 @Nullable createPowerComponent(int component)456 PowerComponentMediator createPowerComponent(int component) { 457 switch (component) { 458 case PowerComponent.AUDIO: 459 // We don't control audio in framework level, because audio is enabled or 460 // disabled in audio HAL according to the current power policy. 461 return null; 462 case PowerComponent.MEDIA: 463 return null; 464 case PowerComponent.DISPLAY: 465 return new DisplayPowerComponentMediator(); 466 case PowerComponent.WIFI: 467 return new WifiPowerComponentMediator(); 468 case PowerComponent.CELLULAR: 469 return null; 470 case PowerComponent.ETHERNET: 471 return null; 472 case PowerComponent.PROJECTION: 473 return null; 474 case PowerComponent.NFC: 475 return null; 476 case PowerComponent.INPUT: 477 return null; 478 case PowerComponent.VOICE_INTERACTION: 479 return new VoiceInteractionPowerComponentMediator(); 480 case PowerComponent.VISUAL_INTERACTION: 481 return null; 482 case PowerComponent.TRUSTED_DEVICE_DETECTION: 483 return null; 484 case PowerComponent.MICROPHONE: 485 // We don't control microphone in framework level, because microphone is enabled 486 // or disabled in audio HAL according to the current power policy. 487 return null; 488 case PowerComponent.BLUETOOTH: 489 // com.android.car.BluetoothDeviceConnectionPolicy handles power state change. 490 // So, bluetooth mediator doesn't directly turn on/off BT, but it changes policy 491 // behavior, considering user intervetion. 492 return new BluetoothPowerComponentMediator(); 493 case PowerComponent.LOCATION: 494 // GNSS HAL handles power state change. 495 return null; 496 case PowerComponent.CPU: 497 return null; 498 default: 499 Slogf.w(TAG, "Unknown component(%d)", component); 500 return null; 501 } 502 } 503 } 504 505 static class PowerComponentException extends Exception { PowerComponentException(String message)506 PowerComponentException(String message) { 507 super(message); 508 } 509 } 510 } 511