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 @VisibleForTesting 81 @UnsupportedAppUsage getCallbacks()82 public List<ClientTransactionItem> getCallbacks() { 83 return mActivityCallbacks; 84 } 85 86 /** Get the target activity. */ 87 @Nullable 88 @UnsupportedAppUsage getActivityToken()89 public IBinder getActivityToken() { 90 return mActivityToken; 91 } 92 93 /** Get the target state lifecycle request. */ 94 @VisibleForTesting 95 @UnsupportedAppUsage getLifecycleStateRequest()96 public ActivityLifecycleItem getLifecycleStateRequest() { 97 return mLifecycleStateRequest; 98 } 99 100 /** 101 * Set the lifecycle state in which the client should be after executing the transaction. 102 * @param stateRequest A lifecycle request initialized with right parameters. 103 */ setLifecycleStateRequest(ActivityLifecycleItem stateRequest)104 public void setLifecycleStateRequest(ActivityLifecycleItem stateRequest) { 105 mLifecycleStateRequest = stateRequest; 106 } 107 108 /** 109 * Do what needs to be done while the transaction is being scheduled on the client side. 110 * @param clientTransactionHandler Handler on the client side that will executed all operations 111 * requested by transaction items. 112 */ preExecute(android.app.ClientTransactionHandler clientTransactionHandler)113 public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) { 114 if (mActivityCallbacks != null) { 115 final int size = mActivityCallbacks.size(); 116 for (int i = 0; i < size; ++i) { 117 mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken); 118 } 119 } 120 if (mLifecycleStateRequest != null) { 121 mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken); 122 } 123 } 124 125 /** 126 * Schedule the transaction after it was initialized. It will be send to client and all its 127 * individual parts will be applied in the following sequence: 128 * 1. The client calls {@link #preExecute(ClientTransactionHandler)}, which triggers all work 129 * that needs to be done before actually scheduling the transaction for callbacks and 130 * lifecycle state request. 131 * 2. The transaction message is scheduled. 132 * 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes 133 * all callbacks and necessary lifecycle transitions. 134 */ schedule()135 public void schedule() throws RemoteException { 136 mClient.scheduleTransaction(this); 137 } 138 139 140 // ObjectPoolItem implementation 141 ClientTransaction()142 private ClientTransaction() {} 143 144 /** Obtain an instance initialized with provided params. */ obtain(IApplicationThread client, IBinder activityToken)145 public static ClientTransaction obtain(IApplicationThread client, IBinder activityToken) { 146 ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class); 147 if (instance == null) { 148 instance = new ClientTransaction(); 149 } 150 instance.mClient = client; 151 instance.mActivityToken = activityToken; 152 153 return instance; 154 } 155 156 @Override recycle()157 public void recycle() { 158 if (mActivityCallbacks != null) { 159 int size = mActivityCallbacks.size(); 160 for (int i = 0; i < size; i++) { 161 mActivityCallbacks.get(i).recycle(); 162 } 163 mActivityCallbacks.clear(); 164 } 165 if (mLifecycleStateRequest != null) { 166 mLifecycleStateRequest.recycle(); 167 mLifecycleStateRequest = null; 168 } 169 mClient = null; 170 mActivityToken = null; 171 ObjectPool.recycle(this); 172 } 173 174 // Parcelable implementation 175 176 /** Write to Parcel. */ 177 @Override writeToParcel(Parcel dest, int flags)178 public void writeToParcel(Parcel dest, int flags) { 179 dest.writeStrongBinder(mClient.asBinder()); 180 final boolean writeActivityToken = mActivityToken != null; 181 dest.writeBoolean(writeActivityToken); 182 if (writeActivityToken) { 183 dest.writeStrongBinder(mActivityToken); 184 } 185 dest.writeParcelable(mLifecycleStateRequest, flags); 186 final boolean writeActivityCallbacks = mActivityCallbacks != null; 187 dest.writeBoolean(writeActivityCallbacks); 188 if (writeActivityCallbacks) { 189 dest.writeParcelableList(mActivityCallbacks, flags); 190 } 191 } 192 193 /** Read from Parcel. */ ClientTransaction(Parcel in)194 private ClientTransaction(Parcel in) { 195 mClient = (IApplicationThread) in.readStrongBinder(); 196 final boolean readActivityToken = in.readBoolean(); 197 if (readActivityToken) { 198 mActivityToken = in.readStrongBinder(); 199 } 200 mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader()); 201 final boolean readActivityCallbacks = in.readBoolean(); 202 if (readActivityCallbacks) { 203 mActivityCallbacks = new ArrayList<>(); 204 in.readParcelableList(mActivityCallbacks, getClass().getClassLoader()); 205 } 206 } 207 208 public static final @android.annotation.NonNull Creator<ClientTransaction> CREATOR = 209 new Creator<ClientTransaction>() { 210 public ClientTransaction createFromParcel(Parcel in) { 211 return new ClientTransaction(in); 212 } 213 214 public ClientTransaction[] newArray(int size) { 215 return new ClientTransaction[size]; 216 } 217 }; 218 219 @Override describeContents()220 public int describeContents() { 221 return 0; 222 } 223 224 @Override equals(@ullable Object o)225 public boolean equals(@Nullable Object o) { 226 if (this == o) { 227 return true; 228 } 229 if (o == null || getClass() != o.getClass()) { 230 return false; 231 } 232 final ClientTransaction other = (ClientTransaction) o; 233 return Objects.equals(mActivityCallbacks, other.mActivityCallbacks) 234 && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest) 235 && mClient == other.mClient 236 && mActivityToken == other.mActivityToken; 237 } 238 239 @Override hashCode()240 public int hashCode() { 241 int result = 17; 242 result = 31 * result + Objects.hashCode(mActivityCallbacks); 243 result = 31 * result + Objects.hashCode(mLifecycleStateRequest); 244 result = 31 * result + Objects.hashCode(mClient); 245 result = 31 * result + Objects.hashCode(mActivityToken); 246 return result; 247 } 248 249 /** Dump transaction items callback items and final lifecycle state request. */ dump(String prefix, PrintWriter pw)250 public void dump(String prefix, PrintWriter pw) { 251 pw.append(prefix).println("ClientTransaction{"); 252 pw.append(prefix).print(" callbacks=["); 253 final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0; 254 if (size > 0) { 255 pw.println(); 256 for (int i = 0; i < size; i++) { 257 pw.append(prefix).append(" ").println(mActivityCallbacks.get(i).toString()); 258 } 259 pw.append(prefix).println(" ]"); 260 } else { 261 pw.println("]"); 262 } 263 pw.append(prefix).append(" stateRequest=").println(mLifecycleStateRequest != null 264 ? mLifecycleStateRequest.toString() : null); 265 pw.append(prefix).println("}"); 266 } 267 } 268