• 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 android.hardware.devicestate;
18 
19 import android.annotation.CallbackExecutor;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemService;
24 import android.annotation.TestApi;
25 import android.content.Context;
26 
27 import com.android.internal.util.ArrayUtils;
28 
29 import java.util.concurrent.Executor;
30 import java.util.function.Consumer;
31 
32 /**
33  * Manages the state of the system for devices with user-configurable hardware like a foldable
34  * phone.
35  *
36  * @hide
37  */
38 @TestApi
39 @SystemService(Context.DEVICE_STATE_SERVICE)
40 public final class DeviceStateManager {
41     /**
42      * Invalid device state.
43      *
44      * @hide
45      */
46     public static final int INVALID_DEVICE_STATE = -1;
47 
48     /** The minimum allowed device state identifier. */
49     public static final int MINIMUM_DEVICE_STATE = 0;
50 
51     /** The maximum allowed device state identifier. */
52     public static final int MAXIMUM_DEVICE_STATE = 255;
53 
54     private final DeviceStateManagerGlobal mGlobal;
55 
56     /** @hide */
DeviceStateManager()57     public DeviceStateManager() {
58         DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance();
59         if (global == null) {
60             throw new IllegalStateException(
61                     "Failed to get instance of global device state manager.");
62         }
63         mGlobal = global;
64     }
65 
66     /**
67      * Returns the list of device states that are supported and can be requested with
68      * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
69      */
70     @NonNull
getSupportedStates()71     public int[] getSupportedStates() {
72         return mGlobal.getSupportedStates();
73     }
74 
75     /**
76      * Submits a {@link DeviceStateRequest request} to modify the device state.
77      * <p>
78      * By default, the request is kept active until a call to
79      * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs:
80      * <ul>
81      *     <li>Another processes submits a request succeeding this request in which case the request
82      *     will be suspended until the interrupting request is canceled.
83      *     <li>The requested state has become unsupported.
84      *     <li>The process submitting the request dies.
85      * </ul>
86      * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
87      *
88      * @throws IllegalArgumentException if the requested state is unsupported.
89      * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
90      * permission is not held.
91      *
92      * @see DeviceStateRequest
93      */
94     @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
requestState(@onNull DeviceStateRequest request, @Nullable @CallbackExecutor Executor executor, @Nullable DeviceStateRequest.Callback callback)95     public void requestState(@NonNull DeviceStateRequest request,
96             @Nullable @CallbackExecutor Executor executor,
97             @Nullable DeviceStateRequest.Callback callback) {
98         mGlobal.requestState(request, callback, executor);
99     }
100 
101     /**
102      * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
103      * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
104      * <p>
105      * This method is noop if the {@code request} has not been submitted with a call to
106      * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
107      *
108      * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
109      * permission is not held.
110      */
111     @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
cancelRequest(@onNull DeviceStateRequest request)112     public void cancelRequest(@NonNull DeviceStateRequest request) {
113         mGlobal.cancelRequest(request);
114     }
115 
116     /**
117      * Registers a callback to receive notifications about changes in device state.
118      *
119      * @param executor the executor to process notifications.
120      * @param callback the callback to register.
121      *
122      * @see DeviceStateCallback
123      */
registerCallback(@onNull @allbackExecutor Executor executor, @NonNull DeviceStateCallback callback)124     public void registerCallback(@NonNull @CallbackExecutor Executor executor,
125             @NonNull DeviceStateCallback callback) {
126         mGlobal.registerDeviceStateCallback(callback, executor);
127     }
128 
129     /**
130      * Unregisters a callback previously registered with
131      * {@link #registerCallback(Executor, DeviceStateCallback)}.
132      */
unregisterCallback(@onNull DeviceStateCallback callback)133     public void unregisterCallback(@NonNull DeviceStateCallback callback) {
134         mGlobal.unregisterDeviceStateCallback(callback);
135     }
136 
137     /** Callback to receive notifications about changes in device state. */
138     public interface DeviceStateCallback {
139         /**
140          * Called in response to a change in the states supported by the device.
141          * <p>
142          * Guaranteed to be called once on registration of the callback with the initial value and
143          * then on every subsequent change in the supported states.
144          *
145          * @param supportedStates the new supported states.
146          *
147          * @see DeviceStateManager#getSupportedStates()
148          */
onSupportedStatesChanged(@onNull int[] supportedStates)149         default void onSupportedStatesChanged(@NonNull int[] supportedStates) {}
150 
151         /**
152          * Called in response to a change in the base device state.
153          * <p>
154          * The base state is the state of the device without considering any requests made through
155          * calls to {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}
156          * from any client process. The base state is guaranteed to match the state provided with a
157          * call to {@link #onStateChanged(int)} when there are no active requests from any process.
158          * <p>
159          * Guaranteed to be called once on registration of the callback with the initial value and
160          * then on every subsequent change in the non-override state.
161          *
162          * @param state the new base device state.
163          */
onBaseStateChanged(int state)164         default void onBaseStateChanged(int state) {}
165 
166         /**
167          * Called in response to device state changes.
168          * <p>
169          * Guaranteed to be called once on registration of the callback with the initial value and
170          * then on every subsequent change in device state.
171          *
172          * @param state the new device state.
173          */
onStateChanged(int state)174         void onStateChanged(int state);
175     }
176 
177     /**
178      * Listens to changes in device state and reports the state as folded if the device state
179      * matches the value in the {@link com.android.internal.R.integer.config_foldedDeviceState}
180      * resource.
181      * @hide
182      */
183     public static class FoldStateListener implements DeviceStateCallback {
184         private final int[] mFoldedDeviceStates;
185         private final Consumer<Boolean> mDelegate;
186 
187         @Nullable
188         private Boolean lastResult;
189 
FoldStateListener(Context context, Consumer<Boolean> listener)190         public FoldStateListener(Context context, Consumer<Boolean> listener) {
191             mFoldedDeviceStates = context.getResources().getIntArray(
192                     com.android.internal.R.array.config_foldedDeviceStates);
193             mDelegate = listener;
194         }
195 
196         @Override
onStateChanged(int state)197         public final void onStateChanged(int state) {
198             final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
199 
200             if (lastResult == null || !lastResult.equals(folded)) {
201                 lastResult = folded;
202                 mDelegate.accept(folded);
203             }
204         }
205     }
206 }
207