1 /*
2  * Copyright (C) 2017 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.annotation.NonNull;
20 import android.app.IApplicationThread;
21 import android.app.compat.CompatChanges;
22 import android.app.servertransaction.ActivityLifecycleItem;
23 import android.app.servertransaction.ClientTransaction;
24 import android.app.servertransaction.ClientTransactionItem;
25 import android.app.servertransaction.LaunchActivityItem;
26 import android.compat.annotation.ChangeId;
27 import android.compat.annotation.EnabledSince;
28 import android.os.Binder;
29 import android.os.Build;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.os.Trace;
33 import android.os.UserHandle;
34 import android.util.ArrayMap;
35 import android.util.Slog;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.window.flags.Flags;
39 
40 /**
41  * Class that is able to combine multiple client lifecycle transition requests and/or callbacks,
42  * and execute them as a single transaction.
43  *
44  * @see ClientTransaction
45  */
46 class ClientLifecycleManager {
47 
48     private static final String TAG = "ClientLifecycleManager";
49 
50     /**
51      * To prevent any existing apps from having app compat issue with the non-sdk usages of
52      * {@link ClientTransaction#getActivityToken()}, only allow bundling {@link LaunchActivityItem}
53      * for apps with targetSDK of V and above.
54      */
55     @ChangeId
56     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
57     private static final long ENABLE_BUNDLE_LAUNCH_ACTIVITY_ITEM = 324203798L;
58 
59     /** Mapping from client process binder to its pending transaction. */
60     @VisibleForTesting
61     final ArrayMap<IBinder, ClientTransaction> mPendingTransactions = new ArrayMap<>();
62 
63     private WindowManagerService mWms;
64 
setWindowManager(@onNull WindowManagerService wms)65     void setWindowManager(@NonNull WindowManagerService wms) {
66         mWms = wms;
67     }
68 
69     /**
70      * Schedules a transaction, which may consist of multiple callbacks and a lifecycle request.
71      * @param transaction A sequence of client transaction items.
72      * @throws RemoteException
73      *
74      * @see ClientTransaction
75      * @deprecated use {@link #scheduleTransactionItem(IApplicationThread, ClientTransactionItem)}.
76      */
77     @Deprecated
scheduleTransaction(@onNull ClientTransaction transaction)78     void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
79         final IApplicationThread client = transaction.getClient();
80         try {
81             transaction.schedule();
82         } catch (RemoteException e) {
83             Slog.w(TAG, "Failed to deliver transaction for " + client
84                             + "\ntransaction=" + transaction);
85             throw e;
86         } finally {
87             if (!(client instanceof Binder)) {
88                 // If client is not an instance of Binder - it's a remote call and at this point it
89                 // is safe to recycle the object. All objects used for local calls will be recycled
90                 // after the transaction is executed on client in ActivityThread.
91                 transaction.recycle();
92             }
93         }
94     }
95 
96     /**
97      * Similar to {@link #scheduleTransactionItem}, but it sends the transaction immediately and
98      * it can be called without WM lock.
99      *
100      * @see WindowProcessController#setReportedProcState(int)
101      */
scheduleTransactionItemNow(@onNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem)102     void scheduleTransactionItemNow(@NonNull IApplicationThread client,
103             @NonNull ClientTransactionItem transactionItem) throws RemoteException {
104         final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
105         clientTransaction.addTransactionItem(transactionItem);
106         scheduleTransaction(clientTransaction);
107     }
108 
109     /**
110      * Schedules a single transaction item, either a callback or a lifecycle request, delivery to
111      * client application.
112      * @throws RemoteException
113      * @see ClientTransactionItem
114      */
scheduleTransactionItem(@onNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem)115     void scheduleTransactionItem(@NonNull IApplicationThread client,
116             @NonNull ClientTransactionItem transactionItem) throws RemoteException {
117         // The behavior is different depending on the flag.
118         // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to
119         // dispatch all pending transactions at once.
120         if (Flags.bundleClientTransactionFlag()) {
121             final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
122             clientTransaction.addTransactionItem(transactionItem);
123 
124             onClientTransactionItemScheduled(clientTransaction,
125                     false /* shouldDispatchImmediately */);
126         } else {
127             // TODO(b/260873529): cleanup after launch.
128             final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
129             clientTransaction.addTransactionItem(transactionItem);
130 
131             scheduleTransaction(clientTransaction);
132         }
133     }
134 
scheduleTransactionAndLifecycleItems(@onNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem, @NonNull ActivityLifecycleItem lifecycleItem)135     void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client,
136             @NonNull ClientTransactionItem transactionItem,
137             @NonNull ActivityLifecycleItem lifecycleItem) throws RemoteException {
138         scheduleTransactionAndLifecycleItems(client, transactionItem, lifecycleItem,
139                 false /* shouldDispatchImmediately */);
140     }
141 
142     /**
143      * Schedules a single transaction item with a lifecycle request, delivery to client application.
144      *
145      * @param shouldDispatchImmediately whether or not to dispatch the transaction immediately. This
146      *                                  should only be {@code true} when it is important to know the
147      *                                  result of dispatching immediately. For example, when cold
148      *                                  launches an app, the server needs to know if the transaction
149      *                                  is dispatched successfully, and may restart the process if
150      *                                  not.
151      *
152      * @throws RemoteException
153      * @see ClientTransactionItem
154      */
scheduleTransactionAndLifecycleItems(@onNull IApplicationThread client, @NonNull ClientTransactionItem transactionItem, @NonNull ActivityLifecycleItem lifecycleItem, boolean shouldDispatchImmediately)155     void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client,
156             @NonNull ClientTransactionItem transactionItem,
157             @NonNull ActivityLifecycleItem lifecycleItem,
158             boolean shouldDispatchImmediately) throws RemoteException {
159         // The behavior is different depending on the flag.
160         // When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to
161         // dispatch all pending transactions at once.
162         if (Flags.bundleClientTransactionFlag()) {
163             final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
164             clientTransaction.addTransactionItem(transactionItem);
165             clientTransaction.addTransactionItem(lifecycleItem);
166 
167             onClientTransactionItemScheduled(clientTransaction, shouldDispatchImmediately);
168         } else {
169             // TODO(b/260873529): cleanup after launch.
170             final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
171             clientTransaction.addTransactionItem(transactionItem);
172             clientTransaction.addTransactionItem(lifecycleItem);
173             scheduleTransaction(clientTransaction);
174         }
175     }
176 
177     /** Executes all the pending transactions. */
dispatchPendingTransactions()178     void dispatchPendingTransactions() {
179         if (!Flags.bundleClientTransactionFlag() || mPendingTransactions.isEmpty()) {
180             return;
181         }
182         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "clientTransactionsDispatched");
183         final int size = mPendingTransactions.size();
184         for (int i = 0; i < size; i++) {
185             final ClientTransaction transaction = mPendingTransactions.valueAt(i);
186             try {
187                 scheduleTransaction(transaction);
188             } catch (RemoteException e) {
189                 Slog.e(TAG, "Failed to deliver pending transaction", e);
190                 // TODO(b/323801078): apply cleanup for individual transaction item if needed.
191             }
192         }
193         mPendingTransactions.clear();
194         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
195     }
196 
197     /** Executes the pending transaction for the given client process. */
dispatchPendingTransaction(@onNull IApplicationThread client)198     void dispatchPendingTransaction(@NonNull IApplicationThread client) {
199         if (!Flags.bundleClientTransactionFlag()) {
200             return;
201         }
202         final ClientTransaction pendingTransaction = mPendingTransactions.remove(client.asBinder());
203         if (pendingTransaction != null) {
204             try {
205                 scheduleTransaction(pendingTransaction);
206             } catch (RemoteException e) {
207                 Slog.e(TAG, "Failed to deliver pending transaction", e);
208                 // TODO(b/323801078): apply cleanup for individual transaction item if needed.
209             }
210         }
211     }
212 
213     /**
214      * Called to when {@link WindowSurfacePlacer#continueLayout}.
215      * Dispatches all pending transactions unless there is an ongoing/scheduled layout, in which
216      * case the pending transactions will be dispatched in
217      * {@link RootWindowContainer#performSurfacePlacementNoTrace}.
218      */
onLayoutContinued()219     void onLayoutContinued() {
220         if (shouldDispatchPendingTransactionsImmediately()) {
221             // Dispatch the pending transactions immediately if there is no ongoing/scheduled layout
222             dispatchPendingTransactions();
223         }
224     }
225 
226     /** Must only be called with WM lock. */
227     @NonNull
getOrCreatePendingTransaction(@onNull IApplicationThread client)228     private ClientTransaction getOrCreatePendingTransaction(@NonNull IApplicationThread client) {
229         final IBinder clientBinder = client.asBinder();
230         final ClientTransaction pendingTransaction = mPendingTransactions.get(clientBinder);
231         if (pendingTransaction != null) {
232             return pendingTransaction;
233         }
234 
235         // Create new transaction if there is no existing.
236         final ClientTransaction transaction = ClientTransaction.obtain(client);
237         mPendingTransactions.put(clientBinder, transaction);
238         return transaction;
239     }
240 
241     /** Must only be called with WM lock. */
onClientTransactionItemScheduled( @onNull ClientTransaction clientTransaction, boolean shouldDispatchImmediately)242     private void onClientTransactionItemScheduled(
243             @NonNull ClientTransaction clientTransaction,
244             boolean shouldDispatchImmediately) throws RemoteException {
245         if (shouldDispatchImmediately || shouldDispatchPendingTransactionsImmediately()) {
246             // Dispatch the pending transaction immediately.
247             mPendingTransactions.remove(clientTransaction.getClient().asBinder());
248             scheduleTransaction(clientTransaction);
249         }
250     }
251 
252     /** Must only be called with WM lock. */
shouldDispatchPendingTransactionsImmediately()253     private boolean shouldDispatchPendingTransactionsImmediately() {
254         if (mWms == null) {
255             return true;
256         }
257         // Do not dispatch when
258         // 1. Layout deferred.
259         // 2. Layout requested.
260         // 3. Layout in process.
261         // The pending transactions will be dispatched during layout in
262         // RootWindowContainer#performSurfacePlacementNoTrace.
263         return !mWms.mWindowPlacerLocked.isLayoutDeferred()
264                 && !mWms.mWindowPlacerLocked.isTraversalScheduled()
265                 && !mWms.mWindowPlacerLocked.isInLayout();
266     }
267 
268     /** Guards bundling {@link LaunchActivityItem} with targetSDK. */
shouldDispatchLaunchActivityItemIndependently( @onNull String appPackageName, int appUid)269     static boolean shouldDispatchLaunchActivityItemIndependently(
270             @NonNull String appPackageName, int appUid) {
271         return !CompatChanges.isChangeEnabled(ENABLE_BUNDLE_LAUNCH_ACTIVITY_ITEM,
272                 appPackageName,
273                 UserHandle.getUserHandleForUid(appUid));
274     }
275 }
276