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