• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
20 
21 import static java.util.Objects.requireNonNull;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.app.ClientTransactionHandler;
26 import android.app.IApplicationThread;
27 import android.compat.annotation.UnsupportedAppUsage;
28 import android.os.Build;
29 import android.os.IBinder;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.os.RemoteException;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 
36 import java.io.PrintWriter;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Objects;
40 
41 /**
42  * A container that holds a sequence of messages, which may be sent to a client.
43  * This includes a list of callbacks and a final lifecycle state.
44  *
45  * @see com.android.server.wm.ClientLifecycleManager
46  * @see ClientTransactionItem
47  * @see ActivityLifecycleItem
48  * @hide
49  */
50 public class ClientTransaction implements Parcelable {
51 
52     /**
53      * List of transaction items that should be executed in order. Including both
54      * {@link ActivityLifecycleItem} and other {@link ClientTransactionItem}.
55      */
56     @NonNull
57     private final List<ClientTransactionItem> mTransactionItems = new ArrayList<>();
58 
59     /** @deprecated use {@link #getTransactionItems} instead. */
60     @Nullable
61     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
62             trackingBug = 324203798,
63             publicAlternatives = "Use {@code #getTransactionItems()}")
64     @Deprecated
65     private List<ClientTransactionItem> mActivityCallbacks;
66 
67     /**
68      * Final lifecycle state in which the client activity should be after the transaction is
69      * executed.
70      */
71     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
72     @Nullable
73     private ActivityLifecycleItem mLifecycleStateRequest;
74 
75     /** Only kept for unsupportedAppUsage {@link #getActivityToken()}. Must not be used. */
76     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
77     @Nullable
78     private IBinder mActivityToken;
79 
80     /**
81      * The target client.
82      * <p>
83      * This field is null only if the object is:
84      * - Read from a Parcel on the client side.
85      * - Constructed for testing purposes.
86      * <p>
87      * When created directly on the server, this field represents the server's connection to the
88      * target client's application thread. It is omitted during parceling and not sent to the
89      * client. On the client side, this field becomes unnecessary.
90      */
91     @Nullable
92     private final IApplicationThread mClient;
93 
94     @VisibleForTesting
ClientTransaction()95     public ClientTransaction() {
96         mClient = null;
97     }
98 
ClientTransaction(@onNull IApplicationThread client)99     public ClientTransaction(@NonNull IApplicationThread client) {
100         mClient = requireNonNull(client);
101     }
102 
103     /**
104      * Gets the target client associated with this transaction.
105      * <p>
106      * This method is intended for server-side use only. Calling it from the client side
107      * will always return {@code null}.
108      *
109      * @return the {@link IApplicationThread} representing the target client, or {@code null} if
110      * called from the client side.
111      * @see #mClient
112      */
getClient()113     public IApplicationThread getClient() {
114         return mClient;
115     }
116 
117     /**
118      * Adds a message to the end of the sequence of transaction items.
119      * @param item A single message that can contain a client activity/window request/callback.
120      */
addTransactionItem(@onNull ClientTransactionItem item)121     public void addTransactionItem(@NonNull ClientTransactionItem item) {
122         mTransactionItems.add(item);
123 
124         // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
125         // Populate even if mTransactionItems is set to support the UnsupportedAppUsage.
126         if (item.isActivityLifecycleItem()) {
127             setLifecycleStateRequest((ActivityLifecycleItem) item);
128         } else {
129             addCallback(item);
130         }
131     }
132 
133     /**
134      * Gets the list of client window requests/callbacks.
135      */
136     @NonNull
getTransactionItems()137     public List<ClientTransactionItem> getTransactionItems() {
138         return mTransactionItems;
139     }
140 
141     /**
142      * Adds a message to the end of the sequence of callbacks.
143      * @param activityCallback A single message that can contain a lifecycle request/callback.
144      * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead.
145      */
146     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
147     @Deprecated
addCallback(@onNull ClientTransactionItem activityCallback)148     private void addCallback(@NonNull ClientTransactionItem activityCallback) {
149         if (mActivityCallbacks == null) {
150             mActivityCallbacks = new ArrayList<>();
151         }
152         mActivityCallbacks.add(activityCallback);
153         setActivityTokenIfNotSet(activityCallback);
154     }
155 
156     /** @deprecated use {@link #getTransactionItems()} instead. */
157     @VisibleForTesting
158     @Nullable
159     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
160             trackingBug = 324203798,
161             publicAlternatives = "Use {@code #getTransactionItems()}")
162     @Deprecated
getCallbacks()163     public List<ClientTransactionItem> getCallbacks() {
164         return mActivityCallbacks;
165     }
166 
167     /**
168      * A transaction can contain {@link ClientTransactionItem} of different activities,
169      * this must not be used. For any unsupported app usages, please be aware that this is set to
170      * the activity of the first item in {@link #getTransactionItems()}.
171      *
172      * @deprecated use {@link ClientTransactionItem#getActivityToken()} instead.
173      */
174     @VisibleForTesting
175     @Nullable
176     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
177             trackingBug = 324203798,
178             publicAlternatives = "Use {@code android.app.servertransaction"
179                     + ".ClientTransactionItem#getActivityToken()}")
180     @Deprecated
getActivityToken()181     public IBinder getActivityToken() {
182         return mActivityToken;
183     }
184 
185     /** @deprecated use {@link #getTransactionItems()} instead. */
186     @VisibleForTesting(visibility = PACKAGE)
187     @Nullable
188     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
189             trackingBug = 324203798,
190             publicAlternatives = "Use {@code #getTransactionItems()}")
191     @Deprecated
getLifecycleStateRequest()192     public ActivityLifecycleItem getLifecycleStateRequest() {
193         return mLifecycleStateRequest;
194     }
195 
196     /**
197      * Sets the lifecycle state in which the client should be after executing the transaction.
198      * @param stateRequest A lifecycle request initialized with right parameters.
199      * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead.
200      */
201     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
202     @Deprecated
setLifecycleStateRequest(@onNull ActivityLifecycleItem stateRequest)203     private void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
204         if (mLifecycleStateRequest != null) {
205             return;
206         }
207         mLifecycleStateRequest = stateRequest;
208         setActivityTokenIfNotSet(stateRequest);
209     }
210 
211     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
setActivityTokenIfNotSet(@ullable ClientTransactionItem item)212     private void setActivityTokenIfNotSet(@Nullable ClientTransactionItem item) {
213         if (mActivityToken == null && item != null) {
214             mActivityToken = item.getActivityToken();
215         }
216     }
217 
218     /**
219      * Do what needs to be done while the transaction is being scheduled on the client side.
220      * @param clientTransactionHandler Handler on the client side that will executed all operations
221      *                                 requested by transaction items.
222      */
preExecute(@onNull ClientTransactionHandler clientTransactionHandler)223     public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) {
224         final int size = mTransactionItems.size();
225         for (int i = 0; i < size; ++i) {
226             mTransactionItems.get(i).preExecute(clientTransactionHandler);
227         }
228     }
229 
230     /**
231      * Schedule the transaction after it was initialized. It will be send to client and all its
232      * individual parts will be applied in the following sequence:
233      * 1. The client calls {@link #preExecute(ClientTransactionHandler)}, which triggers all work
234      *    that needs to be done before actually scheduling the transaction for callbacks and
235      *    lifecycle state request.
236      * 2. The transaction message is scheduled.
237      * 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes
238      *    all callbacks and necessary lifecycle transitions.
239      *
240      * @return {@link RemoteException} if the transaction failed.
241      */
242     @Nullable
schedule()243     public RemoteException schedule() {
244         try {
245             mClient.scheduleTransaction(this);
246             return null;
247         } catch (RemoteException e) {
248             return e;
249         }
250     }
251 
252     // Parcelable implementation
253 
254     /** Writes to Parcel. */
255     @SuppressWarnings("AndroidFrameworkEfficientParcelable") // Item class is not final.
256     @Override
writeToParcel(@onNull Parcel dest, int flags)257     public void writeToParcel(@NonNull Parcel dest, int flags) {
258         dest.writeParcelableList(mTransactionItems, flags);
259     }
260 
261     /** Reads from Parcel. */
ClientTransaction(@onNull Parcel in)262     private ClientTransaction(@NonNull Parcel in) {
263         mClient = null;  // This field is unnecessary on the client side.
264         in.readParcelableList(mTransactionItems, getClass().getClassLoader(),
265                 ClientTransactionItem.class);
266 
267         // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
268         // Populate mLifecycleStateRequest and mActivityCallbacks from mTransactionItems so
269         // that they have the same reference when there is UnsupportedAppUsage to those fields.
270         final int size = mTransactionItems.size();
271         for (int i = 0; i < size; i++) {
272             final ClientTransactionItem item = mTransactionItems.get(i);
273             if (item.isActivityLifecycleItem()) {
274                 setLifecycleStateRequest((ActivityLifecycleItem) item);
275             } else {
276                 addCallback(item);
277             }
278         }
279     }
280 
281     public static final @NonNull Creator<ClientTransaction> CREATOR = new Creator<>() {
282         public ClientTransaction createFromParcel(@NonNull Parcel in) {
283             return new ClientTransaction(in);
284         }
285 
286         public ClientTransaction[] newArray(int size) {
287             return new ClientTransaction[size];
288         }
289     };
290 
291     @Override
describeContents()292     public int describeContents() {
293         return 0;
294     }
295 
296     @Override
equals(@ullable Object o)297     public boolean equals(@Nullable Object o) {
298         if (this == o) {
299             return true;
300         }
301         if (o == null || getClass() != o.getClass()) {
302             return false;
303         }
304         final ClientTransaction other = (ClientTransaction) o;
305         return Objects.equals(mTransactionItems, other.mTransactionItems)
306                 && Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
307                 && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest)
308                 && mClient == other.mClient
309                 && Objects.equals(mActivityToken, other.mActivityToken);
310     }
311 
312     @Override
hashCode()313     public int hashCode() {
314         int result = 17;
315         result = 31 * result + Objects.hashCode(mTransactionItems);
316         result = 31 * result + Objects.hashCode(mActivityCallbacks);
317         result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
318         result = 31 * result + Objects.hashCode(mClient);
319         result = 31 * result + Objects.hashCode(mActivityToken);
320         return result;
321     }
322 
323     @Override
toString()324     public String toString() {
325         final StringBuilder sb = new StringBuilder();
326         sb.append("ClientTransaction{");
327         sb.append("\n  transactionItems=[");
328         final int size = mTransactionItems.size();
329         for (int i = 0; i < size; i++) {
330             sb.append("\n    ").append(mTransactionItems.get(i));
331         }
332         sb.append("\n  ]");
333         sb.append("\n}");
334         return sb.toString();
335     }
336 
337     /** Dump transaction items callback items and final lifecycle state request. */
dump(@onNull String prefix, @NonNull PrintWriter pw, @NonNull ClientTransactionHandler transactionHandler)338     void dump(@NonNull String prefix, @NonNull PrintWriter pw,
339             @NonNull ClientTransactionHandler transactionHandler) {
340         pw.append(prefix).println("ClientTransaction{");
341         pw.append(prefix).print("  transactionItems=[");
342         final String itemPrefix = prefix + "    ";
343         final int size = mTransactionItems.size();
344         if (size > 0) {
345             pw.println();
346             for (int i = 0; i < size; i++) {
347                 mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler);
348             }
349             pw.append(prefix).println("  ]");
350         } else {
351             pw.println("]");
352         }
353         pw.append(prefix).println("}");
354     }
355 }
356