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 17 package android.car.hardware.power; 18 19 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 20 21 import android.annotation.CallbackExecutor; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.RequiresPermission; 25 import android.annotation.SystemApi; 26 import android.car.Car; 27 import android.car.CarManagerBase; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.util.ArrayMap; 31 import android.util.Log; 32 import android.util.Pair; 33 import android.util.SparseIntArray; 34 35 import com.android.internal.annotations.GuardedBy; 36 37 import java.util.ArrayList; 38 import java.util.concurrent.CancellationException; 39 import java.util.concurrent.CompletableFuture; 40 import java.util.concurrent.Executor; 41 42 /** 43 * API to receive power policy change notifications. 44 */ 45 public class CarPowerManager extends CarManagerBase { 46 private static final boolean DBG = false; 47 private static final String TAG = CarPowerManager.class.getSimpleName(); 48 49 private static final int FIRST_POWER_COMPONENT = PowerComponentUtil.FIRST_POWER_COMPONENT; 50 private static final int LAST_POWER_COMPONENT = PowerComponentUtil.LAST_POWER_COMPONENT; 51 52 private final Object mLock = new Object(); 53 private final ICarPower mService; 54 @GuardedBy("mLock") 55 private final ArrayMap<CarPowerPolicyListener, Pair<Executor, CarPowerPolicyFilter>> 56 mPolicyListenerMap = new ArrayMap<>(); 57 // key: power component, value: number of listeners to have interest in the component 58 @GuardedBy("mLock") 59 private final SparseIntArray mInterestedComponentMap = new SparseIntArray(); 60 private final ICarPowerPolicyListener mPolicyChangeBinderCallback = 61 new ICarPowerPolicyListener.Stub() { 62 @Override 63 public void onPolicyChanged(CarPowerPolicy appliedPolicy, 64 CarPowerPolicy accumulatedPolicy) { 65 notifyPowerPolicyListeners(appliedPolicy, accumulatedPolicy); 66 } 67 }; 68 69 @GuardedBy("mLock") 70 private CarPowerStateListener mListener; 71 @GuardedBy("mLock") 72 private CarPowerStateListenerWithCompletion mListenerWithCompletion; 73 @GuardedBy("mLock") 74 private CompletableFuture<Void> mFuture; 75 @GuardedBy("mLock") 76 private ICarPowerStateListener mListenerToService; 77 78 /** 79 * Applications set a {@link CarPowerStateListener} for power state event updates. 80 * 81 * @hide 82 */ 83 @SystemApi 84 public interface CarPowerStateListener { 85 /** 86 * onStateChanged() states. These definitions must match the ones located in the native 87 * CarPowerManager: packages/services/Car/car-lib/native/include/CarPowerManager.h 88 */ 89 90 /** 91 * The current power state is unavailable, unknown, or invalid. 92 * 93 * @hide 94 */ 95 int INVALID = 0; 96 97 /** 98 * Android is up, but vendor is controlling the audio / display. 99 * 100 * @hide 101 */ 102 int WAIT_FOR_VHAL = 1; 103 104 /** 105 * Enter suspend state. CPMS is switching to WAIT_FOR_FINISHED state. 106 * 107 * @hide 108 */ 109 int SUSPEND_ENTER = 2; 110 111 /** 112 * Wake up from suspend. 113 * 114 * @hide 115 */ 116 int SUSPEND_EXIT = 3; 117 118 /** 119 * Enter shutdown state. CPMS is switching to WAIT_FOR_FINISHED state. 120 * 121 * @hide 122 */ 123 int SHUTDOWN_ENTER = 5; 124 125 /** 126 * On state. 127 * 128 * @hide 129 */ 130 int ON = 6; 131 132 /** 133 * State where system is getting ready for shutdown or suspend. Application is expected to 134 * cleanup and be ready to suspend. 135 * 136 * @hide 137 */ 138 int SHUTDOWN_PREPARE = 7; 139 140 /** 141 * Shutdown is cancelled, returning to normal state. 142 * 143 * @hide 144 */ 145 int SHUTDOWN_CANCELLED = 8; 146 147 /** 148 * Called when power state changes. This callback is available to 149 * any listener, even if it is not running in the system process. 150 * 151 * @param state New power state of device. 152 * 153 * @hide 154 */ onStateChanged(int state)155 void onStateChanged(int state); 156 } 157 158 /** 159 * Applications set a {@link CarPowerStateListenerWithCompletion} for power state 160 * event updates where a CompletableFuture is used. 161 * 162 * @hide 163 */ 164 @SystemApi 165 public interface CarPowerStateListenerWithCompletion { 166 /** 167 * Called when power state changes. This callback is only for listeners 168 * that are running in the system process. 169 * 170 * @param state New power state of device. 171 * @param future CompletableFuture used by Car modules to notify CPMS that they 172 * are ready to continue shutting down. CPMS will wait until this 173 * future is completed. 174 * 175 * @hide 176 */ onStateChanged(int state, CompletableFuture<Void> future)177 void onStateChanged(int state, CompletableFuture<Void> future); 178 } 179 180 /** 181 * Listeners to receive power policy change. 182 * 183 * <p> Applications interested in power policy change register 184 * {@code CarPowerPolicyListener} and will be notified when power policy changes. 185 */ 186 public interface CarPowerPolicyListener { 187 /** 188 * Called with {@link #CarPowerPolicy} when power policy changes. 189 * 190 * @param policy The current power policy. 191 */ onPolicyChanged(@onNull CarPowerPolicy policy)192 void onPolicyChanged(@NonNull CarPowerPolicy policy); 193 } 194 195 /** 196 * Gets an instance of the CarPowerManager. 197 * 198 * <p>Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead. 199 * 200 * @hide 201 */ CarPowerManager(Car car, IBinder service)202 public CarPowerManager(Car car, IBinder service) { 203 super(car); 204 mService = ICarPower.Stub.asInterface(service); 205 } 206 207 /** 208 * Requests power manager to shutdown in lieu of suspend at the next opportunity. 209 * 210 * @hide 211 */ 212 @RequiresPermission(Car.PERMISSION_CAR_POWER) requestShutdownOnNextSuspend()213 public void requestShutdownOnNextSuspend() { 214 try { 215 mService.requestShutdownOnNextSuspend(); 216 } catch (RemoteException e) { 217 handleRemoteExceptionFromCarService(e); 218 } 219 } 220 221 /** 222 * Schedules next wake up time in CarPowerManagementService. 223 * 224 * @hide 225 */ 226 @RequiresPermission(Car.PERMISSION_CAR_POWER) scheduleNextWakeupTime(int seconds)227 public void scheduleNextWakeupTime(int seconds) { 228 try { 229 mService.scheduleNextWakeupTime(seconds); 230 } catch (RemoteException e) { 231 handleRemoteExceptionFromCarService(e); 232 } 233 } 234 235 /** 236 * Returns the current power state. 237 * 238 * @return One of the values defined in {@link CarPowerStateListener}. 239 * 240 * @hide 241 */ 242 @SystemApi 243 @RequiresPermission(Car.PERMISSION_CAR_POWER) getPowerState()244 public int getPowerState() { 245 try { 246 return mService.getPowerState(); 247 } catch (RemoteException e) { 248 return handleRemoteExceptionFromCarService(e, CarPowerStateListener.INVALID); 249 } 250 } 251 252 /** 253 * Sets a listener to receive power state changes. Only one listener may be set at a 254 * time for an instance of CarPowerManager. 255 * The listener is assumed to completely handle the 'onStateChanged' before returning. 256 * 257 * @param listener 258 * @throws IllegalStateException 259 * 260 * @hide 261 */ 262 @SystemApi 263 @RequiresPermission(Car.PERMISSION_CAR_POWER) setListener(@ullable CarPowerStateListener listener)264 public void setListener(@Nullable CarPowerStateListener listener) { 265 synchronized (mLock) { 266 if (mListener != null || mListenerWithCompletion != null) { 267 throw new IllegalStateException("Listener must be cleared first"); 268 } 269 // Update listener 270 mListener = listener; 271 setServiceForListenerLocked(false); 272 } 273 } 274 275 /** 276 * Sets a listener to receive power state changes. Only one listener may be set at a 277 * time for an instance of CarPowerManager. 278 * For calls that require completion before continue, we attach a {@link CompletableFuture} 279 * which is being used as a signal that caller is finished and ready to proceed. 280 * Once future is completed, the {@link finished} method will automatically be called to notify 281 * {@link CarPowerManagementService} that the application has handled the 282 * {@link #SHUTDOWN_PREPARE} state transition. 283 * 284 * @param listener 285 * @throws IllegalStateException 286 * 287 * @hide 288 */ 289 @RequiresPermission(Car.PERMISSION_CAR_POWER) setListenerWithCompletion(@ullable CarPowerStateListenerWithCompletion listener)290 public void setListenerWithCompletion(@Nullable CarPowerStateListenerWithCompletion listener) { 291 synchronized (mLock) { 292 if (mListener != null || mListenerWithCompletion != null) { 293 throw new IllegalStateException("Listener must be cleared first"); 294 } 295 // Update listener 296 mListenerWithCompletion = listener; 297 setServiceForListenerLocked(true); 298 } 299 } 300 301 /** 302 * Removes the power state listener. 303 * 304 * @hide 305 */ 306 @SystemApi 307 @RequiresPermission(Car.PERMISSION_CAR_POWER) clearListener()308 public void clearListener() { 309 ICarPowerStateListener listenerToService; 310 synchronized (mLock) { 311 listenerToService = mListenerToService; 312 mListenerToService = null; 313 mListener = null; 314 mListenerWithCompletion = null; 315 cleanupFutureLocked(); 316 } 317 318 if (listenerToService == null) { 319 Log.w(TAG, "unregisterListener: listener was not registered"); 320 return; 321 } 322 323 try { 324 mService.unregisterListener(listenerToService); 325 } catch (RemoteException e) { 326 handleRemoteExceptionFromCarService(e); 327 } 328 } 329 330 /** 331 * Gets the current power policy. 332 * 333 * <p>The returned power policy has ID of the power policy applied most recently. If no power 334 * policy has been applied, the ID is an empty string. Note that enabled components and disabled 335 * components might be different from those of the latest power policy applied. This is because 336 * the returned power policy contains the current state of all power components determined by 337 * applying power policies in an accumulative way. 338 * 339 * @return The power policy containing the latest state of all power components. 340 */ 341 @RequiresPermission(Car.PERMISSION_READ_CAR_POWER_POLICY) 342 @Nullable getCurrentPowerPolicy()343 public CarPowerPolicy getCurrentPowerPolicy() { 344 try { 345 return mService.getCurrentPowerPolicy(); 346 } catch (RemoteException e) { 347 return handleRemoteExceptionFromCarService(e, null); 348 } 349 } 350 351 /** 352 * Applies the given power policy. 353 * 354 * <p> Power components are turned on or off as specified in the given power policy. Power 355 * policies are defined at {@code /vendor/etc/power_policy.xml}. If the given power policy 356 * doesn't exist, this method throws {@link java.lang.IllegalArgumentException}. 357 * 358 * @param policyId ID of power policy. 359 * @throws IllegalArgumentException if {@code policyId} is null. 360 * 361 * @hide 362 */ 363 @SystemApi 364 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_POWER_POLICY) applyPowerPolicy(@onNull String policyId)365 public void applyPowerPolicy(@NonNull String policyId) { 366 checkArgument(policyId != null, "Null policyId"); 367 try { 368 mService.applyPowerPolicy(policyId); 369 } catch (RemoteException e) { 370 handleRemoteExceptionFromCarService(e); 371 } 372 } 373 374 /** 375 * Sets the current power policy group. 376 * 377 * Power policy group defines a rule to apply a certain power policy according to the power 378 * state transition. For example, a power policy named "default_for_on" is supposed to be 379 * applied when the power state becomes ON. This rule is specified in the power policy group. 380 * Many power policy groups can be pre-defined, and one of them is set for the current one using 381 * {@code setPowerPolicyGroup}. 382 * 383 * @param policyGroupId ID of power policy group. 384 * @throws IllegalArgumentException if {@code policyGroupId} is null. 385 * 386 * @hide 387 */ 388 @SystemApi 389 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_POWER_POLICY) setPowerPolicyGroup(@onNull String policyGroupId)390 public void setPowerPolicyGroup(@NonNull String policyGroupId) { 391 checkArgument(policyGroupId != null, "Null policyGroupId"); 392 try { 393 mService.setPowerPolicyGroup(policyGroupId); 394 } catch (RemoteException e) { 395 handleRemoteExceptionFromCarService(e); 396 } 397 } 398 399 /** 400 * Subscribes to power policy change. 401 * 402 * <p>If the same listener is added with different filters, the listener is notified based on 403 * the last added filter. 404 * 405 * @param executor Executor where the listener method is called. 406 * @param listener Listener to be notified. 407 * @param filter Filter specifying power components of interest. 408 * @throws IllegalArgumentException if {@code executor}, {@code listener}, or {@code filter} is 409 * null. 410 */ 411 @RequiresPermission(Car.PERMISSION_READ_CAR_POWER_POLICY) addPowerPolicyListener(@onNull @allbackExecutor Executor executor, @NonNull CarPowerPolicyFilter filter, @NonNull CarPowerPolicyListener listener)412 public void addPowerPolicyListener(@NonNull @CallbackExecutor Executor executor, 413 @NonNull CarPowerPolicyFilter filter, @NonNull CarPowerPolicyListener listener) { 414 assertPermission(Car.PERMISSION_READ_CAR_POWER_POLICY); 415 checkArgument(executor != null, "Null executor"); 416 checkArgument(filter != null, "Null filter"); 417 checkArgument(listener != null, "Null listener"); 418 boolean updateCallbackNeeded = false; 419 CarPowerPolicyFilter newFilter = null; 420 synchronized (mLock) { 421 mPolicyListenerMap.remove(listener); 422 int[] filterComponents = filter.getComponents().clone(); 423 Pair<Executor, CarPowerPolicyFilter> pair = 424 new Pair<>(executor, new CarPowerPolicyFilter(filterComponents)); 425 mPolicyListenerMap.put(listener, pair); 426 for (int i = 0; i < filterComponents.length; i++) { 427 int key = filterComponents[i]; 428 int currentCount = mInterestedComponentMap.get(key); 429 if (currentCount == 0) { 430 updateCallbackNeeded = true; 431 mInterestedComponentMap.put(key, 1); 432 } else { 433 mInterestedComponentMap.put(key, currentCount + 1); 434 } 435 } 436 if (updateCallbackNeeded) { 437 newFilter = createFilterFromInterestedComponentsLocked(); 438 } 439 } 440 if (updateCallbackNeeded) { 441 updatePowerPolicyChangeCallback(newFilter); 442 } 443 } 444 445 /** 446 * Unsubscribes from power policy change. 447 * 448 * @param listener Listener that will not be notified any more. 449 * @throws IllegalArgumentException if {@code listener} is null. 450 */ 451 @RequiresPermission(Car.PERMISSION_READ_CAR_POWER_POLICY) removePowerPolicyListener(@onNull CarPowerPolicyListener listener)452 public void removePowerPolicyListener(@NonNull CarPowerPolicyListener listener) { 453 assertPermission(Car.PERMISSION_READ_CAR_POWER_POLICY); 454 checkArgument(listener != null, "Null listener"); 455 boolean updateCallbackNeeded = false; 456 CarPowerPolicyFilter filter = null; 457 synchronized (mLock) { 458 Pair<Executor, CarPowerPolicyFilter> pair = mPolicyListenerMap.remove(listener); 459 if (pair == null) { 460 return; 461 } 462 int[] filterComponents = pair.second.getComponents(); 463 for (int i = 0; i < filterComponents.length; i++) { 464 int key = filterComponents[i]; 465 int currentCount = mInterestedComponentMap.get(key); 466 if (currentCount == 0 || currentCount == 1) { 467 mInterestedComponentMap.delete(key); 468 updateCallbackNeeded = true; 469 } else { 470 mInterestedComponentMap.put(key, currentCount - 1); 471 } 472 } 473 if (updateCallbackNeeded) { 474 filter = createFilterFromInterestedComponentsLocked(); 475 } 476 } 477 if (updateCallbackNeeded) { 478 updatePowerPolicyChangeCallback(filter); 479 } 480 } 481 setServiceForListenerLocked(boolean useCompletion)482 private void setServiceForListenerLocked(boolean useCompletion) { 483 if (mListenerToService == null) { 484 ICarPowerStateListener listenerToService = new ICarPowerStateListener.Stub() { 485 @Override 486 public void onStateChanged(int state) throws RemoteException { 487 if (useCompletion) { 488 CarPowerStateListenerWithCompletion listenerWithCompletion; 489 CompletableFuture<Void> future; 490 synchronized (mLock) { 491 // Update CompletableFuture. This will recreate it or just clean it up. 492 updateFutureLocked(state); 493 listenerWithCompletion = mListenerWithCompletion; 494 future = mFuture; 495 } 496 // Notify user that the state has changed and supply a future 497 if (listenerWithCompletion != null) { 498 listenerWithCompletion.onStateChanged(state, future); 499 } 500 } else { 501 CarPowerStateListener listener; 502 synchronized (mLock) { 503 listener = mListener; 504 } 505 // Notify the user without supplying a future 506 if (listener != null) { 507 listener.onStateChanged(state); 508 } 509 } 510 } 511 }; 512 try { 513 if (useCompletion) { 514 mService.registerListenerWithCompletion(listenerToService); 515 } else { 516 mService.registerListener(listenerToService); 517 } 518 mListenerToService = listenerToService; 519 } catch (RemoteException e) { 520 handleRemoteExceptionFromCarService(e); 521 } 522 } 523 } 524 updateFutureLocked(int state)525 private void updateFutureLocked(int state) { 526 cleanupFutureLocked(); 527 if (state == CarPowerStateListener.SHUTDOWN_PREPARE) { 528 // Create a CompletableFuture and pass it to the listener. 529 // When the listener completes the future, tell 530 // CarPowerManagementService that this action is finished. 531 mFuture = new CompletableFuture<>(); 532 mFuture.whenComplete((result, exception) -> { 533 if (exception != null && !(exception instanceof CancellationException)) { 534 Log.e(TAG, "Exception occurred while waiting for future", exception); 535 } 536 ICarPowerStateListener listenerToService; 537 synchronized (mLock) { 538 listenerToService = mListenerToService; 539 } 540 try { 541 mService.finished(listenerToService); 542 } catch (RemoteException e) { 543 handleRemoteExceptionFromCarService(e); 544 } 545 }); 546 } 547 } 548 cleanupFutureLocked()549 private void cleanupFutureLocked() { 550 if (mFuture != null) { 551 if (!mFuture.isDone()) { 552 mFuture.cancel(false); 553 } 554 mFuture = null; 555 } 556 } 557 createFilterFromInterestedComponentsLocked()558 private CarPowerPolicyFilter createFilterFromInterestedComponentsLocked() { 559 CarPowerPolicyFilter newFilter = null; 560 int componentCount = mInterestedComponentMap.size(); 561 if (componentCount != 0) { 562 int[] components = new int[componentCount]; 563 for (int i = 0; i < componentCount; i++) { 564 components[i] = mInterestedComponentMap.keyAt(i); 565 } 566 newFilter = new CarPowerPolicyFilter(components); 567 } 568 return newFilter; 569 } 570 updatePowerPolicyChangeCallback(CarPowerPolicyFilter filter)571 private void updatePowerPolicyChangeCallback(CarPowerPolicyFilter filter) { 572 try { 573 if (filter == null) { 574 mService.removePowerPolicyListener(mPolicyChangeBinderCallback); 575 } else { 576 mService.addPowerPolicyListener(filter, mPolicyChangeBinderCallback); 577 } 578 } catch (RemoteException e) { 579 handleRemoteExceptionFromCarService(e); 580 } 581 } 582 notifyPowerPolicyListeners(CarPowerPolicy appliedPolicy, CarPowerPolicy accumulatedPolicy)583 private void notifyPowerPolicyListeners(CarPowerPolicy appliedPolicy, 584 CarPowerPolicy accumulatedPolicy) { 585 ArrayList<Pair<CarPowerPolicyListener, Executor>> listeners = new ArrayList<>(); 586 synchronized (mLock) { 587 for (int i = 0; i < mPolicyListenerMap.size(); i++) { 588 CarPowerPolicyListener listener = mPolicyListenerMap.keyAt(i); 589 Pair<Executor, CarPowerPolicyFilter> pair = mPolicyListenerMap.valueAt(i); 590 if (PowerComponentUtil.hasComponents(appliedPolicy, pair.second)) { 591 listeners.add( 592 new Pair<CarPowerPolicyListener, Executor>(listener, pair.first)); 593 } 594 } 595 } 596 for (int i = 0; i < listeners.size(); i++) { 597 Pair<CarPowerPolicyListener, Executor> pair = listeners.get(i); 598 pair.second.execute(() -> pair.first.onPolicyChanged(accumulatedPolicy)); 599 } 600 } 601 assertPermission(String permission)602 private void assertPermission(String permission) { 603 if (getContext().checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) { 604 throw new SecurityException("requires " + permission); 605 } 606 } 607 checkArgument(boolean test, String message)608 private void checkArgument(boolean test, String message) { 609 if (!test) { 610 throw new IllegalArgumentException(message); 611 } 612 } 613 614 /** @hide */ 615 @Override onCarDisconnected()616 public void onCarDisconnected() { 617 synchronized (mLock) { 618 mListener = null; 619 mListenerWithCompletion = null; 620 } 621 } 622 } 623