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