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