1 /*
2  * Copyright 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 android.app.servertransaction;
18 
19 import android.annotation.Nullable;
20 import android.app.ClientTransactionHandler;
21 import android.app.IApplicationThread;
22 import android.compat.annotation.UnsupportedAppUsage;
23 import android.os.IBinder;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.os.RemoteException;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 
30 import java.io.PrintWriter;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Objects;
34 
35 /**
36  * A container that holds a sequence of messages, which may be sent to a client.
37  * This includes a list of callbacks and a final lifecycle state.
38  *
39  * @see com.android.server.am.ClientLifecycleManager
40  * @see ClientTransactionItem
41  * @see ActivityLifecycleItem
42  * @hide
43  */
44 public class ClientTransaction implements Parcelable, ObjectPoolItem {
45 
46     /** A list of individual callbacks to a client. */
47     @UnsupportedAppUsage
48     private List<ClientTransactionItem> mActivityCallbacks;
49 
50     /**
51      * Final lifecycle state in which the client activity should be after the transaction is
52      * executed.
53      */
54     private ActivityLifecycleItem mLifecycleStateRequest;
55 
56     /** Target client. */
57     private IApplicationThread mClient;
58 
59     /** Target client activity. Might be null if the entire transaction is targeting an app. */
60     private IBinder mActivityToken;
61 
62     /** Get the target client of the transaction. */
getClient()63     public IApplicationThread getClient() {
64         return mClient;
65     }
66 
67     /**
68      * Add a message to the end of the sequence of callbacks.
69      * @param activityCallback A single message that can contain a lifecycle request/callback.
70      */
addCallback(ClientTransactionItem activityCallback)71     public void addCallback(ClientTransactionItem activityCallback) {
72         if (mActivityCallbacks == null) {
73             mActivityCallbacks = new ArrayList<>();
74         }
75         mActivityCallbacks.add(activityCallback);
76     }
77 
78     /** Get the list of callbacks. */
79     @Nullable
80     @UnsupportedAppUsage
getCallbacks()81     List<ClientTransactionItem> getCallbacks() {
82         return mActivityCallbacks;
83     }
84 
85     /** Get the target activity. */
86     @Nullable
87     @UnsupportedAppUsage
getActivityToken()88     public IBinder getActivityToken() {
89         return mActivityToken;
90     }
91 
92     /** Get the target state lifecycle request. */
93     @VisibleForTesting
94     @UnsupportedAppUsage
getLifecycleStateRequest()95     public ActivityLifecycleItem getLifecycleStateRequest() {
96         return mLifecycleStateRequest;
97     }
98 
99     /**
100      * Set the lifecycle state in which the client should be after executing the transaction.
101      * @param stateRequest A lifecycle request initialized with right parameters.
102      */
setLifecycleStateRequest(ActivityLifecycleItem stateRequest)103     public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) {
104         mLifecycleStateRequest = stateRequest;
105     }
106 
107     /**
108      * Do what needs to be done while the transaction is being scheduled on the client side.
109      * @param clientTransactionHandler Handler on the client side that will executed all operations
110      *                                 requested by transaction items.
111      */
preExecute(android.app.ClientTransactionHandler clientTransactionHandler)112     public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) {
113         if (mActivityCallbacks != null) {
114             final int size = mActivityCallbacks.size();
115             for (int i = 0; i < size; ++i) {
116                 mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken);
117             }
118         }
119         if (mLifecycleStateRequest != null) {
120             mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken);
121         }
122     }
123 
124     /**
125      * Schedule the transaction after it was initialized. It will be send to client and all its
126      * individual parts will be applied in the following sequence:
127      * 1. The client calls {@link #preExecute(ClientTransactionHandler)}, which triggers all work
128      *    that needs to be done before actually scheduling the transaction for callbacks and
129      *    lifecycle state request.
130      * 2. The transaction message is scheduled.
131      * 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes
132      *    all callbacks and necessary lifecycle transitions.
133      */
schedule()134     public void schedule() throws RemoteException {
135         mClient.scheduleTransaction(this);
136     }
137 
138 
139     // ObjectPoolItem implementation
140 
ClientTransaction()141     private ClientTransaction() {}
142 
143     /** Obtain an instance initialized with provided params. */
obtain(IApplicationThread client, IBinder activityToken)144     public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) {
145         ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class);
146         if (instance == null) {
147             instance = new ClientTransaction();
148         }
149         instance.mClient = client;
150         instance.mActivityToken = activityToken;
151 
152         return instance;
153     }
154 
155     @Override
recycle()156     public void recycle() {
157         if (mActivityCallbacks != null) {
158             int size = mActivityCallbacks.size();
159             for (int i = 0; i < size; i++) {
160                 mActivityCallbacks.get(i).recycle();
161             }
162             mActivityCallbacks.clear();
163         }
164         if (mLifecycleStateRequest != null) {
165             mLifecycleStateRequest.recycle();
166             mLifecycleStateRequest = null;
167         }
168         mClient = null;
169         mActivityToken = null;
170         ObjectPool.recycle(this);
171     }
172 
173     // Parcelable implementation
174 
175     /** Write to Parcel. */
176     @Override
writeToParcel(Parcel dest, int flags)177     public void writeToParcel(Parcel dest, int flags) {
178         dest.writeStrongBinder(mClient.asBinder());
179         final boolean writeActivityToken = mActivityToken != null;
180         dest.writeBoolean(writeActivityToken);
181         if (writeActivityToken) {
182             dest.writeStrongBinder(mActivityToken);
183         }
184         dest.writeParcelable(mLifecycleStateRequest, flags);
185         final boolean writeActivityCallbacks = mActivityCallbacks != null;
186         dest.writeBoolean(writeActivityCallbacks);
187         if (writeActivityCallbacks) {
188             dest.writeParcelableList(mActivityCallbacks, flags);
189         }
190     }
191 
192     /** Read from Parcel. */
ClientTransaction(Parcel in)193     private ClientTransaction(Parcel in) {
194         mClient = (IApplicationThread) in.readStrongBinder();
195         final boolean readActivityToken = in.readBoolean();
196         if (readActivityToken) {
197             mActivityToken = in.readStrongBinder();
198         }
199         mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader());
200         final boolean readActivityCallbacks = in.readBoolean();
201         if (readActivityCallbacks) {
202             mActivityCallbacks = new ArrayList<>();
203             in.readParcelableList(mActivityCallbacks, getClass().getClassLoader());
204         }
205     }
206 
207     public static final @android.annotation.NonNull Creator<ClientTransaction> CREATOR =
208             new Creator<ClientTransaction>() {
209         public ClientTransaction createFromParcel(Parcel in) {
210             return new ClientTransaction(in);
211         }
212 
213         public ClientTransaction[] newArray(int size) {
214             return new ClientTransaction[size];
215         }
216     };
217 
218     @Override
describeContents()219     public int describeContents() {
220         return 0;
221     }
222 
223     @Override
equals(Object o)224     public boolean equals(Object o) {
225         if (this == o) {
226             return true;
227         }
228         if (o == null || getClass() != o.getClass()) {
229             return false;
230         }
231         final ClientTransaction other = (ClientTransaction) o;
232         return Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
233                 && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest)
234                 && mClient == other.mClient
235                 && mActivityToken == other.mActivityToken;
236     }
237 
238     @Override
hashCode()239     public int hashCode() {
240         int result = 17;
241         result = 31 * result + Objects.hashCode(mActivityCallbacks);
242         result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
243         return result;
244     }
245 
246     /** Dump transaction items callback items and final lifecycle state request. */
dump(String prefix, PrintWriter pw)247     public void dump(String prefix, PrintWriter pw) {
248         pw.append(prefix).println("ClientTransaction{");
249         pw.append(prefix).print("  callbacks=[");
250         final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
251         if (size > 0) {
252             pw.println();
253             for (int i = 0; i < size; i++) {
254                 pw.append(prefix).append("    ").println(mActivityCallbacks.get(i).toString());
255             }
256             pw.append(prefix).println("  ]");
257         } else {
258             pw.println("]");
259         }
260         pw.append(prefix).append("  stateRequest=").println(mLifecycleStateRequest != null
261                 ? mLifecycleStateRequest.toString() : null);
262         pw.append(prefix).println("}");
263     }
264 }
265