• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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