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