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 android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.ClientTransactionHandler; 24 import android.app.IApplicationThread; 25 import android.compat.annotation.UnsupportedAppUsage; 26 import android.os.Build; 27 import android.os.IBinder; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.os.RemoteException; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.window.flags.Flags; 34 35 import java.io.PrintWriter; 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Objects; 39 40 /** 41 * A container that holds a sequence of messages, which may be sent to a client. 42 * This includes a list of callbacks and a final lifecycle state. 43 * 44 * @see com.android.server.wm.ClientLifecycleManager 45 * @see ClientTransactionItem 46 * @see ActivityLifecycleItem 47 * @hide 48 */ 49 public class ClientTransaction implements Parcelable, ObjectPoolItem { 50 51 /** 52 * List of transaction items that should be executed in order. Including both 53 * {@link ActivityLifecycleItem} and other {@link ClientTransactionItem}. 54 */ 55 @Nullable 56 private List<ClientTransactionItem> mTransactionItems; 57 58 /** @deprecated use {@link #getTransactionItems} instead. */ 59 @Nullable 60 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 61 trackingBug = 324203798, 62 publicAlternatives = "Use {@code #getTransactionItems()}") 63 @Deprecated 64 private List<ClientTransactionItem> mActivityCallbacks; 65 66 /** 67 * Final lifecycle state in which the client activity should be after the transaction is 68 * executed. 69 */ 70 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage 71 @Nullable 72 private ActivityLifecycleItem mLifecycleStateRequest; 73 74 /** Only kept for unsupportedAppUsage {@link #getActivityToken()}. Must not be used. */ 75 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage 76 @Nullable 77 private IBinder mActivityToken; 78 79 /** Target client. */ 80 private IApplicationThread mClient; 81 82 /** Get the target client of the transaction. */ getClient()83 public IApplicationThread getClient() { 84 return mClient; 85 } 86 87 /** 88 * Adds a message to the end of the sequence of transaction items. 89 * @param item A single message that can contain a client activity/window request/callback. 90 */ addTransactionItem(@onNull ClientTransactionItem item)91 public void addTransactionItem(@NonNull ClientTransactionItem item) { 92 if (Flags.bundleClientTransactionFlag()) { 93 if (mTransactionItems == null) { 94 mTransactionItems = new ArrayList<>(); 95 } 96 mTransactionItems.add(item); 97 } 98 99 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage 100 // Populate even if mTransactionItems is set to support the UnsupportedAppUsage. 101 if (item.isActivityLifecycleItem()) { 102 setLifecycleStateRequest((ActivityLifecycleItem) item); 103 } else { 104 addCallback(item); 105 } 106 } 107 108 /** 109 * Gets the list of client window requests/callbacks. 110 * TODO(b/260873529): must be non null after remove the deprecated methods. 111 */ 112 @Nullable getTransactionItems()113 public List<ClientTransactionItem> getTransactionItems() { 114 return mTransactionItems; 115 } 116 117 /** 118 * Adds a message to the end of the sequence of callbacks. 119 * @param activityCallback A single message that can contain a lifecycle request/callback. 120 * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead. 121 */ 122 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage 123 @Deprecated addCallback(@onNull ClientTransactionItem activityCallback)124 private void addCallback(@NonNull ClientTransactionItem activityCallback) { 125 if (mActivityCallbacks == null) { 126 mActivityCallbacks = new ArrayList<>(); 127 } 128 mActivityCallbacks.add(activityCallback); 129 setActivityTokenIfNotSet(activityCallback); 130 } 131 132 /** @deprecated use {@link #getTransactionItems()} instead. */ 133 @VisibleForTesting 134 @Nullable 135 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 136 trackingBug = 324203798, 137 publicAlternatives = "Use {@code #getTransactionItems()}") 138 @Deprecated getCallbacks()139 public List<ClientTransactionItem> getCallbacks() { 140 return mActivityCallbacks; 141 } 142 143 /** 144 * A transaction can contain {@link ClientTransactionItem} of different activities, 145 * this must not be used. For any unsupported app usages, please be aware that this is set to 146 * the activity of the first item in {@link #getTransactionItems()}. 147 * 148 * @deprecated use {@link ClientTransactionItem#getActivityToken()} instead. 149 */ 150 @VisibleForTesting 151 @Nullable 152 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 153 trackingBug = 324203798, 154 publicAlternatives = "Use {@code android.app.servertransaction" 155 + ".ClientTransactionItem#getActivityToken()}") 156 @Deprecated getActivityToken()157 public IBinder getActivityToken() { 158 return mActivityToken; 159 } 160 161 /** @deprecated use {@link #getTransactionItems()} instead. */ 162 @VisibleForTesting(visibility = PACKAGE) 163 @Nullable 164 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 165 trackingBug = 324203798, 166 publicAlternatives = "Use {@code #getTransactionItems()}") 167 @Deprecated getLifecycleStateRequest()168 public ActivityLifecycleItem getLifecycleStateRequest() { 169 return mLifecycleStateRequest; 170 } 171 172 /** 173 * Sets the lifecycle state in which the client should be after executing the transaction. 174 * @param stateRequest A lifecycle request initialized with right parameters. 175 * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead. 176 */ 177 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage 178 @Deprecated setLifecycleStateRequest(@onNull ActivityLifecycleItem stateRequest)179 private void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) { 180 if (mLifecycleStateRequest != null) { 181 return; 182 } 183 mLifecycleStateRequest = stateRequest; 184 setActivityTokenIfNotSet(stateRequest); 185 } 186 187 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage setActivityTokenIfNotSet(@ullable ClientTransactionItem item)188 private void setActivityTokenIfNotSet(@Nullable ClientTransactionItem item) { 189 if (mActivityToken == null && item != null) { 190 mActivityToken = item.getActivityToken(); 191 } 192 } 193 194 /** 195 * Do what needs to be done while the transaction is being scheduled on the client side. 196 * @param clientTransactionHandler Handler on the client side that will executed all operations 197 * requested by transaction items. 198 */ preExecute(@onNull ClientTransactionHandler clientTransactionHandler)199 public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) { 200 if (mTransactionItems != null) { 201 final int size = mTransactionItems.size(); 202 for (int i = 0; i < size; ++i) { 203 mTransactionItems.get(i).preExecute(clientTransactionHandler); 204 } 205 return; 206 } 207 208 if (mActivityCallbacks != null) { 209 final int size = mActivityCallbacks.size(); 210 for (int i = 0; i < size; ++i) { 211 mActivityCallbacks.get(i).preExecute(clientTransactionHandler); 212 } 213 } 214 if (mLifecycleStateRequest != null) { 215 mLifecycleStateRequest.preExecute(clientTransactionHandler); 216 } 217 } 218 219 /** 220 * Schedule the transaction after it was initialized. It will be send to client and all its 221 * individual parts will be applied in the following sequence: 222 * 1. The client calls {@link #preExecute(ClientTransactionHandler)}, which triggers all work 223 * that needs to be done before actually scheduling the transaction for callbacks and 224 * lifecycle state request. 225 * 2. The transaction message is scheduled. 226 * 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes 227 * all callbacks and necessary lifecycle transitions. 228 */ schedule()229 public void schedule() throws RemoteException { 230 mClient.scheduleTransaction(this); 231 } 232 233 234 // ObjectPoolItem implementation 235 ClientTransaction()236 private ClientTransaction() {} 237 238 /** Obtains an instance initialized with provided params. */ 239 @NonNull obtain(@ullable IApplicationThread client)240 public static ClientTransaction obtain(@Nullable IApplicationThread client) { 241 ClientTransaction instance = ObjectPool.obtain(ClientTransaction.class); 242 if (instance == null) { 243 instance = new ClientTransaction(); 244 } 245 instance.mClient = client; 246 247 return instance; 248 } 249 250 @Override recycle()251 public void recycle() { 252 if (Flags.disableObjectPool()) { 253 return; 254 } 255 if (mTransactionItems != null) { 256 int size = mTransactionItems.size(); 257 for (int i = 0; i < size; i++) { 258 mTransactionItems.get(i).recycle(); 259 } 260 mTransactionItems = null; 261 mActivityCallbacks = null; 262 mLifecycleStateRequest = null; 263 } else { 264 // Only needed when mTransactionItems is null, otherwise these will have the same 265 // reference as mTransactionItems to support UnsupportedAppUsage. 266 if (mActivityCallbacks != null) { 267 int size = mActivityCallbacks.size(); 268 for (int i = 0; i < size; i++) { 269 mActivityCallbacks.get(i).recycle(); 270 } 271 mActivityCallbacks = null; 272 } 273 if (mLifecycleStateRequest != null) { 274 mLifecycleStateRequest.recycle(); 275 mLifecycleStateRequest = null; 276 } 277 } 278 mClient = null; 279 mActivityToken = null; 280 ObjectPool.recycle(this); 281 } 282 283 // Parcelable implementation 284 285 /** Write to Parcel. */ 286 @SuppressWarnings("AndroidFrameworkEfficientParcelable") // Item class is not final. 287 @Override writeToParcel(@onNull Parcel dest, int flags)288 public void writeToParcel(@NonNull Parcel dest, int flags) { 289 final boolean writeTransactionItems = mTransactionItems != null; 290 dest.writeBoolean(writeTransactionItems); 291 if (writeTransactionItems) { 292 dest.writeParcelableList(mTransactionItems, flags); 293 } else { 294 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage 295 // Only write mLifecycleStateRequest and mActivityCallbacks when mTransactionItems is 296 // null 297 dest.writeParcelable(mLifecycleStateRequest, flags); 298 final boolean writeActivityCallbacks = mActivityCallbacks != null; 299 dest.writeBoolean(writeActivityCallbacks); 300 if (writeActivityCallbacks) { 301 dest.writeParcelableList(mActivityCallbacks, flags); 302 } 303 } 304 } 305 306 /** Read from Parcel. */ ClientTransaction(@onNull Parcel in)307 private ClientTransaction(@NonNull Parcel in) { 308 final boolean readTransactionItems = in.readBoolean(); 309 if (readTransactionItems) { 310 mTransactionItems = new ArrayList<>(); 311 in.readParcelableList(mTransactionItems, getClass().getClassLoader(), 312 ClientTransactionItem.class); 313 314 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage 315 // Populate mLifecycleStateRequest and mActivityCallbacks from mTransactionItems so 316 // that they have the same reference when there is UnsupportedAppUsage to those fields. 317 final int size = mTransactionItems.size(); 318 for (int i = 0; i < size; i++) { 319 final ClientTransactionItem item = mTransactionItems.get(i); 320 if (item.isActivityLifecycleItem()) { 321 setLifecycleStateRequest((ActivityLifecycleItem) item); 322 } else { 323 addCallback(item); 324 } 325 } 326 } else { 327 // TODO(b/324203798): cleanup after remove UnsupportedAppUsage 328 // Only read mLifecycleStateRequest and mActivityCallbacks when mTransactionItems is 329 // null 330 mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), 331 ActivityLifecycleItem.class); 332 setActivityTokenIfNotSet(mLifecycleStateRequest); 333 final boolean readActivityCallbacks = in.readBoolean(); 334 if (readActivityCallbacks) { 335 mActivityCallbacks = new ArrayList<>(); 336 in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(), 337 ClientTransactionItem.class); 338 final int size = mActivityCallbacks.size(); 339 for (int i = 0; mActivityToken == null && i < size; i++) { 340 final ClientTransactionItem item = mActivityCallbacks.get(i); 341 setActivityTokenIfNotSet(item); 342 } 343 } 344 } 345 } 346 347 public static final @NonNull Creator<ClientTransaction> CREATOR = new Creator<>() { 348 public ClientTransaction createFromParcel(@NonNull Parcel in) { 349 return new ClientTransaction(in); 350 } 351 352 public ClientTransaction[] newArray(int size) { 353 return new ClientTransaction[size]; 354 } 355 }; 356 357 @Override describeContents()358 public int describeContents() { 359 return 0; 360 } 361 362 @Override equals(@ullable Object o)363 public boolean equals(@Nullable Object o) { 364 if (this == o) { 365 return true; 366 } 367 if (o == null || getClass() != o.getClass()) { 368 return false; 369 } 370 final ClientTransaction other = (ClientTransaction) o; 371 return Objects.equals(mTransactionItems, other.mTransactionItems) 372 && Objects.equals(mActivityCallbacks, other.mActivityCallbacks) 373 && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest) 374 && mClient == other.mClient 375 && Objects.equals(mActivityToken, other.mActivityToken); 376 } 377 378 @Override hashCode()379 public int hashCode() { 380 int result = 17; 381 result = 31 * result + Objects.hashCode(mTransactionItems); 382 result = 31 * result + Objects.hashCode(mActivityCallbacks); 383 result = 31 * result + Objects.hashCode(mLifecycleStateRequest); 384 result = 31 * result + Objects.hashCode(mClient); 385 result = 31 * result + Objects.hashCode(mActivityToken); 386 return result; 387 } 388 389 @Override toString()390 public String toString() { 391 final StringBuilder sb = new StringBuilder(); 392 sb.append("ClientTransaction{"); 393 if (mTransactionItems != null) { 394 // #addTransactionItem 395 sb.append("\n transactionItems=["); 396 final int size = mTransactionItems.size(); 397 for (int i = 0; i < size; i++) { 398 sb.append("\n ").append(mTransactionItems.get(i)); 399 } 400 sb.append("\n ]"); 401 } else { 402 // #addCallback 403 sb.append("\n callbacks=["); 404 final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0; 405 for (int i = 0; i < size; i++) { 406 sb.append("\n ").append(mActivityCallbacks.get(i)); 407 } 408 sb.append("\n ]"); 409 // #setLifecycleStateRequest 410 sb.append("\n stateRequest=").append(mLifecycleStateRequest); 411 } 412 sb.append("\n}"); 413 return sb.toString(); 414 } 415 416 /** Dump transaction items callback items and final lifecycle state request. */ dump(@onNull String prefix, @NonNull PrintWriter pw, @NonNull ClientTransactionHandler transactionHandler)417 void dump(@NonNull String prefix, @NonNull PrintWriter pw, 418 @NonNull ClientTransactionHandler transactionHandler) { 419 pw.append(prefix).println("ClientTransaction{"); 420 if (mTransactionItems != null) { 421 pw.append(prefix).print(" transactionItems=["); 422 final String itemPrefix = prefix + " "; 423 final int size = mTransactionItems.size(); 424 if (size > 0) { 425 pw.println(); 426 for (int i = 0; i < size; i++) { 427 mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler); 428 } 429 pw.append(prefix).println(" ]"); 430 } else { 431 pw.println("]"); 432 } 433 pw.append(prefix).println("}"); 434 return; 435 } 436 pw.append(prefix).print(" callbacks=["); 437 final String itemPrefix = prefix + " "; 438 final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0; 439 if (size > 0) { 440 pw.println(); 441 for (int i = 0; i < size; i++) { 442 mActivityCallbacks.get(i).dump(itemPrefix, pw, transactionHandler); 443 } 444 pw.append(prefix).println(" ]"); 445 } else { 446 pw.println("]"); 447 } 448 449 pw.append(prefix).println(" stateRequest="); 450 if (mLifecycleStateRequest != null) { 451 mLifecycleStateRequest.dump(itemPrefix, pw, transactionHandler); 452 } else { 453 pw.append(itemPrefix).println("null"); 454 } 455 pw.append(prefix).println("}"); 456 } 457 } 458