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