blob: 6b6388b10ce7bcc27b2d2faea5e9b0314351f3f0 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.annotation.NonNull;
import android.app.IApplicationThread;
import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.window.flags.Flags;
/**
* Class that is able to combine multiple client lifecycle transition requests and/or callbacks,
* and execute them as a single transaction.
*
* @see ClientTransaction
*/
class ClientLifecycleManager {
private static final String TAG = "ClientLifecycleManager";
/** Mapping from client process binder to its pending transaction. */
@VisibleForTesting
final ArrayMap<IBinder, ClientTransaction> mPendingTransactions = new ArrayMap<>();
private WindowManagerService mWms;
void setWindowManager(@NonNull WindowManagerService wms) {
mWms = wms;
}
/**
* Schedules a transaction, which may consist of multiple callbacks and a lifecycle request.
* @param transaction A sequence of client transaction items.
* @throws RemoteException
*
* @see ClientTransaction
* @deprecated use {@link #scheduleTransactionItem(IApplicationThread, ClientTransactionItem)}.
*/
@Deprecated
void scheduleTransaction(@NonNull ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient();
try {
transaction.schedule();
} finally {
if (!(client instanceof Binder)) {
// If client is not an instance of Binder - it's a remote call and at this point it
// is safe to recycle the object. All objects used for local calls will be recycled
// after the transaction is executed on client in ActivityThread.
transaction.recycle();
}
}
}
/**
* Similar to {@link #scheduleTransactionItem}, but is called without WM lock.
*
* @see WindowProcessController#setReportedProcState(int)
*/
void scheduleTransactionItemUnlocked(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
// Immediately dispatching to client, and must not access WMS.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
if (transactionItem.isActivityLifecycleItem()) {
clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
} else {
clientTransaction.addCallback(transactionItem);
}
scheduleTransaction(clientTransaction);
}
/**
* Schedules a single transaction item, either a callback or a lifecycle request, delivery to
* client application.
* @throws RemoteException
* @see ClientTransactionItem
*/
void scheduleTransactionItem(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
// The behavior is different depending on the flag.
// When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to
// dispatch all pending transactions at once.
if (Flags.bundleClientTransactionFlag()) {
final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
clientTransaction.addTransactionItem(transactionItem);
onClientTransactionItemScheduledLocked(clientTransaction);
} else {
// TODO(b/260873529): cleanup after launch.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
if (transactionItem.isActivityLifecycleItem()) {
clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
} else {
clientTransaction.addCallback(transactionItem);
}
scheduleTransaction(clientTransaction);
}
}
/**
* Schedules a single transaction item with a lifecycle request, delivery to client application.
* @throws RemoteException
* @see ClientTransactionItem
*/
void scheduleTransactionAndLifecycleItems(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem,
@NonNull ActivityLifecycleItem lifecycleItem) throws RemoteException {
// The behavior is different depending on the flag.
// When flag is on, we wait until RootWindowContainer#performSurfacePlacementNoTrace to
// dispatch all pending transactions at once.
if (Flags.bundleClientTransactionFlag()) {
final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client);
clientTransaction.addTransactionItem(transactionItem);
clientTransaction.addTransactionItem(lifecycleItem);
onClientTransactionItemScheduledLocked(clientTransaction);
} else {
// TODO(b/260873529): cleanup after launch.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
clientTransaction.addCallback(transactionItem);
clientTransaction.setLifecycleStateRequest(lifecycleItem);
scheduleTransaction(clientTransaction);
}
}
/** Executes all the pending transactions. */
void dispatchPendingTransactions() {
final int size = mPendingTransactions.size();
for (int i = 0; i < size; i++) {
final ClientTransaction transaction = mPendingTransactions.valueAt(i);
try {
scheduleTransaction(transaction);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to deliver transaction for " + transaction.getClient());
}
}
mPendingTransactions.clear();
}
@NonNull
private ClientTransaction getOrCreatePendingTransaction(@NonNull IApplicationThread client) {
final IBinder clientBinder = client.asBinder();
final ClientTransaction pendingTransaction = mPendingTransactions.get(clientBinder);
if (pendingTransaction != null) {
return pendingTransaction;
}
// Create new transaction if there is no existing.
final ClientTransaction transaction = ClientTransaction.obtain(client);
mPendingTransactions.put(clientBinder, transaction);
return transaction;
}
/** Must only be called with WM lock. */
private void onClientTransactionItemScheduledLocked(
@NonNull ClientTransaction clientTransaction) throws RemoteException {
// TODO(b/260873529): make sure WindowSurfacePlacer#requestTraversal is called before
// ClientTransaction scheduled when needed.
if (mWms != null && (mWms.mWindowPlacerLocked.isInLayout()
|| mWms.mWindowPlacerLocked.isTraversalScheduled())) {
// The pending transactions will be dispatched when
// RootWindowContainer#performSurfacePlacementNoTrace.
return;
}
// Dispatch the pending transaction immediately.
mPendingTransactions.remove(clientTransaction.getClient().asBinder());
scheduleTransaction(clientTransaction);
}
}