1 /* 2 * Copyright (C) 2018 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 com.google.android.startop.iorap; 18 19 import android.annotation.LongDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.ComponentName; 23 import android.content.Intent; 24 import android.content.pm.ActivityInfo; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.util.proto.ProtoOutputStream; 28 29 import com.android.server.wm.ActivityMetricsLaunchObserver; 30 import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; 31 import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.lang.reflect.InvocationTargetException; 36 import java.util.Arrays; 37 import java.util.Objects; 38 39 /** 40 * Provide a hint to iorapd that an app launch sequence has transitioned state.<br /><br /> 41 * 42 * Knowledge of when an activity starts/stops can be used by iorapd to increase system 43 * performance (e.g. by launching perfetto tracing to record an io profile, or by 44 * playing back an ioprofile via readahead) over the long run.<br /><br /> 45 * 46 * /@see com.google.android.startop.iorap.IIorap#onAppLaunchEvent <br /><br /> 47 * @see com.android.server.wm.ActivityMetricsLaunchObserver 48 * ActivityMetricsLaunchObserver for the possible event states. 49 * @hide 50 */ 51 public abstract class AppLaunchEvent implements Parcelable { 52 @LongDef 53 @Retention(RetentionPolicy.SOURCE) 54 public @interface SequenceId {} 55 56 public final @SequenceId 57 long sequenceId; 58 AppLaunchEvent(@equenceId long sequenceId)59 protected AppLaunchEvent(@SequenceId long sequenceId) { 60 this.sequenceId = sequenceId; 61 } 62 63 @Override equals(Object other)64 public boolean equals(Object other) { 65 if (other instanceof AppLaunchEvent) { 66 return equals((AppLaunchEvent) other); 67 } 68 return false; 69 } 70 equals(AppLaunchEvent other)71 protected boolean equals(AppLaunchEvent other) { 72 return sequenceId == other.sequenceId; 73 } 74 75 76 @Override toString()77 public String toString() { 78 return getClass().getSimpleName() + 79 "{" + "sequenceId=" + Long.toString(sequenceId) + 80 toStringBody() + "}"; 81 } 82 toStringBody()83 protected String toStringBody() { return ""; }; 84 85 // List of possible variants: 86 87 public static final class IntentStarted extends AppLaunchEvent { 88 @NonNull 89 public final Intent intent; 90 public final long timestampNs; 91 IntentStarted(@equenceId long sequenceId, Intent intent, long timestampNs)92 public IntentStarted(@SequenceId long sequenceId, 93 Intent intent, 94 long timestampNs) { 95 super(sequenceId); 96 this.intent = intent; 97 this.timestampNs = timestampNs; 98 99 Objects.requireNonNull(intent, "intent"); 100 } 101 102 @Override equals(Object other)103 public boolean equals(Object other) { 104 if (other instanceof IntentStarted) { 105 return intent.equals(((IntentStarted)other).intent) && 106 timestampNs == ((IntentStarted)other).timestampNs && 107 super.equals(other); 108 } 109 return false; 110 } 111 112 @Override toStringBody()113 protected String toStringBody() { 114 return ", intent=" + intent.toString() + 115 " , timestampNs=" + Long.toString(timestampNs); 116 } 117 118 119 @Override writeToParcelImpl(Parcel p, int flags)120 protected void writeToParcelImpl(Parcel p, int flags) { 121 super.writeToParcelImpl(p, flags); 122 IntentProtoParcelable.write(p, intent, flags); 123 p.writeLong(timestampNs); 124 } 125 IntentStarted(Parcel p)126 IntentStarted(Parcel p) { 127 super(p); 128 intent = IntentProtoParcelable.create(p); 129 timestampNs = p.readLong(); 130 } 131 } 132 133 public static final class IntentFailed extends AppLaunchEvent { IntentFailed(@equenceId long sequenceId)134 public IntentFailed(@SequenceId long sequenceId) { 135 super(sequenceId); 136 } 137 138 @Override equals(Object other)139 public boolean equals(Object other) { 140 if (other instanceof IntentFailed) { 141 return super.equals(other); 142 } 143 return false; 144 } 145 IntentFailed(Parcel p)146 IntentFailed(Parcel p) { 147 super(p); 148 } 149 } 150 151 public static abstract class BaseWithActivityRecordData extends AppLaunchEvent { 152 public final @NonNull 153 @ActivityRecordProto byte[] activityRecordSnapshot; 154 BaseWithActivityRecordData(@equenceId long sequenceId, @NonNull @ActivityRecordProto byte[] snapshot)155 protected BaseWithActivityRecordData(@SequenceId long sequenceId, 156 @NonNull @ActivityRecordProto byte[] snapshot) { 157 super(sequenceId); 158 activityRecordSnapshot = snapshot; 159 160 Objects.requireNonNull(snapshot, "snapshot"); 161 } 162 163 @Override equals(Object other)164 public boolean equals(Object other) { 165 if (other instanceof BaseWithActivityRecordData) { 166 return (Arrays.equals(activityRecordSnapshot, 167 ((BaseWithActivityRecordData)other).activityRecordSnapshot)) && 168 super.equals(other); 169 } 170 return false; 171 } 172 173 @Override toStringBody()174 protected String toStringBody() { 175 return ", " + new String(activityRecordSnapshot); 176 } 177 178 @Override writeToParcelImpl(Parcel p, int flags)179 protected void writeToParcelImpl(Parcel p, int flags) { 180 super.writeToParcelImpl(p, flags); 181 ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags); 182 } 183 BaseWithActivityRecordData(Parcel p)184 BaseWithActivityRecordData(Parcel p) { 185 super(p); 186 activityRecordSnapshot = ActivityRecordProtoParcelable.create(p); 187 } 188 } 189 190 public static final class ActivityLaunched extends BaseWithActivityRecordData { 191 public final @ActivityMetricsLaunchObserver.Temperature 192 int temperature; 193 ActivityLaunched(@equenceId long sequenceId, @NonNull @ActivityRecordProto byte[] snapshot, @ActivityMetricsLaunchObserver.Temperature int temperature)194 public ActivityLaunched(@SequenceId long sequenceId, 195 @NonNull @ActivityRecordProto byte[] snapshot, 196 @ActivityMetricsLaunchObserver.Temperature int temperature) { 197 super(sequenceId, snapshot); 198 this.temperature = temperature; 199 } 200 201 @Override equals(Object other)202 public boolean equals(Object other) { 203 if (other instanceof ActivityLaunched) { 204 return temperature == ((ActivityLaunched)other).temperature && 205 super.equals(other); 206 } 207 return false; 208 } 209 210 @Override toStringBody()211 protected String toStringBody() { 212 return super.toStringBody() + ", temperature=" + Integer.toString(temperature); 213 } 214 215 @Override writeToParcelImpl(Parcel p, int flags)216 protected void writeToParcelImpl(Parcel p, int flags) { 217 super.writeToParcelImpl(p, flags); 218 p.writeInt(temperature); 219 } 220 ActivityLaunched(Parcel p)221 ActivityLaunched(Parcel p) { 222 super(p); 223 temperature = p.readInt(); 224 } 225 } 226 227 public static final class ActivityLaunchFinished extends BaseWithActivityRecordData { 228 public final long timestampNs; 229 ActivityLaunchFinished(@equenceId long sequenceId, @NonNull @ActivityRecordProto byte[] snapshot, long timestampNs)230 public ActivityLaunchFinished(@SequenceId long sequenceId, 231 @NonNull @ActivityRecordProto byte[] snapshot, 232 long timestampNs) { 233 super(sequenceId, snapshot); 234 this.timestampNs = timestampNs; 235 } 236 237 @Override equals(Object other)238 public boolean equals(Object other) { 239 if (other instanceof ActivityLaunchFinished) { 240 return timestampNs == ((ActivityLaunchFinished)other).timestampNs && 241 super.equals(other); 242 } 243 return false; 244 } 245 246 @Override toStringBody()247 protected String toStringBody() { 248 return super.toStringBody() + ", timestampNs=" + Long.toString(timestampNs); 249 } 250 251 @Override writeToParcelImpl(Parcel p, int flags)252 protected void writeToParcelImpl(Parcel p, int flags) { 253 super.writeToParcelImpl(p, flags); 254 p.writeLong(timestampNs); 255 } 256 ActivityLaunchFinished(Parcel p)257 ActivityLaunchFinished(Parcel p) { 258 super(p); 259 timestampNs = p.readLong(); 260 } 261 } 262 263 public static class ActivityLaunchCancelled extends AppLaunchEvent { 264 public final @Nullable @ActivityRecordProto byte[] activityRecordSnapshot; 265 ActivityLaunchCancelled(@equenceId long sequenceId, @Nullable @ActivityRecordProto byte[] snapshot)266 public ActivityLaunchCancelled(@SequenceId long sequenceId, 267 @Nullable @ActivityRecordProto byte[] snapshot) { 268 super(sequenceId); 269 activityRecordSnapshot = snapshot; 270 } 271 272 @Override equals(Object other)273 public boolean equals(Object other) { 274 if (other instanceof ActivityLaunchCancelled) { 275 return Arrays.equals(activityRecordSnapshot, 276 ((ActivityLaunchCancelled)other).activityRecordSnapshot) && 277 super.equals(other); 278 } 279 return false; 280 } 281 282 @Override toStringBody()283 protected String toStringBody() { 284 return super.toStringBody() + ", " + new String(activityRecordSnapshot); 285 } 286 287 @Override writeToParcelImpl(Parcel p, int flags)288 protected void writeToParcelImpl(Parcel p, int flags) { 289 super.writeToParcelImpl(p, flags); 290 if (activityRecordSnapshot != null) { 291 p.writeBoolean(true); 292 ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags); 293 } else { 294 p.writeBoolean(false); 295 } 296 } 297 ActivityLaunchCancelled(Parcel p)298 ActivityLaunchCancelled(Parcel p) { 299 super(p); 300 if (p.readBoolean()) { 301 activityRecordSnapshot = ActivityRecordProtoParcelable.create(p); 302 } else { 303 activityRecordSnapshot = null; 304 } 305 } 306 } 307 308 public static final class ReportFullyDrawn extends BaseWithActivityRecordData { 309 public final long timestampNs; 310 ReportFullyDrawn(@equenceId long sequenceId, @NonNull @ActivityRecordProto byte[] snapshot, long timestampNs)311 public ReportFullyDrawn(@SequenceId long sequenceId, 312 @NonNull @ActivityRecordProto byte[] snapshot, 313 long timestampNs) { 314 super(sequenceId, snapshot); 315 this.timestampNs = timestampNs; 316 } 317 318 @Override equals(Object other)319 public boolean equals(Object other) { 320 if (other instanceof ReportFullyDrawn) { 321 return timestampNs == ((ReportFullyDrawn)other).timestampNs && 322 super.equals(other); 323 } 324 return false; 325 } 326 327 @Override toStringBody()328 protected String toStringBody() { 329 return super.toStringBody() + ", timestampNs=" + Long.toString(timestampNs); 330 } 331 332 @Override writeToParcelImpl(Parcel p, int flags)333 protected void writeToParcelImpl(Parcel p, int flags) { 334 super.writeToParcelImpl(p, flags); 335 p.writeLong(timestampNs); 336 } 337 ReportFullyDrawn(Parcel p)338 ReportFullyDrawn(Parcel p) { 339 super(p); 340 timestampNs = p.readLong(); 341 } 342 } 343 344 @Override describeContents()345 public @ContentsFlags int describeContents() { return 0; } 346 347 @Override writeToParcel(Parcel p, @WriteFlags int flags)348 public void writeToParcel(Parcel p, @WriteFlags int flags) { 349 p.writeInt(getTypeIndex()); 350 351 writeToParcelImpl(p, flags); 352 } 353 354 355 public static Creator<AppLaunchEvent> CREATOR = 356 new Creator<AppLaunchEvent>() { 357 @Override 358 public AppLaunchEvent createFromParcel(Parcel source) { 359 int typeIndex = source.readInt(); 360 361 Class<?> kls = getClassFromTypeIndex(typeIndex); 362 if (kls == null) { 363 throw new IllegalArgumentException("Invalid type index: " + typeIndex); 364 } 365 366 try { 367 return (AppLaunchEvent) kls.getConstructor(Parcel.class).newInstance(source); 368 } catch (InstantiationException e) { 369 throw new AssertionError(e); 370 } catch (IllegalAccessException e) { 371 throw new AssertionError(e); 372 } catch (InvocationTargetException e) { 373 throw new AssertionError(e); 374 } catch (NoSuchMethodException e) { 375 throw new AssertionError(e); 376 } 377 } 378 379 @Override 380 public AppLaunchEvent[] newArray(int size) { 381 return new AppLaunchEvent[0]; 382 } 383 }; 384 writeToParcelImpl(Parcel p, int flags)385 protected void writeToParcelImpl(Parcel p, int flags) { 386 p.writeLong(sequenceId); 387 } 388 AppLaunchEvent(Parcel p)389 protected AppLaunchEvent(Parcel p) { 390 sequenceId = p.readLong(); 391 } 392 getTypeIndex()393 private int getTypeIndex() { 394 for (int i = 0; i < sTypes.length; ++i) { 395 if (sTypes[i].equals(this.getClass())) { 396 return i; 397 } 398 } 399 throw new AssertionError("sTypes did not include this type: " + this.getClass()); 400 } 401 getClassFromTypeIndex(int typeIndex)402 private static @Nullable Class<?> getClassFromTypeIndex(int typeIndex) { 403 if (typeIndex >= 0 && typeIndex < sTypes.length) { 404 return sTypes[typeIndex]; 405 } 406 return null; 407 } 408 409 // Index position matters: It is used to encode the specific type in parceling. 410 // Keep up-to-date with C++ side. 411 private static Class<?>[] sTypes = new Class[] { 412 IntentStarted.class, 413 IntentFailed.class, 414 ActivityLaunched.class, 415 ActivityLaunchFinished.class, 416 ActivityLaunchCancelled.class, 417 ReportFullyDrawn.class, 418 }; 419 420 public static class ActivityRecordProtoParcelable { write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot, int flags)421 public static void write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot, 422 int flags) { 423 p.writeByteArray(activityRecordSnapshot); 424 } 425 create(Parcel p)426 public static @ActivityRecordProto byte[] create(Parcel p) { 427 byte[] data = p.createByteArray(); 428 429 return data; 430 } 431 } 432 433 public static class IntentProtoParcelable { 434 private static final int INTENT_PROTO_CHUNK_SIZE = 1024; 435 write(Parcel p, @NonNull Intent intent, int flags)436 public static void write(Parcel p, @NonNull Intent intent, int flags) { 437 // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream, 438 // so create a new one every time. 439 final ProtoOutputStream protoOutputStream = 440 new ProtoOutputStream(INTENT_PROTO_CHUNK_SIZE); 441 // Write this data out as the top-most IntentProto (i.e. it is not a sub-object). 442 intent.dumpDebug(protoOutputStream); 443 final byte[] bytes = protoOutputStream.getBytes(); 444 445 p.writeByteArray(bytes); 446 } 447 448 // TODO: Should be mockable for testing? 449 // We cannot deserialize in the platform because we don't have a 'readFromProto' 450 // code. create(Parcel p)451 public static @NonNull Intent create(Parcel p) { 452 // This will "read" the correct amount of data, but then we discard it. 453 byte[] data = p.createByteArray(); 454 455 // Never called by real code in a platform, this binder API is implemented only in C++. 456 return new Intent("<cannot deserialize IntentProto>"); 457 } 458 } 459 } 460