• 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.server.policy;
18 
19 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
20 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.hardware.Sensor;
29 import android.hardware.SensorEvent;
30 import android.hardware.SensorEventListener;
31 import android.hardware.SensorManager;
32 import android.hardware.devicestate.DeviceState;
33 import android.os.Environment;
34 import android.os.PowerManager;
35 import android.util.ArrayMap;
36 import android.util.ArraySet;
37 import android.util.Slog;
38 import android.util.SparseArray;
39 
40 import com.android.internal.annotations.GuardedBy;
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.internal.util.Preconditions;
43 import com.android.server.LocalServices;
44 import com.android.server.devicestate.DeviceStateProvider;
45 import com.android.server.input.InputManagerInternal;
46 import com.android.server.policy.devicestate.config.Conditions;
47 import com.android.server.policy.devicestate.config.DeviceStateConfig;
48 import com.android.server.policy.devicestate.config.Flags;
49 import com.android.server.policy.devicestate.config.LidSwitchCondition;
50 import com.android.server.policy.devicestate.config.NumericRange;
51 import com.android.server.policy.devicestate.config.Properties;
52 import com.android.server.policy.devicestate.config.SensorCondition;
53 import com.android.server.policy.devicestate.config.XmlParser;
54 
55 import org.xmlpull.v1.XmlPullParserException;
56 
57 import java.io.BufferedInputStream;
58 import java.io.File;
59 import java.io.FileInputStream;
60 import java.io.IOException;
61 import java.io.InputStream;
62 import java.io.PrintWriter;
63 import java.math.BigDecimal;
64 import java.util.ArrayList;
65 import java.util.Arrays;
66 import java.util.Comparator;
67 import java.util.HashSet;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Set;
71 import java.util.function.BooleanSupplier;
72 
73 import javax.xml.datatype.DatatypeConfigurationException;
74 
75 /**
76  * Implementation of {@link DeviceStateProvider} that reads the set of supported device states
77  * from a configuration file provided at either /vendor/etc/devicestate or
78  * /data/system/devicestate/.
79  * <p>
80  * When a device state configuration file is present this provider will consider the provided
81  * {@link Conditions} block for each declared state, halting and returning when the first set of
82  * conditions for a device state match the current system state. If there are multiple states whose
83  * conditions match the current system state the matching state with the smallest integer identifier
84  * will be returned. When no declared state matches the current system state, the device state with
85  * the smallest integer identifier will be returned.
86  * <p>
87  * By default, the provider reports {@link #DEFAULT_DEVICE_STATE} when no configuration file is
88  * provided.
89  */
90 public final class DeviceStateProviderImpl implements DeviceStateProvider,
91         InputManagerInternal.LidSwitchCallback, SensorEventListener,
92         PowerManager.OnThermalStatusChangedListener {
93     private static final String TAG = "DeviceStateProviderImpl";
94     private static final boolean DEBUG = false;
95 
96     private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
97     private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false;
98 
99     @VisibleForTesting
100     static final DeviceState DEFAULT_DEVICE_STATE =
101             new DeviceState(new DeviceState.Configuration.Builder(MINIMUM_DEVICE_STATE_IDENTIFIER,
102                     "DEFAULT").build());
103 
104     private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
105     private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
106     private static final String CONFIG_FILE_NAME = "device_state_configuration.xml";
107     private static final String PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED =
108             "com.android.server.policy.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED";
109     private static final String PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN =
110             "com.android.server.policy.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN";
111     private static final String PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN =
112             "com.android.server.policy.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN";
113     private static final String PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS =
114             "com.android.server.policy.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS";
115     private static final String PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
116             "com.android.server.policy.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP";
117     private static final String PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL =
118             "com.android.server.policy.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL";
119     private static final String PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE =
120             "com.android.server.policy.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE";
121     private static final String PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST =
122             "com.android.server.policy.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST";
123     private static final String PROPERTY_APP_INACCESSIBLE =
124             "com.android.server.policy.PROPERTY_APP_INACCESSIBLE";
125     private static final String PROPERTY_EMULATED_ONLY =
126             "com.android.server.policy.PROPERTY_EMULATED_ONLY";
127     private static final String PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY =
128             "com.android.server.policy.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY";
129     private static final String PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY =
130             "com.android.server.policy.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY";
131     private static final String PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP =
132             "com.android.server.policy.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP";
133     private static final String PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE =
134             "com.android.server.policy.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE";
135     private static final String PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY =
136             "com.android.server.policy.PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY";
137     private static final String PROPERTY_FEATURE_REAR_DISPLAY =
138             "com.android.server.policy.PROPERTY_FEATURE_REAR_DISPLAY";
139     private static final String PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT =
140             "com.android.server.policy.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT";
141     private static final String PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT =
142             "com.android.server.policy.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT";
143 
144     // Deprecated flag definitions to maintain backwards compatibility.
145     private static final String FLAG_CANCEL_OVERRIDE_REQUESTS = "FLAG_CANCEL_OVERRIDE_REQUESTS";
146     private static final String FLAG_APP_INACCESSIBLE = "FLAG_APP_INACCESSIBLE";
147     private static final String FLAG_EMULATED_ONLY = "FLAG_EMULATED_ONLY";
148     private static final String FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP =
149             "FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP";
150     private static final String FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL =
151             "FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL";
152     private static final String FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE =
153             "FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE";
154 
155     /** Interface that allows reading the device state configuration. */
156     interface ReadableConfig {
157         @NonNull
openRead()158         InputStream openRead() throws IOException;
159     }
160 
161     /**
162      * Returns a new {@link DeviceStateProviderImpl} instance.
163      *
164      * @param context the {@link Context} that should be used to access system services.
165      */
create(@onNull Context context)166     public static DeviceStateProviderImpl create(@NonNull Context context) {
167         File configFile = getConfigurationFile();
168         if (configFile == null) {
169             return createFromConfig(context, null);
170         }
171         return createFromConfig(context, new ReadableFileConfig(configFile));
172     }
173 
174     /**
175      * Returns a new {@link DeviceStateProviderImpl} instance.
176      *
177      * @param context the {@link Context} that should be used to access system services.
178      * @param readableConfig the config the provider instance should read supported states from.
179      */
180     @VisibleForTesting
createFromConfig(@onNull Context context, @Nullable ReadableConfig readableConfig)181     static DeviceStateProviderImpl createFromConfig(@NonNull Context context,
182             @Nullable ReadableConfig readableConfig) {
183         List<DeviceState> deviceStateList = new ArrayList<>();
184         List<Conditions> conditionsList = new ArrayList<>();
185 
186         if (readableConfig != null) {
187             DeviceStateConfig config = parseConfig(readableConfig);
188             if (config != null) {
189                 for (com.android.server.policy.devicestate.config.DeviceState stateConfig :
190                         config.getDeviceState()) {
191                     final int state = stateConfig.getIdentifier().intValue();
192                     final String name = stateConfig.getName() == null ? "" : stateConfig.getName();
193 
194                     Set<@DeviceState.DeviceStateProperties Integer> systemProperties =
195                             new HashSet<>();
196                     Set<@DeviceState.DeviceStateProperties Integer> physicalProperties =
197                             new HashSet<>();
198                     final Properties configProperties = stateConfig.getProperties();
199                     if (configProperties != null) {
200                         List<String> configPropertyStrings = configProperties.getProperty();
201                         for (int i = 0; i < configPropertyStrings.size(); i++) {
202                             final String configPropertyString = configPropertyStrings.get(i);
203                             addPropertyByString(configPropertyString, systemProperties,
204                                     physicalProperties);
205                         }
206                     }
207 
208                     if (android.hardware.devicestate.feature.flags
209                             .Flags.deviceStateConfigurationFlag()) {
210                         // Parse through the deprecated flag configuration to keep compatibility.
211                         final Flags configFlags = stateConfig.getFlags();
212                         if (configFlags != null) {
213                             List<String> configFlagStrings = configFlags.getFlag();
214                             for (int i = 0; i < configFlagStrings.size(); i++) {
215                                 final String configFlagString = configFlagStrings.get(i);
216                                 addFlagByString(configFlagString, systemProperties);
217                             }
218                         }
219                     }
220 
221                     DeviceState.Configuration deviceStateConfiguration =
222                             new DeviceState.Configuration.Builder(state, name)
223                                     .setSystemProperties(systemProperties)
224                                     .setPhysicalProperties(physicalProperties)
225                                     .build();
226                     deviceStateList.add(new DeviceState(deviceStateConfiguration));
227 
228                     final Conditions condition = stateConfig.getConditions();
229                     conditionsList.add(condition);
230                 }
231             }
232         }
233 
234         if (deviceStateList.isEmpty()) {
235             deviceStateList.add(DEFAULT_DEVICE_STATE);
236             conditionsList.add(null);
237         }
238         return new DeviceStateProviderImpl(context, deviceStateList, conditionsList);
239     }
240 
addPropertyByString(String propertyString, Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties, Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties)241     private static void addPropertyByString(String propertyString,
242             Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties,
243             Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties) {
244         switch (propertyString) {
245             // Look for the physical hardware properties first
246             case PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED:
247                 physicalProperties.add(
248                         DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED);
249                 break;
250             case PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN:
251                 physicalProperties.add(
252                         DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN);
253                 break;
254             case PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN:
255                 physicalProperties.add(
256                         DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN);
257                 break;
258             case PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS:
259                 systemProperties.add(
260                         DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS);
261                 break;
262             case PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP:
263                 systemProperties.add(
264                         DeviceState.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
265                 break;
266             case PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL:
267                 systemProperties.add(
268                         DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL);
269                 break;
270             case PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE:
271                 systemProperties.add(
272                         DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE);
273                 break;
274             case PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST:
275                 systemProperties.add(
276                         DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST);
277                 break;
278             case PROPERTY_APP_INACCESSIBLE:
279                 systemProperties.add(DeviceState.PROPERTY_APP_INACCESSIBLE);
280                 break;
281             case PROPERTY_EMULATED_ONLY:
282                 systemProperties.add(DeviceState.PROPERTY_EMULATED_ONLY);
283                 break;
284             case PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY:
285                 systemProperties.add(
286                         DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
287                 break;
288             case PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY:
289                 systemProperties.add(
290                         DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY);
291                 break;
292             case PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP:
293                 systemProperties.add(
294                         DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP);
295                 break;
296             case PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE:
297                 systemProperties.add(
298                         DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE);
299                 break;
300             case PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY:
301                 systemProperties.add(
302                         DeviceState.PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY);
303                 break;
304             case PROPERTY_FEATURE_REAR_DISPLAY:
305                 systemProperties.add(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY);
306                 break;
307             case PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT:
308                 systemProperties.add(DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT);
309                 break;
310             case PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT:
311                 systemProperties.add(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
312                 break;
313             default:
314                 Slog.w(TAG, "Parsed unknown flag with name: " + propertyString);
315                 break;
316         }
317     }
318 
addFlagByString(String flagString, Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties)319     private static void addFlagByString(String flagString,
320             Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties) {
321         switch (flagString) {
322             case FLAG_APP_INACCESSIBLE:
323                 systemProperties.add(DeviceState.PROPERTY_APP_INACCESSIBLE);
324                 break;
325             case FLAG_EMULATED_ONLY:
326                 systemProperties.add(DeviceState.PROPERTY_EMULATED_ONLY);
327                 break;
328             case FLAG_CANCEL_OVERRIDE_REQUESTS:
329                 systemProperties.add(DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS);
330                 break;
331             case FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP:
332                 systemProperties.add(DeviceState.PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP);
333                 break;
334             case FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE:
335                 systemProperties.add(DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE);
336                 break;
337             case FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL:
338                 systemProperties.add(
339                         DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL);
340                 break;
341             default:
342                 Slog.w(TAG, "Parsed unknown flag with name: " + flagString);
343                 break;
344         }
345     }
346 
347     // Lock for internal state.
348     private final Object mLock = new Object();
349     private final Context mContext;
350     // List of supported states in ascending order based on their identifier.
351     private final DeviceState[] mOrderedStates;
352     // Map of state identifier to a boolean supplier that returns true when all required conditions
353     // are met for the device to be in the state.
354     private final SparseArray<BooleanSupplier> mStateConditions = new SparseArray<>();
355 
356     @Nullable
357     @GuardedBy("mLock")
358     private Listener mListener = null;
359     @GuardedBy("mLock")
360     private int mLastReportedState = INVALID_DEVICE_STATE_IDENTIFIER;
361 
362     @GuardedBy("mLock")
363     private Boolean mIsLidOpen;
364     @GuardedBy("mLock")
365     private final Map<Sensor, SensorEvent> mLatestSensorEvent = new ArrayMap<>();
366     @GuardedBy("mLock")
367     private @PowerManager.ThermalStatus int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
368 
369     @GuardedBy("mLock")
370     private boolean mPowerSaveModeEnabled;
371 
DeviceStateProviderImpl(@onNull Context context, @NonNull List<DeviceState> deviceStates, @NonNull List<Conditions> stateConditions)372     private DeviceStateProviderImpl(@NonNull Context context,
373             @NonNull List<DeviceState> deviceStates,
374             @NonNull List<Conditions> stateConditions) {
375         Preconditions.checkArgument(deviceStates.size() == stateConditions.size(),
376                 "Number of device states must be equal to the number of device state conditions.");
377 
378         mContext = context;
379 
380         DeviceState[] orderedStates = deviceStates.toArray(new DeviceState[deviceStates.size()]);
381         Arrays.sort(orderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
382         mOrderedStates = orderedStates;
383 
384         setStateConditions(deviceStates, stateConditions);
385 
386         PowerManager powerManager = context.getSystemService(PowerManager.class);
387         if (powerManager != null) {
388             // If any of the device states are thermal sensitive, i.e. it should be disabled when
389             // the device is overheating, then we will update the list of supported states when
390             // thermal status changes.
391             if (hasThermalSensitiveState(deviceStates)) {
392                 powerManager.addThermalStatusListener(this);
393             }
394 
395             // If any of the device states are power sensitive, i.e. it should be disabled when
396             // power save mode is enabled, then we will update the list of supported states when
397             // power save mode is toggled.
398             if (hasPowerSaveSensitiveState(deviceStates)) {
399                 IntentFilter filter = new IntentFilter(
400                         PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
401                 BroadcastReceiver receiver = new BroadcastReceiver() {
402                     @Override
403                     public void onReceive(Context context, Intent intent) {
404                         if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL.equals(
405                                 intent.getAction())) {
406                             onPowerSaveModeChanged(powerManager.isPowerSaveMode());
407                         }
408                     }
409                 };
410                 mContext.registerReceiver(receiver, filter);
411             }
412         }
413     }
414 
setStateConditions(@onNull List<DeviceState> deviceStates, @NonNull List<Conditions> stateConditions)415     private void setStateConditions(@NonNull List<DeviceState> deviceStates,
416             @NonNull List<Conditions> stateConditions) {
417         // Whether or not this instance should register to receive lid switch notifications from
418         // InputManagerInternal. If there are no device state conditions that are based on the lid
419         // switch there is no need to register for a callback.
420         boolean shouldListenToLidSwitch = false;
421 
422         // The set of Sensor(s) that this instance should register to receive SensorEvent(s) from.
423         final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>();
424 
425         for (int i = 0; i < stateConditions.size(); i++) {
426             final int state = deviceStates.get(i).getIdentifier();
427             if (DEBUG) {
428                 Slog.d(TAG, "Evaluating conditions for device state " + state
429                         + " (" + deviceStates.get(i).getName() + ")");
430             }
431             final Conditions conditions = stateConditions.get(i);
432             if (conditions == null) {
433                 // If this state has the FLAG_EMULATED_ONLY flag on it, it should never be triggered
434                 // by a physical hardware change, and should always return false for it's conditions
435                 if (deviceStates.get(i).hasProperty(DeviceState.PROPERTY_EMULATED_ONLY)) {
436                     mStateConditions.put(state, FALSE_BOOLEAN_SUPPLIER);
437                 } else {
438                     mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
439                 }
440                 continue;
441             }
442 
443             // Whether or not all the required hardware components could be found that match the
444             // requirements from the config.
445             boolean allRequiredComponentsFound = true;
446             // Whether or not this condition requires the lid switch.
447             boolean lidSwitchRequired = false;
448             // Set of sensors required for this condition.
449             ArraySet<Sensor> sensorsRequired = new ArraySet<>();
450 
451             List<BooleanSupplier> suppliers = new ArrayList<>();
452 
453             LidSwitchCondition lidSwitchCondition = conditions.getLidSwitch();
454             if (lidSwitchCondition != null) {
455                 suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen()));
456                 lidSwitchRequired = true;
457                 if (DEBUG) {
458                     Slog.d(TAG, "Lid switch required");
459                 }
460             }
461 
462             List<SensorCondition> sensorConditions = conditions.getSensor();
463             for (int j = 0; j < sensorConditions.size(); j++) {
464                 SensorCondition sensorCondition = sensorConditions.get(j);
465                 final String expectedSensorType = sensorCondition.getType();
466                 final String expectedSensorName = sensorCondition.getName();
467 
468                 final Sensor foundSensor = findSensor(expectedSensorType, expectedSensorName);
469                 if (foundSensor == null) {
470                     Slog.e(TAG, "Failed to find Sensor with type: " + expectedSensorType
471                             + " and name: " + expectedSensorName);
472                     allRequiredComponentsFound = false;
473                     break;
474                 }
475 
476                 if (DEBUG) {
477                     Slog.d(TAG, "Found sensor with type: " + expectedSensorType
478                             + " (" + expectedSensorName + ")");
479                 }
480 
481                 suppliers.add(new SensorBooleanSupplier(foundSensor, sensorCondition.getValue()));
482                 sensorsRequired.add(foundSensor);
483             }
484 
485             if (allRequiredComponentsFound) {
486                 shouldListenToLidSwitch |= lidSwitchRequired;
487                 sensorsToListenTo.addAll(sensorsRequired);
488 
489                 if (suppliers.size() > 1) {
490                     mStateConditions.put(state, new AndBooleanSupplier(suppliers));
491                 } else if (suppliers.size() > 0) {
492                     // No need to wrap with an AND supplier if there is only 1.
493                     mStateConditions.put(state, suppliers.get(0));
494                 } else {
495                     // There are no conditions for this state. Default to always true.
496                     mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
497                 }
498             } else {
499                 // Failed to setup this condition. This can happen if a sensor is missing. Default
500                 // this state to always false.
501                 mStateConditions.put(state, FALSE_BOOLEAN_SUPPLIER);
502             }
503         }
504 
505         if (shouldListenToLidSwitch) {
506             InputManagerInternal inputManager = LocalServices.getService(
507                     InputManagerInternal.class);
508             inputManager.registerLidSwitchCallback(this);
509         }
510 
511         final SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
512         for (int i = 0; i < sensorsToListenTo.size(); i++) {
513             Sensor sensor = sensorsToListenTo.valueAt(i);
514             sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
515         }
516     }
517 
518     @Nullable
findSensor(String type, String name)519     private Sensor findSensor(String type, String name) {
520         final SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
521         final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
522         for (int sensorIndex = 0; sensorIndex < sensors.size(); sensorIndex++) {
523             final Sensor sensor = sensors.get(sensorIndex);
524             final String sensorType = sensor.getStringType();
525             final String sensorName = sensor.getName();
526 
527             if (sensorType == null || sensorName == null) {
528                 continue;
529             }
530 
531             if (sensorType.equals(type) && sensorName.equals(name)) {
532                 return sensor;
533             }
534         }
535         return null;
536     }
537 
538     @Override
setListener(Listener listener)539     public void setListener(Listener listener) {
540         synchronized (mLock) {
541             if (mListener != null) {
542                 throw new RuntimeException("Provider already has a listener set.");
543             }
544             mListener = listener;
545         }
546         notifySupportedStatesChanged(SUPPORTED_DEVICE_STATES_CHANGED_INITIALIZED);
547         notifyDeviceStateChangedIfNeeded();
548     }
549 
550     /** Notifies the listener that the set of supported device states has changed. */
notifySupportedStatesChanged(@upportedStatesUpdatedReason int reason)551     private void notifySupportedStatesChanged(@SupportedStatesUpdatedReason int reason) {
552         List<DeviceState> supportedStates = new ArrayList<>();
553         Listener listener;
554         synchronized (mLock) {
555             if (mListener == null) {
556                 return;
557             }
558             listener = mListener;
559             for (DeviceState deviceState : mOrderedStates) {
560                 if (isThermalStatusCriticalOrAbove(mThermalStatus) && deviceState.hasProperty(
561                         DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL
562                 )) {
563                     continue;
564                 }
565                 if (mPowerSaveModeEnabled && deviceState.hasProperty(
566                         DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
567                     continue;
568                 }
569                 supportedStates.add(deviceState);
570             }
571         }
572 
573         listener.onSupportedDeviceStatesChanged(
574                 supportedStates.toArray(new DeviceState[supportedStates.size()]),
575                 reason);
576     }
577 
578     /** Computes the current device state and notifies the listener of a change, if needed. */
notifyDeviceStateChangedIfNeeded()579     void notifyDeviceStateChangedIfNeeded() {
580         int stateToReport = INVALID_DEVICE_STATE_IDENTIFIER;
581         synchronized (mLock) {
582             if (mListener == null) {
583                 return;
584             }
585 
586             int newState = INVALID_DEVICE_STATE_IDENTIFIER;
587             for (int i = 0; i < mOrderedStates.length; i++) {
588                 int state = mOrderedStates[i].getIdentifier();
589                 if (DEBUG) {
590                     Slog.d(TAG, "Checking conditions for " + mOrderedStates[i].getName() + "("
591                             + i + ")");
592                 }
593                 boolean conditionSatisfied;
594                 try {
595                     conditionSatisfied = mStateConditions.get(state).getAsBoolean();
596                 } catch (IllegalStateException e) {
597                     // Failed to compute the current state based on current available data. Continue
598                     // with the expectation that notifyDeviceStateChangedIfNeeded() will be called
599                     // when a callback with the missing data is triggered. May trigger another state
600                     // change if another state is satisfied currently.
601                     if (DEBUG) {
602                         Slog.d(TAG, "Unable to check current state", e);
603                     }
604                     continue;
605                 }
606 
607                 if (conditionSatisfied) {
608                     if (DEBUG) {
609                         Slog.d(TAG, "Device State conditions satisfied, transition to " + state);
610                     }
611                     newState = state;
612                     break;
613                 }
614             }
615             if (newState == INVALID_DEVICE_STATE_IDENTIFIER) {
616                 Slog.e(TAG, "No declared device states match any of the required conditions.");
617                 dumpSensorValues();
618             }
619 
620             if (newState != INVALID_DEVICE_STATE_IDENTIFIER && newState != mLastReportedState) {
621                 mLastReportedState = newState;
622                 stateToReport = newState;
623             }
624         }
625 
626         if (stateToReport != INVALID_DEVICE_STATE_IDENTIFIER) {
627             mListener.onStateChanged(stateToReport);
628         }
629     }
630 
631     @Override
notifyLidSwitchChanged(long whenNanos, boolean lidOpen)632     public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
633         synchronized (mLock) {
634             mIsLidOpen = lidOpen;
635         }
636         if (DEBUG) {
637             Slog.d(TAG, "Lid switch state: " + (lidOpen ? "open" : "closed"));
638         }
639         notifyDeviceStateChangedIfNeeded();
640     }
641 
642     @Override
onSensorChanged(SensorEvent event)643     public void onSensorChanged(SensorEvent event) {
644         synchronized (mLock) {
645             mLatestSensorEvent.put(event.sensor, event);
646         }
647         notifyDeviceStateChangedIfNeeded();
648     }
649 
650     @Override
onAccuracyChanged(Sensor sensor, int accuracy)651     public void onAccuracyChanged(Sensor sensor, int accuracy) {
652         // Do nothing.
653     }
654 
655     @Override
dump(@onNull PrintWriter writer, @Nullable String[] args)656     public void dump(@NonNull PrintWriter writer, @Nullable String[] args) {
657         writer.println("DeviceStateProviderImpl");
658 
659         synchronized (mLock) {
660             writer.println("  mLastReportedState = " + mLastReportedState);
661             writer.println("  mPowerSaveModeEnabled = " + mPowerSaveModeEnabled);
662             writer.println("  mThermalStatus = " + mThermalStatus);
663             writer.println("  mIsLidOpen = " + mIsLidOpen);
664             writer.println("  Sensor values:");
665 
666             for (Sensor sensor : mLatestSensorEvent.keySet()) {
667                 SensorEvent sensorEvent = mLatestSensorEvent.get(sensor);
668                 writer.println("   - " + toSensorValueString(sensor, sensorEvent));
669             }
670         }
671     }
672 
673     /**
674      * Implementation of {@link BooleanSupplier} that returns {@code true} if the expected lid
675      * switch open state matches {@link #mIsLidOpen}.
676      */
677     private final class LidSwitchBooleanSupplier implements BooleanSupplier {
678         private final boolean mExpectedOpen;
679 
LidSwitchBooleanSupplier(boolean expectedOpen)680         LidSwitchBooleanSupplier(boolean expectedOpen) {
681             mExpectedOpen = expectedOpen;
682         }
683 
684         @Override
getAsBoolean()685         public boolean getAsBoolean() {
686             synchronized (mLock) {
687                 if (mIsLidOpen == null) {
688                     throw new IllegalStateException("Have not received lid switch value.");
689                 }
690 
691                 return mIsLidOpen == mExpectedOpen;
692             }
693         }
694     }
695 
696     /**
697      * Implementation of {@link BooleanSupplier} that returns {@code true} if the latest
698      * {@link SensorEvent#values sensor event values} for the specified {@link Sensor} adhere to
699      * the supplied {@link NumericRange ranges}.
700      */
701     private final class SensorBooleanSupplier implements BooleanSupplier {
702         @NonNull
703         private final Sensor mSensor;
704         @NonNull
705         private final List<NumericRange> mExpectedValues;
706 
SensorBooleanSupplier(@onNull Sensor sensor, @NonNull List<NumericRange> expectedValues)707         SensorBooleanSupplier(@NonNull Sensor sensor, @NonNull List<NumericRange> expectedValues) {
708             mSensor = sensor;
709             mExpectedValues = expectedValues;
710         }
711 
712         @Override
getAsBoolean()713         public boolean getAsBoolean() {
714             synchronized (mLock) {
715                 SensorEvent latestEvent = mLatestSensorEvent.get(mSensor);
716                 if (latestEvent == null) {
717                     throw new IllegalStateException("Have not received sensor event.");
718                 }
719 
720                 if (latestEvent.values.length < mExpectedValues.size()) {
721                     throw new RuntimeException("Number of supplied numeric range(s) does not "
722                             + "match the number of values in the latest sensor event for sensor: "
723                             + mSensor);
724                 }
725 
726                 for (int i = 0; i < mExpectedValues.size(); i++) {
727                     if (!adheresToRange(latestEvent.values[i], mExpectedValues.get(i))) {
728                         return false;
729                     }
730                 }
731                 return true;
732             }
733         }
734 
735         /**
736          * Returns {@code true} if the supplied {@code value} adheres to the constraints specified
737          * in {@code range}.
738          */
adheresToRange(float value, @NonNull NumericRange range)739         private boolean adheresToRange(float value, @NonNull NumericRange range) {
740             final BigDecimal min = range.getMin_optional();
741             if (min != null) {
742                 if (DEBUG) {
743                     Slog.d(TAG, "value: " + value + ", constraint min: " + min.floatValue());
744                 }
745                 if (value <= min.floatValue()) {
746                     return false;
747                 }
748             }
749 
750             final BigDecimal minInclusive = range.getMinInclusive_optional();
751             if (minInclusive != null) {
752                 if (DEBUG) {
753                     Slog.d(TAG, "value: " + value + ", constraint min-inclusive: "
754                             + minInclusive.floatValue());
755                 }
756                 if (value < minInclusive.floatValue()) {
757                     return false;
758                 }
759             }
760 
761             final BigDecimal max = range.getMax_optional();
762             if (max != null) {
763                 if (DEBUG) {
764                     Slog.d(TAG, "value: " + value + ", constraint max: " + max.floatValue());
765                 }
766                 if (value >= max.floatValue()) {
767                     return false;
768                 }
769             }
770 
771             final BigDecimal maxInclusive = range.getMaxInclusive_optional();
772             if (maxInclusive != null) {
773                 if (DEBUG) {
774                     Slog.d(TAG, "value: " + value + ", constraint max-inclusive: "
775                             + maxInclusive.floatValue());
776                 }
777                 if (value > maxInclusive.floatValue()) {
778                     return false;
779                 }
780             }
781 
782             return true;
783         }
784     }
785 
786     /**
787      * Implementation of {@link BooleanSupplier} whose result is the product of an AND operation
788      * applied to the result of all child suppliers.
789      */
790     private static final class AndBooleanSupplier implements BooleanSupplier {
791         @NonNull
792         List<BooleanSupplier> mBooleanSuppliers;
793 
AndBooleanSupplier(@onNull List<BooleanSupplier> booleanSuppliers)794         AndBooleanSupplier(@NonNull List<BooleanSupplier> booleanSuppliers) {
795             mBooleanSuppliers = booleanSuppliers;
796         }
797 
798         @Override
getAsBoolean()799         public boolean getAsBoolean() {
800             for (int i = 0; i < mBooleanSuppliers.size(); i++) {
801                 if (!mBooleanSuppliers.get(i).getAsBoolean()) {
802                     return false;
803                 }
804             }
805             return true;
806         }
807     }
808 
809     /**
810      * Returns the device state configuration file that should be used, or {@code null} if no file
811      * is present on the device.
812      * <p>
813      * Defaults to returning a config file present in the data/ dir at
814      * {@link #DATA_CONFIG_FILE_PATH}, and then falls back to the config file in the vendor/ dir
815      * at {@link #VENDOR_CONFIG_FILE_PATH} if no config file is found in the data/ dir.
816      */
817     @Nullable
getConfigurationFile()818     private static File getConfigurationFile() {
819         final File configFileFromDataDir = Environment.buildPath(Environment.getDataDirectory(),
820                 DATA_CONFIG_FILE_PATH, CONFIG_FILE_NAME);
821         if (configFileFromDataDir.exists()) {
822             return configFileFromDataDir;
823         }
824 
825         final File configFileFromVendorDir = Environment.buildPath(Environment.getVendorDirectory(),
826                 VENDOR_CONFIG_FILE_PATH, CONFIG_FILE_NAME);
827         if (configFileFromVendorDir.exists()) {
828             return configFileFromVendorDir;
829         }
830 
831         return null;
832     }
833 
834     @GuardedBy("mLock")
dumpSensorValues()835     private void dumpSensorValues() {
836         Slog.i(TAG, "Sensor values:");
837         for (Sensor sensor : mLatestSensorEvent.keySet()) {
838             SensorEvent sensorEvent = mLatestSensorEvent.get(sensor);
839             Slog.i(TAG, toSensorValueString(sensor, sensorEvent));
840         }
841     }
842 
toSensorValueString(Sensor sensor, @Nullable SensorEvent event)843     private String toSensorValueString(Sensor sensor, @Nullable SensorEvent event) {
844         String sensorString = sensor == null ? "null" : sensor.getName();
845         String eventValues = event == null ? "null" : Arrays.toString(event.values);
846         return sensorString + " : " + eventValues;
847     }
848 
849     /**
850      * Tries to parse the provided file into a {@link DeviceStateConfig} object. Returns
851      * {@code null} if the file could not be successfully parsed.
852      */
853     @Nullable
parseConfig(@onNull ReadableConfig readableConfig)854     private static DeviceStateConfig parseConfig(@NonNull ReadableConfig readableConfig) {
855         try (InputStream in = readableConfig.openRead();
856                 InputStream bin = new BufferedInputStream(in)) {
857             return XmlParser.read(bin);
858         } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
859             Slog.e(TAG, "Encountered an error while reading device state config", e);
860         }
861         return null;
862     }
863 
864     /** Implementation of {@link ReadableConfig} that reads config data from a file. */
865     private static final class ReadableFileConfig implements ReadableConfig {
866         @NonNull
867         private final File mFile;
868 
ReadableFileConfig(@onNull File file)869         private ReadableFileConfig(@NonNull File file) {
870             mFile = file;
871         }
872 
873         @Override
openRead()874         public InputStream openRead() throws IOException {
875             return new FileInputStream(mFile);
876         }
877     }
878 
879     @VisibleForTesting
onPowerSaveModeChanged(boolean isPowerSaveModeEnabled)880     void onPowerSaveModeChanged(boolean isPowerSaveModeEnabled) {
881         synchronized (mLock) {
882             if (mPowerSaveModeEnabled != isPowerSaveModeEnabled) {
883                 mPowerSaveModeEnabled = isPowerSaveModeEnabled;
884                 notifySupportedStatesChanged(
885                         isPowerSaveModeEnabled ? SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_ENABLED
886                                 : SUPPORTED_DEVICE_STATES_CHANGED_POWER_SAVE_DISABLED);
887             }
888         }
889     }
890 
891     @Override
onThermalStatusChanged(@owerManager.ThermalStatus int thermalStatus)892     public void onThermalStatusChanged(@PowerManager.ThermalStatus int thermalStatus) {
893         int previousThermalStatus;
894         synchronized (mLock) {
895             previousThermalStatus = mThermalStatus;
896             mThermalStatus = thermalStatus;
897         }
898 
899         boolean isThermalStatusCriticalOrAbove = isThermalStatusCriticalOrAbove(thermalStatus);
900         boolean isPreviousThermalStatusCriticalOrAbove =
901                 isThermalStatusCriticalOrAbove(previousThermalStatus);
902         if (isThermalStatusCriticalOrAbove != isPreviousThermalStatusCriticalOrAbove) {
903             Slog.i(TAG, "Updating supported device states due to thermal status change."
904                     + " isThermalStatusCriticalOrAbove: " + isThermalStatusCriticalOrAbove);
905             notifySupportedStatesChanged(
906                     isThermalStatusCriticalOrAbove
907                             ? SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_CRITICAL
908                             : SUPPORTED_DEVICE_STATES_CHANGED_THERMAL_NORMAL);
909         }
910     }
911 
isThermalStatusCriticalOrAbove( @owerManager.ThermalStatus int thermalStatus)912     private static boolean isThermalStatusCriticalOrAbove(
913             @PowerManager.ThermalStatus int thermalStatus) {
914         switch (thermalStatus) {
915             case PowerManager.THERMAL_STATUS_CRITICAL:
916             case PowerManager.THERMAL_STATUS_EMERGENCY:
917             case PowerManager.THERMAL_STATUS_SHUTDOWN:
918                 return true;
919             default:
920                 return false;
921         }
922     }
923 
hasThermalSensitiveState(List<DeviceState> deviceStates)924     private static boolean hasThermalSensitiveState(List<DeviceState> deviceStates) {
925         for (int i = 0; i < deviceStates.size(); i++) {
926             if (deviceStates.get(i).hasProperty(
927                     DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL)) {
928                 return true;
929             }
930         }
931         return false;
932     }
933 
hasPowerSaveSensitiveState(List<DeviceState> deviceStates)934     private static boolean hasPowerSaveSensitiveState(List<DeviceState> deviceStates) {
935         for (int i = 0; i < deviceStates.size(); i++) {
936             if (deviceStates.get(i).hasProperty(
937                     DeviceState.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE)) {
938                 return true;
939             }
940         }
941         return false;
942     }
943 }
944