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