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