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.ondevicepersonalization.services.process; 18 19 import static com.android.ondevicepersonalization.services.PhFlags.KEY_IS_ART_IMAGE_LOADING_OPTIMIZATION_ENABLED; 20 import static com.android.ondevicepersonalization.services.PhFlags.KEY_TRUSTED_PARTNER_APPS_LIST; 21 22 import android.adservices.ondevicepersonalization.Constants; 23 import android.adservices.ondevicepersonalization.IsolatedServiceException; 24 import android.adservices.ondevicepersonalization.aidl.IIsolatedService; 25 import android.adservices.ondevicepersonalization.aidl.IIsolatedServiceCallback; 26 import android.annotation.NonNull; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ServiceInfo; 31 import android.os.Bundle; 32 33 import androidx.concurrent.futures.CallbackToFutureAdapter; 34 35 import com.android.federatedcompute.internal.util.AbstractServiceBinder; 36 import com.android.modules.utils.build.SdkLevel; 37 import com.android.odp.module.common.Clock; 38 import com.android.odp.module.common.MonotonicClock; 39 import com.android.odp.module.common.PackageUtils; 40 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 41 import com.android.ondevicepersonalization.services.FlagsFactory; 42 import com.android.ondevicepersonalization.services.OdpServiceException; 43 import com.android.ondevicepersonalization.services.OnDevicePersonalizationApplication; 44 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors; 45 import com.android.ondevicepersonalization.services.util.AllowListUtils; 46 47 import com.google.common.util.concurrent.FluentFuture; 48 import com.google.common.util.concurrent.Futures; 49 import com.google.common.util.concurrent.ListenableFuture; 50 import com.google.common.util.concurrent.ListeningExecutorService; 51 52 import java.util.Objects; 53 54 /** Utilities for running remote isolated services in a shared isolated process (SIP). Note that 55 * this runner is only selected when the shared_isolated_process_feature_enabled flag is enabled. 56 */ 57 public class SharedIsolatedProcessRunner implements ProcessRunner { 58 59 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 60 61 private static final String TAG = SharedIsolatedProcessRunner.class.getSimpleName(); 62 63 // SIP that hosts services from all trusted partners, as well as internal isolated services. 64 public static final String TRUSTED_PARTNER_APPS_SIP = "trusted_partner_apps_sip"; 65 66 // SIP that hosts unknown remote services. 67 public static final String UNKNOWN_APPS_SIP = "unknown_apps_sip"; 68 69 private final Context mApplicationContext; 70 private final Injector mInjector; 71 72 static class Injector { getClock()73 Clock getClock() { 74 return MonotonicClock.getInstance(); 75 } 76 getExecutor()77 ListeningExecutorService getExecutor() { 78 return OnDevicePersonalizationExecutors.getBackgroundExecutor(); 79 } 80 } SharedIsolatedProcessRunner( @onNull Context applicationContext, @NonNull Injector injector)81 SharedIsolatedProcessRunner( 82 @NonNull Context applicationContext, 83 @NonNull Injector injector) { 84 mApplicationContext = Objects.requireNonNull(applicationContext); 85 mInjector = Objects.requireNonNull(injector); 86 } 87 88 private static class LazyInstanceHolder { 89 static final SharedIsolatedProcessRunner LAZY_INSTANCE = 90 new SharedIsolatedProcessRunner( 91 OnDevicePersonalizationApplication.getAppContext(), 92 new Injector()); 93 } 94 95 /** Returns the global ProcessRunner. */ 96 @NonNull getInstance()97 public static SharedIsolatedProcessRunner getInstance() { 98 return SharedIsolatedProcessRunner.LazyInstanceHolder.LAZY_INSTANCE; 99 } 100 101 /** Binds to a service and put it in one of ODP's shared isolated process. */ 102 @Override loadIsolatedService( @onNull String taskName, @NonNull ComponentName componentName)103 @NonNull public ListenableFuture<IsolatedServiceInfo> loadIsolatedService( 104 @NonNull String taskName, @NonNull ComponentName componentName) { 105 try { 106 ListenableFuture<AbstractServiceBinder<IIsolatedService>> isolatedServiceFuture = 107 mInjector.getExecutor().submit( 108 () -> getIsolatedServiceBinder(componentName)); 109 110 return FluentFuture.from(isolatedServiceFuture) 111 .transformAsync( 112 (isolatedService) -> { 113 try { 114 return Futures.immediateFuture(new IsolatedServiceInfo( 115 mInjector.getClock().elapsedRealtime(), componentName, 116 /* pluginController= */ null, isolatedService)); 117 } catch (Exception e) { 118 return Futures.immediateFailedFuture(e); 119 } 120 }, mInjector.getExecutor()) 121 .catchingAsync( 122 Exception.class, 123 Futures::immediateFailedFuture, 124 mInjector.getExecutor()); 125 } catch (Exception e) { 126 return Futures.immediateFailedFuture(e); 127 } 128 } 129 130 /** Runs the remote isolated service in the shared isolated process. */ 131 @NonNull 132 @Override 133 public ListenableFuture<Bundle> runIsolatedService( 134 @NonNull IsolatedServiceInfo isolatedProcessInfo, int operationCode, 135 @NonNull Bundle serviceParams) { 136 return CallbackToFutureAdapter.getFuture( 137 completer -> { 138 isolatedProcessInfo.getIsolatedServiceBinder() 139 .getService(Runnable::run) 140 .onRequest( 141 operationCode, serviceParams, 142 new IIsolatedServiceCallback.Stub() { 143 @Override public void onSuccess(Bundle result) { 144 completer.set(result); 145 } 146 147 // TO-DO (323882182): Granular isolated servce failures. 148 @Override public void onError( 149 int errorCode, int isolatedServiceErrorCode) { 150 if (isolatedServiceErrorCode > 0 151 && isolatedServiceErrorCode < 128) { 152 completer.setException( 153 new OdpServiceException( 154 Constants.STATUS_SERVICE_FAILED, 155 new IsolatedServiceException( 156 isolatedServiceErrorCode))); 157 } else { 158 completer.setException( 159 new OdpServiceException( 160 Constants.STATUS_SERVICE_FAILED)); 161 } 162 } 163 }); 164 return null; 165 }); 166 } 167 168 /** Unbinds from the remote isolated service. */ 169 @NonNull 170 @Override 171 public ListenableFuture<Void> unloadIsolatedService( 172 @NonNull IsolatedServiceInfo isolatedServiceInfo) { 173 try { 174 return (ListenableFuture<Void>) mInjector.getExecutor().submit( 175 () -> isolatedServiceInfo.getIsolatedServiceBinder().unbindFromService()); 176 } catch (Exception e) { 177 return Futures.immediateFailedFuture(e); 178 } 179 } 180 181 private AbstractServiceBinder<IIsolatedService> getIsolatedServiceBinder( 182 @NonNull ComponentName service) throws Exception { 183 boolean isSipRequested = isSharedIsolatedProcessRequested(service); 184 185 // null instance name results in regular isolated service being created. 186 String instanceName = isSipRequested ? getSipInstanceName(service.getPackageName()) : null; 187 int bindFlag = isSipRequested 188 ? Context.BIND_SHARED_ISOLATED_PROCESS 189 : Context.BIND_AUTO_CREATE; 190 191 return AbstractServiceBinder.getIsolatedServiceBinderByServiceName( 192 mApplicationContext, 193 service.getClassName(), service.getPackageName(), 194 instanceName, bindFlag, IIsolatedService.Stub::asInterface); 195 } 196 197 String getSipInstanceName(String packageName) { 198 String partnerAppsList = 199 (String) FlagsFactory.getFlags().getStableFlag(KEY_TRUSTED_PARTNER_APPS_LIST); 200 String packageCertificate = null; 201 try { 202 packageCertificate = PackageUtils.getCertDigest(mApplicationContext, packageName); 203 } catch (Exception e) { 204 sLogger.d(TAG + ": not able to find certificate for package " + packageName, e); 205 } 206 boolean isPartnerApp = AllowListUtils.isAllowListed( 207 packageName, packageCertificate, partnerAppsList); 208 String sipInstanceName = isPartnerApp ? TRUSTED_PARTNER_APPS_SIP : UNKNOWN_APPS_SIP; 209 return (boolean) FlagsFactory.getFlags() 210 .getStableFlag(KEY_IS_ART_IMAGE_LOADING_OPTIMIZATION_ENABLED) 211 ? sipInstanceName + "_disable_art_image_" : sipInstanceName; 212 } 213 214 boolean isSharedIsolatedProcessRequested(ComponentName service) throws Exception { 215 if (!SdkLevel.isAtLeastU()) { 216 return false; 217 } 218 219 PackageManager pm = mApplicationContext.getPackageManager(); 220 ServiceInfo si = pm.getServiceInfo(service, PackageManager.GET_META_DATA); 221 222 if ((si.flags & si.FLAG_ISOLATED_PROCESS) == 0) { 223 throw new IllegalArgumentException("ODP client services should run in isolated processes."); 224 } 225 226 return (si.flags & si.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) != 0; 227 } 228 } 229