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.wm;
18 
19 import android.util.ArrayMap;
20 import android.util.ArraySet;
21 
22 import java.util.Set;
23 
24 /**
25  * Utility class for collecting WindowContainers that will merge transactions.
26  * For example to use to synchronously resize all the children of a window container
27  *   1. Open a new sync set, and pass the listener that will be invoked
28  *        int id startSyncSet(TransactionReadyListener)
29  *      the returned ID will be eventually passed to the TransactionReadyListener in combination
30  *      with a set of WindowContainers that are ready, meaning onTransactionReady was called for
31  *      those WindowContainers. You also use it to refer to the operation in future steps.
32  *   2. Ask each child to participate:
33  *       addToSyncSet(int id, WindowContainer wc)
34  *      if the child thinks it will be affected by a configuration change (a.k.a. has a visible
35  *      window in its sub hierarchy, then we will increment a counter of expected callbacks
36  *      At this point the containers hierarchy will redirect pendingTransaction and sub hierarchy
37  *      updates in to the sync engine.
38  *   3. Apply your configuration changes to the window containers.
39  *   4. Tell the engine that the sync set is ready
40  *       setReady(int id)
41  *   5. If there were no sub windows anywhere in the hierarchy to wait on, then
42  *      transactionReady is immediately invoked, otherwise all the windows are poked
43  *      to redraw and to deliver a buffer to {@link WindowState#finishDrawing}.
44  *      Once all this drawing is complete the WindowContainer that's ready will be added to the
45  *      set of ready WindowContainers. When the final onTransactionReady is called, it will merge
46  *      the transactions of the all the WindowContainers and will be delivered to the
47  *      TransactionReadyListener
48  */
49 class BLASTSyncEngine {
50     private static final String TAG = "BLASTSyncEngine";
51 
52     interface TransactionReadyListener {
onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady)53         void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady);
54     };
55 
56     // Holds state associated with a single synchronous set of operations.
57     class SyncState implements TransactionReadyListener {
58         int mSyncId;
59         int mRemainingTransactions;
60         TransactionReadyListener mListener;
61         boolean mReady = false;
62         Set<WindowContainer> mWindowContainersReady = new ArraySet<>();
63 
tryFinish()64         private void tryFinish() {
65             if (mRemainingTransactions == 0 && mReady) {
66                 mListener.onTransactionReady(mSyncId, mWindowContainersReady);
67                 mPendingSyncs.remove(mSyncId);
68             }
69         }
70 
onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady)71         public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
72             mRemainingTransactions--;
73             mWindowContainersReady.addAll(windowContainersReady);
74             tryFinish();
75         }
76 
setReady()77         void setReady() {
78             mReady = true;
79             tryFinish();
80         }
81 
addToSync(WindowContainer wc)82         boolean addToSync(WindowContainer wc) {
83             if (wc.prepareForSync(this, mSyncId)) {
84                 mRemainingTransactions++;
85                 return true;
86             }
87             return false;
88         }
89 
SyncState(TransactionReadyListener l, int id)90         SyncState(TransactionReadyListener l, int id) {
91             mListener = l;
92             mSyncId = id;
93             mRemainingTransactions = 0;
94         }
95     };
96 
97     private int mNextSyncId = 0;
98 
99     private final ArrayMap<Integer, SyncState> mPendingSyncs = new ArrayMap<>();
100 
BLASTSyncEngine()101     BLASTSyncEngine() {
102     }
103 
startSyncSet(TransactionReadyListener listener)104     int startSyncSet(TransactionReadyListener listener) {
105         final int id = mNextSyncId++;
106         final SyncState s = new SyncState(listener, id);
107         mPendingSyncs.put(id, s);
108         return id;
109     }
110 
addToSyncSet(int id, WindowContainer wc)111     boolean addToSyncSet(int id, WindowContainer wc) {
112         final SyncState st = mPendingSyncs.get(id);
113         return st.addToSync(wc);
114     }
115 
setReady(int id)116     void setReady(int id) {
117         final SyncState st = mPendingSyncs.get(id);
118         st.setReady();
119     }
120 }
121