1 /* 2 * Copyright (C) 2024 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.server.art.prereboot; 18 19 import static com.android.server.art.model.DexoptResult.PackageDexoptResult; 20 import static com.android.server.art.proto.PreRebootStats.Status; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.os.ArtModuleServiceManager; 26 import android.os.Build; 27 import android.os.CancellationSignal; 28 29 import androidx.annotation.RequiresApi; 30 31 import com.android.server.LocalManagerRegistry; 32 import com.android.server.art.ArtManagerLocal; 33 import com.android.server.art.ArtdRefCache; 34 import com.android.server.art.AsLog; 35 import com.android.server.art.ReasonMapping; 36 import com.android.server.art.Utils; 37 import com.android.server.art.model.ArtFlags; 38 import com.android.server.art.model.BatchDexoptParams; 39 import com.android.server.art.model.DexoptResult; 40 import com.android.server.art.model.OperationProgress; 41 import com.android.server.art.proto.BatchDexoptParamsProto; 42 import com.android.server.pm.PackageManagerLocal; 43 44 import com.google.protobuf.InvalidProtocolBufferException; 45 46 import java.util.ArrayList; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.Objects; 50 import java.util.concurrent.ExecutorService; 51 import java.util.concurrent.Executors; 52 import java.util.concurrent.TimeUnit; 53 import java.util.function.Consumer; 54 55 /** 56 * Implementation of {@link PreRebootManagerInterface}. 57 * 58 * DO NOT add a constructor with parameters! There can't be stability guarantees on constructors as 59 * they can't be checked against the interface. 60 * 61 * During Pre-reboot Dexopt, the new version of this code is run. 62 * 63 * @hide 64 */ 65 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 66 public class PreRebootManager implements PreRebootManagerInterface { run(@onNull ArtModuleServiceManager artModuleServiceManager, @NonNull Context context, @NonNull CancellationSignal cancellationSignal)67 public void run(@NonNull ArtModuleServiceManager artModuleServiceManager, 68 @NonNull Context context, @NonNull CancellationSignal cancellationSignal) { 69 PackageManagerLocal packageManagerLocal = 70 Objects.requireNonNull(LocalManagerRegistry.getManager(PackageManagerLocal.class)); 71 try (var snapshot = packageManagerLocal.withFilteredSnapshot()) { 72 run(artModuleServiceManager, context, cancellationSignal, snapshot, 73 null /* batchDexoptParamsProto */); 74 } 75 } 76 run(@onNull ArtModuleServiceManager artModuleServiceManager, @NonNull Context context, @NonNull CancellationSignal cancellationSignal, @NonNull PackageManagerLocal.FilteredSnapshot snapshot, @Nullable byte[] batchDexoptParamsProto)77 public void run(@NonNull ArtModuleServiceManager artModuleServiceManager, 78 @NonNull Context context, @NonNull CancellationSignal cancellationSignal, 79 @NonNull PackageManagerLocal.FilteredSnapshot snapshot, 80 @Nullable byte[] batchDexoptParamsProto) { 81 ExecutorService callbackExecutor = Executors.newSingleThreadExecutor(); 82 try { 83 if (!PreRebootGlobalInjector.init( 84 artModuleServiceManager, context, cancellationSignal)) { 85 return; 86 } 87 ArtManagerLocal artManagerLocal = new ArtManagerLocal(context); 88 89 var progressSession = new PreRebootStatsReporter().new ProgressSession(); 90 91 // Contains four values: skipped, performed, failed, has pre-reboot artifacts. 92 List<Integer> values = new ArrayList(List.of(0, 0, 0, 0)); 93 94 // Record every progress change right away, in case the job is interrupted by a reboot. 95 Consumer<OperationProgress> progressCallback = progress -> { 96 PackageDexoptResult result = progress.getLastPackageDexoptResult(); 97 if (result == null) { 98 return; 99 } 100 switch (result.getStatus()) { 101 case DexoptResult.DEXOPT_SKIPPED: 102 if (hasExistingArtifacts(result)) { 103 values.set(1, values.get(1) + 1); 104 } else { 105 values.set(0, values.get(0) + 1); 106 } 107 break; 108 case DexoptResult.DEXOPT_PERFORMED: 109 values.set(1, values.get(1) + 1); 110 break; 111 case DexoptResult.DEXOPT_FAILED: 112 values.set(2, values.get(2) + 1); 113 break; 114 case DexoptResult.DEXOPT_CANCELLED: 115 break; 116 default: 117 throw new IllegalStateException("Unknown status: " + result.getStatus()); 118 } 119 if (hasExistingArtifacts(result) || result.hasUpdatedArtifacts()) { 120 values.set(3, values.get(3) + 1); 121 } 122 123 progressSession.recordProgress(values.get(0), values.get(1), values.get(2), 124 progress.getTotal(), values.get(3)); 125 }; 126 127 BatchDexoptParams params; 128 try { 129 params = batchDexoptParamsProto != null 130 ? BatchDexoptParams.fromProto( 131 BatchDexoptParamsProto.parseFrom(batchDexoptParamsProto)) 132 : null; 133 } catch (InvalidProtocolBufferException e) { 134 throw new IllegalArgumentException(e); 135 } 136 137 artManagerLocal.dexoptPackagesWithParams(snapshot, 138 ReasonMapping.REASON_PRE_REBOOT_DEXOPT, cancellationSignal, callbackExecutor, 139 Map.of(ArtFlags.PASS_MAIN, progressCallback), params); 140 } finally { 141 ArtdRefCache.getInstance().reset(); 142 callbackExecutor.shutdown(); 143 try { 144 // Make sure we have no running threads when we tear down. 145 callbackExecutor.awaitTermination(5, TimeUnit.SECONDS); 146 } catch (InterruptedException e) { 147 AsLog.wtf("Interrupted", e); 148 } 149 } 150 } 151 hasExistingArtifacts(@onNull PackageDexoptResult result)152 private boolean hasExistingArtifacts(@NonNull PackageDexoptResult result) { 153 return result.getDexContainerFileDexoptResults().stream().anyMatch(fileResult 154 -> (fileResult.getExtendedStatusFlags() 155 & DexoptResult.EXTENDED_SKIPPED_PRE_REBOOT_ALREADY_EXIST) 156 != 0); 157 } 158 } 159