• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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