1 /* 2 * Copyright (C) 2023 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.devicelockcontroller.policy; 18 19 import static com.android.devicelockcontroller.policy.FinalizationControllerImpl.FinalizationState.FINALIZED; 20 import static com.android.devicelockcontroller.policy.FinalizationControllerImpl.FinalizationState.FINALIZED_UNREPORTED; 21 import static com.android.devicelockcontroller.policy.FinalizationControllerImpl.FinalizationState.UNFINALIZED; 22 import static com.android.devicelockcontroller.policy.FinalizationControllerImpl.FinalizationState.UNINITIALIZED; 23 24 import androidx.annotation.NonNull; 25 import androidx.annotation.Nullable; 26 import androidx.annotation.VisibleForTesting; 27 28 import com.android.devicelockcontroller.policy.FinalizationControllerImpl.FinalizationState; 29 30 import com.google.common.util.concurrent.ExecutionSequencer; 31 import com.google.common.util.concurrent.Futures; 32 import com.google.common.util.concurrent.ListenableFuture; 33 34 import java.util.concurrent.Executor; 35 import java.util.concurrent.Executors; 36 37 /** 38 * Dispatch queue for any changes to the finalization state. 39 * 40 * Guarantees serialization of any state changes so that callbacks happen sequentially in the 41 * order the state changes occurred. 42 */ 43 final class FinalizationStateDispatchQueue { 44 private final ExecutionSequencer mExecutionSequencer; 45 private final Executor mBgExecutor; 46 private @Nullable StateChangeCallback mCallback; 47 private @FinalizationState int mState = UNINITIALIZED; 48 FinalizationStateDispatchQueue()49 FinalizationStateDispatchQueue() { 50 this(ExecutionSequencer.create()); 51 } 52 53 @VisibleForTesting FinalizationStateDispatchQueue(ExecutionSequencer sequencer)54 FinalizationStateDispatchQueue(ExecutionSequencer sequencer) { 55 mExecutionSequencer = sequencer; 56 mBgExecutor = Executors.newSingleThreadExecutor(); 57 } 58 59 /** 60 * Initializes the queue. 61 * 62 * @param callback callback that runs atomically with any state changes 63 */ init(@onNull StateChangeCallback callback)64 void init(@NonNull StateChangeCallback callback) { 65 mCallback = callback; 66 } 67 68 /** 69 * Enqueue a state change to be handled after all previous state change requests have resolved. 70 * 71 * Attempting to return to a previous state in the finalization process will no-op. 72 * 73 * @param newState new state to go to 74 * @return future for when the state changes and any callbacks are complete 75 */ enqueueStateChange(@inalizationState int newState)76 ListenableFuture<Void> enqueueStateChange(@FinalizationState int newState) { 77 return mExecutionSequencer.submitAsync(() -> handleStateChange(newState), mBgExecutor); 78 } 79 80 /** 81 * Handles a state change. 82 * 83 * @param newState state to change to 84 * @return future for when the state changes and any callbacks are complete 85 */ handleStateChange(@inalizationState int newState)86 private ListenableFuture<Void> handleStateChange(@FinalizationState int newState) { 87 final int oldState = mState; 88 if (oldState == newState) { 89 return Futures.immediateVoidFuture(); 90 } 91 if (!isValidStateChange(oldState, newState)) { 92 return Futures.immediateVoidFuture(); 93 } 94 mState = newState; 95 if (mCallback == null) { 96 return Futures.immediateVoidFuture(); 97 } 98 return mCallback.onStateChanged(oldState, newState); 99 } 100 isValidStateChange( @inalizationState int oldState, @FinalizationState int newState)101 private static boolean isValidStateChange( 102 @FinalizationState int oldState, 103 @FinalizationState int newState) { 104 if (oldState == UNINITIALIZED) { 105 return true; 106 } 107 if (newState == UNINITIALIZED) { 108 // Valid to reset state on multi-user switch 109 return true; 110 } 111 if (oldState == UNFINALIZED && newState == FINALIZED_UNREPORTED) { 112 return true; 113 } 114 if (oldState == UNFINALIZED && newState == FINALIZED) { 115 return true; 116 } 117 if (oldState == FINALIZED_UNREPORTED && newState == FINALIZED) { 118 return true; 119 } 120 return false; 121 } 122 123 /** 124 * Callback for when the state has changed. Runs sequentially with {@link #mExecutionSequencer}. 125 */ 126 interface StateChangeCallback { 127 128 /** 129 * Called when the state has changed 130 * 131 * @param oldState the previous state 132 * @param newState the new state 133 * @return future for when state change callback has finished 134 */ onStateChanged(@inalizationState int oldState, @FinalizationState int newState)135 ListenableFuture<Void> onStateChanged(@FinalizationState int oldState, 136 @FinalizationState int newState); 137 } 138 } 139