1 /* 2 * Copyright (C) 2021 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.android.car.watchdog; 18 19 import static com.android.car.watchdog.WatchdogPerfHandler.INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS; 20 import static com.android.car.watchdog.WatchdogPerfHandler.INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA; 21 22 import android.automotive.watchdog.PerStateBytes; 23 import android.automotive.watchdog.internal.ApplicationCategoryType; 24 import android.automotive.watchdog.internal.ComponentType; 25 import android.automotive.watchdog.internal.IoOveruseConfiguration; 26 import android.automotive.watchdog.internal.PackageMetadata; 27 import android.automotive.watchdog.internal.PerStateIoOveruseThreshold; 28 import android.automotive.watchdog.internal.ResourceOveruseConfiguration; 29 import android.automotive.watchdog.internal.ResourceSpecificConfiguration; 30 import android.car.builtin.util.Slogf; 31 import android.util.ArrayMap; 32 import android.util.ArraySet; 33 import android.util.SparseArray; 34 import android.util.proto.ProtoOutputStream; 35 36 import com.android.car.internal.util.IndentingPrintWriter; 37 import com.android.car.watchdog.PerformanceDump.IoThresholdByAppCategory; 38 import com.android.car.watchdog.PerformanceDump.IoThresholdByComponent; 39 import com.android.car.watchdog.PerformanceDump.IoThresholdByPackage; 40 import com.android.car.watchdog.PerformanceDump.OveruseConfigurationCacheDump; 41 import com.android.car.watchdog.PerformanceDump.PackageByAppCategory; 42 import com.android.internal.annotations.GuardedBy; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 import java.util.Set; 47 import java.util.function.BiFunction; 48 49 /** 50 * Cache to store overuse configurations in memory. 51 * 52 * <p>It assumes that the error checking and loading/merging initial configs are done prior to 53 * setting the cache. 54 */ 55 public final class OveruseConfigurationCache { 56 private static final String TAG = OveruseConfigurationCache.class.getSimpleName(); 57 static final PerStateBytes DEFAULT_THRESHOLD = 58 constructPerStateBytes(Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE); 59 60 private final Object mLock = new Object(); 61 @GuardedBy("mLock") 62 private final ArraySet<String> mSafeToKillSystemPackages = new ArraySet<>(); 63 @GuardedBy("mLock") 64 private final ArraySet<String> mSafeToKillVendorPackages = new ArraySet<>(); 65 @GuardedBy("mLock") 66 private final List<String> mVendorPackagePrefixes = new ArrayList<>(); 67 @GuardedBy("mLock") 68 private final SparseArray<ArraySet<String>> mPackagesByAppCategoryType = new SparseArray<>(); 69 @GuardedBy("mLock") 70 private final SparseArray<PerStateBytes> mGenericIoThresholdsByComponent = new SparseArray<>(); 71 @GuardedBy("mLock") 72 private final ArrayMap<String, PerStateBytes> mIoThresholdsBySystemPackages = new ArrayMap<>(); 73 @GuardedBy("mLock") 74 private final ArrayMap<String, PerStateBytes> mIoThresholdsByVendorPackages = new ArrayMap<>(); 75 @GuardedBy("mLock") 76 private final SparseArray<PerStateBytes> mIoThresholdsByAppCategoryType = new SparseArray<>(); 77 78 /** Dumps the contents of the cache. */ dump(IndentingPrintWriter writer)79 public void dump(IndentingPrintWriter writer) { 80 writer.println("*" + getClass().getSimpleName() + "*"); 81 writer.increaseIndent(); 82 synchronized (mLock) { 83 writer.println("mSafeToKillSystemPackages: " + mSafeToKillSystemPackages); 84 writer.println("mSafeToKillVendorPackages: " + mSafeToKillVendorPackages); 85 writer.println("mVendorPackagePrefixes: " + mVendorPackagePrefixes); 86 writer.println("mPackagesByAppCategoryType: "); 87 writer.increaseIndent(); 88 for (int i = 0; i < mPackagesByAppCategoryType.size(); ++i) { 89 writer.print("App category: " 90 + toApplicationCategoryTypeString(mPackagesByAppCategoryType.keyAt(i))); 91 writer.println(", Packages: " + mPackagesByAppCategoryType.valueAt(i)); 92 } 93 writer.decreaseIndent(); 94 writer.println("mGenericIoThresholdsByComponent: "); 95 writer.increaseIndent(); 96 for (int i = 0; i < mGenericIoThresholdsByComponent.size(); ++i) { 97 writer.print("Component type: " 98 + toComponentTypeString(mGenericIoThresholdsByComponent.keyAt(i))); 99 writer.print(", Threshold: "); 100 dumpPerStateBytes(mGenericIoThresholdsByComponent.valueAt(i), writer); 101 } 102 writer.decreaseIndent(); 103 writer.println("mIoThresholdsBySystemPackages: "); 104 writer.increaseIndent(); 105 for (int i = 0; i < mIoThresholdsBySystemPackages.size(); ++i) { 106 writer.print("Package name: " + mIoThresholdsBySystemPackages.keyAt(i)); 107 writer.print(", Threshold: "); 108 dumpPerStateBytes(mIoThresholdsBySystemPackages.valueAt(i), writer); 109 } 110 writer.decreaseIndent(); 111 writer.println("mIoThresholdsByVendorPackages: "); 112 writer.increaseIndent(); 113 for (int i = 0; i < mIoThresholdsByVendorPackages.size(); ++i) { 114 writer.print("Package name: " + mIoThresholdsByVendorPackages.keyAt(i)); 115 writer.print(", Threshold: "); 116 dumpPerStateBytes(mIoThresholdsByVendorPackages.valueAt(i), writer); 117 } 118 writer.decreaseIndent(); 119 writer.println("mIoThresholdsByAppCategoryType: "); 120 writer.increaseIndent(); 121 for (int i = 0; i < mIoThresholdsByAppCategoryType.size(); ++i) { 122 writer.print("App category: " 123 + toApplicationCategoryTypeString(mIoThresholdsByAppCategoryType.keyAt(i))); 124 writer.print(", Threshold: "); 125 dumpPerStateBytes(mIoThresholdsByAppCategoryType.valueAt(i), writer); 126 } 127 writer.decreaseIndent(); 128 } 129 writer.decreaseIndent(); 130 } 131 132 /** Dumps the contents of the cache in proto format. */ dumpProto(ProtoOutputStream proto)133 public void dumpProto(ProtoOutputStream proto) { 134 synchronized (mLock) { 135 long overuseConfigurationCacheDumpToken = proto.start( 136 PerformanceDump.OVERUSE_CONFIGURATION_CACHE_DUMP); 137 for (int i = 0; i < mSafeToKillSystemPackages.size(); i++) { 138 proto.write(OveruseConfigurationCacheDump.SAFE_TO_KILL_SYSTEM_PACKAGES, 139 mSafeToKillSystemPackages.valueAt(i)); 140 } 141 for (int i = 0; i < mSafeToKillVendorPackages.size(); i++) { 142 proto.write(OveruseConfigurationCacheDump.SAFE_TO_KILL_VENDOR_PACKAGES, 143 mSafeToKillVendorPackages.valueAt(i)); 144 } 145 for (int i = 0; i < mVendorPackagePrefixes.size(); i++) { 146 proto.write(OveruseConfigurationCacheDump.VENDOR_PACKAGE_PREFIXES, 147 mVendorPackagePrefixes.get(i)); 148 } 149 150 for (int i = 0; i < mPackagesByAppCategoryType.size(); i++) { 151 long packageByAppCategoryToken = proto.start( 152 OveruseConfigurationCacheDump.PACKAGES_BY_APP_CATEGORY); 153 proto.write(PackageByAppCategory.APPLICATION_CATEGORY, 154 toProtoApplicationCategory(mPackagesByAppCategoryType.keyAt(i))); 155 ArraySet<String> packages = mPackagesByAppCategoryType.valueAt(i); 156 for (int j = 0; j < packages.size(); j++) { 157 proto.write(PackageByAppCategory.PACKAGE_NAME, 158 packages.valueAt(j)); 159 } 160 proto.end(packageByAppCategoryToken); 161 } 162 163 for (int i = 0; i < mGenericIoThresholdsByComponent.size(); i++) { 164 long ioThresholdByComponentToken = proto.start( 165 OveruseConfigurationCacheDump.GENERIC_IO_THRESHOLDS_BY_COMPONENT); 166 proto.write(IoThresholdByComponent.COMPONENT_TYPE, 167 toProtoComponentType(mGenericIoThresholdsByComponent.keyAt(i))); 168 long perStateBytesToken = proto.start(IoThresholdByComponent.THRESHOLD); 169 PerStateBytes perStateBytes = mGenericIoThresholdsByComponent.valueAt(i); 170 proto.write(PerformanceDump.PerStateBytes.FOREGROUND_BYTES, 171 perStateBytes.foregroundBytes); 172 proto.write(PerformanceDump.PerStateBytes.BACKGROUND_BYTES, 173 perStateBytes.backgroundBytes); 174 proto.write(PerformanceDump.PerStateBytes.GARAGEMODE_BYTES, 175 perStateBytes.garageModeBytes); 176 proto.end(perStateBytesToken); 177 proto.end(ioThresholdByComponentToken); 178 } 179 180 for (int i = 0; i < mIoThresholdsBySystemPackages.size(); i++) { 181 long ioThresholdByPackageToken = proto.start( 182 OveruseConfigurationCacheDump.IO_THRESHOLDS_BY_PACKAGE); 183 proto.write(IoThresholdByPackage.PACKAGE_TYPE, PerformanceDump.SYSTEM); 184 long perStateBytesToken = proto.start(IoThresholdByComponent.THRESHOLD); 185 PerStateBytes perStateBytes = mIoThresholdsBySystemPackages.valueAt(i); 186 proto.write(PerformanceDump.PerStateBytes.FOREGROUND_BYTES, 187 perStateBytes.foregroundBytes); 188 proto.write(PerformanceDump.PerStateBytes.BACKGROUND_BYTES, 189 perStateBytes.backgroundBytes); 190 proto.write(PerformanceDump.PerStateBytes.GARAGEMODE_BYTES, 191 perStateBytes.garageModeBytes); 192 proto.end(perStateBytesToken); 193 proto.write(IoThresholdByPackage.PACKAGE_NAME, 194 mIoThresholdsBySystemPackages.keyAt(i)); 195 proto.end(ioThresholdByPackageToken); 196 } 197 198 for (int i = 0; i < mIoThresholdsByVendorPackages.size(); i++) { 199 long ioThresholdByPackageToken = proto.start( 200 OveruseConfigurationCacheDump.IO_THRESHOLDS_BY_PACKAGE); 201 proto.write(IoThresholdByPackage.PACKAGE_TYPE, PerformanceDump.VENDOR); 202 long perStateBytesToken = proto.start(IoThresholdByComponent.THRESHOLD); 203 PerStateBytes perStateBytes = mIoThresholdsByVendorPackages.valueAt(i); 204 proto.write(PerformanceDump.PerStateBytes.FOREGROUND_BYTES, 205 perStateBytes.foregroundBytes); 206 proto.write(PerformanceDump.PerStateBytes.BACKGROUND_BYTES, 207 perStateBytes.backgroundBytes); 208 proto.write(PerformanceDump.PerStateBytes.GARAGEMODE_BYTES, 209 perStateBytes.garageModeBytes); 210 proto.end(perStateBytesToken); 211 proto.write(IoThresholdByPackage.PACKAGE_NAME, 212 mIoThresholdsByVendorPackages.keyAt(i)); 213 proto.end(ioThresholdByPackageToken); 214 } 215 216 for (int i = 0; i < mIoThresholdsByAppCategoryType.size(); i++) { 217 long ioThresholdsByAppCategoryTypeToken = proto.start( 218 OveruseConfigurationCacheDump.THRESHOLDS_BY_APP_CATEGORY); 219 proto.write(IoThresholdByAppCategory.APPLICATION_CATEGORY, 220 toProtoApplicationCategory(mIoThresholdsByAppCategoryType.keyAt(i))); 221 long perStateBytesToken = proto.start(IoThresholdByAppCategory.THRESHOLD); 222 PerStateBytes perStateBytes = mIoThresholdsByAppCategoryType.valueAt(i); 223 proto.write(PerformanceDump.PerStateBytes.FOREGROUND_BYTES, 224 perStateBytes.foregroundBytes); 225 proto.write(PerformanceDump.PerStateBytes.BACKGROUND_BYTES, 226 perStateBytes.backgroundBytes); 227 proto.write(PerformanceDump.PerStateBytes.GARAGEMODE_BYTES, 228 perStateBytes.garageModeBytes); 229 proto.end(perStateBytesToken); 230 proto.end(ioThresholdsByAppCategoryTypeToken); 231 } 232 233 proto.end(overuseConfigurationCacheDumpToken); 234 } 235 } 236 237 /** Overwrites the configurations in the cache. */ set(List<ResourceOveruseConfiguration> configs)238 public void set(List<ResourceOveruseConfiguration> configs) { 239 synchronized (mLock) { 240 clearLocked(); 241 for (int i = 0; i < configs.size(); i++) { 242 ResourceOveruseConfiguration config = configs.get(i); 243 switch (config.componentType) { 244 case ComponentType.SYSTEM: 245 mSafeToKillSystemPackages.addAll(config.safeToKillPackages); 246 break; 247 case ComponentType.VENDOR: 248 mSafeToKillVendorPackages.addAll(config.safeToKillPackages); 249 mVendorPackagePrefixes.addAll(config.vendorPackagePrefixes); 250 for (int j = 0; j < config.packageMetadata.size(); ++j) { 251 PackageMetadata meta = config.packageMetadata.get(j); 252 ArraySet<String> packages = 253 mPackagesByAppCategoryType.get(meta.appCategoryType); 254 if (packages == null) { 255 packages = new ArraySet<>(); 256 } 257 packages.add(meta.packageName); 258 mPackagesByAppCategoryType.append(meta.appCategoryType, packages); 259 } 260 break; 261 default: 262 // All third-party apps are killable. 263 break; 264 } 265 for (int j = 0; j < config.resourceSpecificConfigurations.size(); ++j) { 266 if (config.resourceSpecificConfigurations.get(j).getTag() 267 == ResourceSpecificConfiguration.ioOveruseConfiguration) { 268 setIoThresholdsLocked(config.componentType, 269 config.resourceSpecificConfigurations.get(j) 270 .getIoOveruseConfiguration()); 271 } 272 } 273 } 274 } 275 } 276 277 /** Returns the threshold for the given package and component type. */ fetchThreshold(String genericPackageName, @ComponentType int componentType)278 public PerStateBytes fetchThreshold(String genericPackageName, 279 @ComponentType int componentType) { 280 synchronized (mLock) { 281 PerStateBytes threshold = null; 282 switch (componentType) { 283 case ComponentType.SYSTEM: 284 threshold = mIoThresholdsBySystemPackages.get(genericPackageName); 285 if (threshold != null) { 286 return copyPerStateBytes(threshold); 287 } 288 break; 289 case ComponentType.VENDOR: 290 threshold = mIoThresholdsByVendorPackages.get(genericPackageName); 291 if (threshold != null) { 292 return copyPerStateBytes(threshold); 293 } 294 break; 295 default: 296 // THIRD_PARTY and UNKNOWN components are set with the 297 // default thresholds. 298 break; 299 } 300 threshold = fetchAppCategorySpecificThresholdLocked(genericPackageName); 301 if (threshold != null) { 302 return copyPerStateBytes(threshold); 303 } 304 threshold = mGenericIoThresholdsByComponent.get(componentType); 305 return threshold != null ? copyPerStateBytes(threshold) 306 : copyPerStateBytes(DEFAULT_THRESHOLD); 307 } 308 } 309 310 /** Returns whether or not the given package is safe-to-kill on resource overuse. */ isSafeToKill(String genericPackageName, @ComponentType int componentType, List<String> sharedPackages)311 public boolean isSafeToKill(String genericPackageName, @ComponentType int componentType, 312 List<String> sharedPackages) { 313 synchronized (mLock) { 314 BiFunction<List<String>, Set<String>, Boolean> isSafeToKillAnyPackage = 315 (packages, safeToKillPackages) -> { 316 if (packages == null) { 317 return false; 318 } 319 for (int i = 0; i < packages.size(); i++) { 320 if (safeToKillPackages.contains(packages.get(i))) { 321 return true; 322 } 323 } 324 return false; 325 }; 326 327 switch (componentType) { 328 case ComponentType.SYSTEM: 329 if (mSafeToKillSystemPackages.contains(genericPackageName)) { 330 return true; 331 } 332 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages); 333 case ComponentType.VENDOR: 334 if (mSafeToKillVendorPackages.contains(genericPackageName)) { 335 return true; 336 } 337 /* 338 * Packages under the vendor shared UID may contain system packages because when 339 * CarWatchdogService derives the shared component type it attributes system 340 * packages as vendor packages when there is at least one vendor package. 341 */ 342 return isSafeToKillAnyPackage.apply(sharedPackages, mSafeToKillSystemPackages) 343 || isSafeToKillAnyPackage.apply(sharedPackages, 344 mSafeToKillVendorPackages); 345 default: 346 // Third-party apps are always killable 347 return true; 348 } 349 } 350 } 351 352 /** Returns the list of vendor package prefixes. */ getVendorPackagePrefixes()353 public List<String> getVendorPackagePrefixes() { 354 synchronized (mLock) { 355 return new ArrayList<>(mVendorPackagePrefixes); 356 } 357 } 358 359 @GuardedBy("mLock") clearLocked()360 private void clearLocked() { 361 mSafeToKillSystemPackages.clear(); 362 mSafeToKillVendorPackages.clear(); 363 mVendorPackagePrefixes.clear(); 364 mPackagesByAppCategoryType.clear(); 365 mGenericIoThresholdsByComponent.clear(); 366 mIoThresholdsBySystemPackages.clear(); 367 mIoThresholdsByVendorPackages.clear(); 368 mIoThresholdsByAppCategoryType.clear(); 369 } 370 371 @GuardedBy("mLock") setIoThresholdsLocked(int componentType, IoOveruseConfiguration ioConfig)372 private void setIoThresholdsLocked(int componentType, IoOveruseConfiguration ioConfig) { 373 mGenericIoThresholdsByComponent.append(componentType, 374 ioConfig.componentLevelThresholds.perStateWriteBytes); 375 switch (componentType) { 376 case ComponentType.SYSTEM: 377 populateThresholdsByPackagesLocked( 378 ioConfig.packageSpecificThresholds, mIoThresholdsBySystemPackages); 379 break; 380 case ComponentType.VENDOR: 381 populateThresholdsByPackagesLocked( 382 ioConfig.packageSpecificThresholds, mIoThresholdsByVendorPackages); 383 setIoThresholdsByAppCategoryTypeLocked(ioConfig.categorySpecificThresholds); 384 break; 385 default: 386 Slogf.i(TAG, "Ignoring I/O overuse threshold for invalid component type: %d", 387 componentType); 388 } 389 } 390 391 @GuardedBy("mLock") setIoThresholdsByAppCategoryTypeLocked( List<PerStateIoOveruseThreshold> thresholds)392 private void setIoThresholdsByAppCategoryTypeLocked( 393 List<PerStateIoOveruseThreshold> thresholds) { 394 for (int i = 0; i < thresholds.size(); ++i) { 395 PerStateIoOveruseThreshold threshold = thresholds.get(i); 396 switch(threshold.name) { 397 case INTERNAL_APPLICATION_CATEGORY_TYPE_MAPS: 398 mIoThresholdsByAppCategoryType.append( 399 ApplicationCategoryType.MAPS, threshold.perStateWriteBytes); 400 break; 401 case INTERNAL_APPLICATION_CATEGORY_TYPE_MEDIA: 402 mIoThresholdsByAppCategoryType.append(ApplicationCategoryType.MEDIA, 403 threshold.perStateWriteBytes); 404 break; 405 default: 406 Slogf.i(TAG, 407 "Ignoring I/O overuse threshold for invalid application category: %s", 408 threshold.name); 409 } 410 } 411 } 412 413 @GuardedBy("mLock") populateThresholdsByPackagesLocked(List<PerStateIoOveruseThreshold> thresholds, ArrayMap<String, PerStateBytes> thresholdsByPackages)414 private void populateThresholdsByPackagesLocked(List<PerStateIoOveruseThreshold> thresholds, 415 ArrayMap<String, PerStateBytes> thresholdsByPackages) { 416 for (int i = 0; i < thresholds.size(); ++i) { 417 thresholdsByPackages.put( 418 thresholds.get(i).name, thresholds.get(i).perStateWriteBytes); 419 } 420 } 421 422 @GuardedBy("mLock") fetchAppCategorySpecificThresholdLocked(String genericPackageName)423 private PerStateBytes fetchAppCategorySpecificThresholdLocked(String genericPackageName) { 424 for (int i = 0; i < mPackagesByAppCategoryType.size(); ++i) { 425 if (mPackagesByAppCategoryType.valueAt(i).contains(genericPackageName)) { 426 return mIoThresholdsByAppCategoryType.get(mPackagesByAppCategoryType.keyAt(i)); 427 } 428 } 429 return null; 430 } 431 toApplicationCategoryTypeString(@pplicationCategoryType int type)432 private static String toApplicationCategoryTypeString(@ApplicationCategoryType int type) { 433 switch (type) { 434 case ApplicationCategoryType.MAPS: 435 return "ApplicationCategoryType.MAPS"; 436 case ApplicationCategoryType.MEDIA: 437 return "ApplicationCategoryType.MEDIA"; 438 case ApplicationCategoryType.OTHERS: 439 return "ApplicationCategoryType.OTHERS"; 440 default: 441 return "Invalid ApplicationCategoryType"; 442 } 443 } 444 toComponentTypeString(@omponentType int type)445 private static String toComponentTypeString(@ComponentType int type) { 446 switch (type) { 447 case ComponentType.SYSTEM: 448 return "ComponentType.SYSTEM"; 449 case ComponentType.VENDOR: 450 return "ComponentType.VENDOR"; 451 case ComponentType.THIRD_PARTY: 452 return "ComponentType.THIRD_PARTY"; 453 default: 454 return "ComponentType.UNKNOWN"; 455 } 456 } 457 toProtoApplicationCategory(@pplicationCategoryType int type)458 private static int toProtoApplicationCategory(@ApplicationCategoryType int type) { 459 switch (type) { 460 case ApplicationCategoryType.MAPS: 461 return PerformanceDump.MAPS; 462 case ApplicationCategoryType.MEDIA: 463 return PerformanceDump.MEDIA; 464 case ApplicationCategoryType.OTHERS: 465 return PerformanceDump.OTHERS; 466 default: 467 return PerformanceDump.APPLICATION_CATEGORY_UNSPECIFIED; 468 } 469 } 470 toProtoComponentType(@omponentType int type)471 private static int toProtoComponentType(@ComponentType int type) { 472 switch (type) { 473 case ComponentType.SYSTEM: 474 return PerformanceDump.SYSTEM; 475 case ComponentType.VENDOR: 476 return PerformanceDump.VENDOR; 477 case ComponentType.THIRD_PARTY: 478 return PerformanceDump.THIRD_PARTY; 479 default: 480 return PerformanceDump.COMPONENT_TYPE_UNSPECIFIED; 481 } 482 } 483 dumpPerStateBytes(PerStateBytes perStateBytes, IndentingPrintWriter writer)484 private static void dumpPerStateBytes(PerStateBytes perStateBytes, 485 IndentingPrintWriter writer) { 486 if (perStateBytes == null) { 487 writer.println("{NULL}"); 488 return; 489 } 490 writer.println("{Foreground bytes: " + perStateBytes.foregroundBytes 491 + ", Background bytes: " + perStateBytes.backgroundBytes + ", Garage mode bytes: " 492 + perStateBytes.garageModeBytes + '}'); 493 } 494 constructPerStateBytes(long fgBytes, long bgBytes, long gmBytes)495 private static PerStateBytes constructPerStateBytes(long fgBytes, long bgBytes, long gmBytes) { 496 return new PerStateBytes() {{ 497 foregroundBytes = fgBytes; 498 backgroundBytes = bgBytes; 499 garageModeBytes = gmBytes; 500 }}; 501 } 502 503 private static PerStateBytes copyPerStateBytes(PerStateBytes perStateBytes) { 504 return new PerStateBytes() {{ 505 foregroundBytes = perStateBytes.foregroundBytes; 506 backgroundBytes = perStateBytes.backgroundBytes; 507 garageModeBytes = perStateBytes.garageModeBytes; 508 }}; 509 } 510 } 511