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