• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.server.compos;
18 
19 import static android.os.Build.isDebuggable;
20 
21 import android.annotation.NonNull;
22 import android.app.job.JobScheduler;
23 import android.content.Context;
24 import android.content.pm.ApexStagedEvent;
25 import android.content.pm.IPackageManagerNative;
26 import android.content.pm.IStagedApexObserver;
27 import android.content.pm.StagedApexInfo;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.sysprop.HypervisorProperties;
31 import android.util.Log;
32 
33 import com.android.server.SystemService;
34 
35 /**
36  * A system service responsible for performing Isolated Compilation (compiling boot & system server
37  * classpath JARs in a protected VM) when appropriate.
38  *
39  * @hide
40  */
41 public class IsolatedCompilationService extends SystemService {
42     private static final String TAG = IsolatedCompilationService.class.getName();
43 
IsolatedCompilationService(@onNull Context context)44     public IsolatedCompilationService(@NonNull Context context) {
45         super(context);
46     }
47 
48     @Override
onStart()49     public void onStart() {
50         // Note that our binder service is exposed directly from native code in composd, so
51         // we don't need to do anything here.
52     }
53 
54     @Override
onBootPhase( int phase)55     public void onBootPhase(/* @BootPhase */ int phase) {
56         if (phase != PHASE_BOOT_COMPLETED) return;
57 
58         if (!isIsolatedCompilationSupported()) {
59             Log.i(TAG, "Isolated compilation not supported, not scheduling job");
60             return;
61         }
62 
63         JobScheduler scheduler = getContext().getSystemService(JobScheduler.class);
64         if (scheduler == null) {
65             Log.e(TAG, "No scheduler");
66             return;
67         }
68 
69         StagedApexObserver.registerForStagedApexUpdates(scheduler);
70     }
71 
isIsolatedCompilationSupported()72     private static boolean isIsolatedCompilationSupported() {
73         // The CompOS APEX is present or we wouldn't be here. So just check that the device
74         // has a suitably capable hypervisor.
75 
76         // We really want a protected VM
77         if (HypervisorProperties.hypervisor_protected_vm_supported().orElse(false)) {
78             return true;
79         }
80 
81         // But can use a non-protected VM on a debug build
82         if (isDebuggable()) {
83             return HypervisorProperties.hypervisor_vm_supported().orElse(false);
84         }
85 
86         return false;
87     }
88 
89     private static class StagedApexObserver extends IStagedApexObserver.Stub {
90         private final JobScheduler mScheduler;
91         private final IPackageManagerNative mPackageNative;
92 
registerForStagedApexUpdates(JobScheduler scheduler)93         static void registerForStagedApexUpdates(JobScheduler scheduler) {
94             final IPackageManagerNative packageNative =
95                     IPackageManagerNative.Stub.asInterface(
96                             ServiceManager.getService("package_native"));
97             if (packageNative == null) {
98                 Log.e(TAG, "No IPackageManagerNative");
99                 return;
100             }
101 
102             StagedApexObserver observer = new StagedApexObserver(scheduler, packageNative);
103             try {
104                 packageNative.registerStagedApexObserver(observer);
105                 // In the unlikely event that an APEX has been staged before we get here, we may
106                 // have to schedule compilation immediately.
107                 observer.checkModules(packageNative.getStagedApexInfos());
108             } catch (RemoteException e) {
109                 Log.e(TAG, "Failed to initialize observer", e);
110             }
111         }
112 
StagedApexObserver(JobScheduler scheduler, IPackageManagerNative packageNative)113         private StagedApexObserver(JobScheduler scheduler, IPackageManagerNative packageNative) {
114             mScheduler = scheduler;
115             mPackageNative = packageNative;
116         }
117 
118         @Override
onApexStaged(ApexStagedEvent event)119         public void onApexStaged(ApexStagedEvent event) {
120             Log.d(TAG, "onApexStaged");
121             checkModules(event.stagedApexInfos);
122         }
123 
checkModules(StagedApexInfo[] stagedApexInfos)124         void checkModules(StagedApexInfo[] stagedApexInfos) {
125             if (IsolatedCompilationJobService.isStagedApexJobScheduled(mScheduler)) {
126                 Log.d(TAG, "Job already scheduled");
127                 // We're going to run anyway, we don't need to check this update
128                 return;
129             }
130             boolean needCompilation = false;
131             for (StagedApexInfo apexInfo : stagedApexInfos) {
132                 if (apexInfo != null && apexInfo.hasClassPathJars) {
133                     Log.i(TAG, "Classpath affecting module updated: " + apexInfo.moduleName);
134                     needCompilation = true;
135                     break;
136                 }
137             }
138             if (needCompilation) {
139                 IsolatedCompilationJobService.scheduleStagedApexJob(mScheduler);
140             }
141         }
142     }
143 }
144