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