1 /* 2 * Copyright (C) 2022 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.sdksandbox; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.ServiceConnection; 25 import android.content.pm.PackageManager; 26 import android.content.pm.ResolveInfo; 27 import android.content.pm.ServiceInfo; 28 import android.os.RemoteException; 29 import android.os.UserHandle; 30 import android.util.ArrayMap; 31 import android.util.Log; 32 33 import com.android.internal.annotations.GuardedBy; 34 import com.android.modules.utils.build.SdkLevel; 35 import com.android.sdksandbox.ISdkSandboxService; 36 import com.android.server.LocalManagerRegistry; 37 import com.android.server.am.ActivityManagerLocal; 38 39 import java.io.PrintWriter; 40 import java.util.Objects; 41 42 import javax.annotation.concurrent.ThreadSafe; 43 44 /** 45 * Implementation of {@link SdkSandboxServiceProvider}. 46 * 47 * @hide 48 */ 49 @ThreadSafe 50 class SdkSandboxServiceProviderImpl implements SdkSandboxServiceProvider { 51 52 private static final String TAG = "SdkSandboxManager"; 53 54 private final Object mLock = new Object(); 55 56 private final Context mContext; 57 private final ActivityManagerLocal mActivityManagerLocal; 58 59 @GuardedBy("mLock") 60 private final ArrayMap<CallingInfo, SdkSandboxConnection> mAppSdkSandboxConnections = 61 new ArrayMap<>(); 62 SdkSandboxServiceProviderImpl(Context context)63 SdkSandboxServiceProviderImpl(Context context) { 64 mContext = context; 65 mActivityManagerLocal = LocalManagerRegistry.getManager(ActivityManagerLocal.class); 66 } 67 68 @Override 69 @Nullable bindService(CallingInfo callingInfo, ServiceConnection serviceConnection)70 public void bindService(CallingInfo callingInfo, ServiceConnection serviceConnection) { 71 synchronized (mLock) { 72 SdkSandboxConnection sdkSandboxConnection = getSdkSandboxConnectionLocked(callingInfo); 73 if (sdkSandboxConnection != null && sdkSandboxConnection.getStatus() != NON_EXISTENT) { 74 // The sandbox is either already created or is in the process of being 75 // created/restarted. Do not bind again. Note that later restarts can take a while, 76 // since retries are done exponentially. 77 Log.i(TAG, "SDK sandbox for " + callingInfo + " is already created"); 78 return; 79 } 80 81 Log.i(TAG, "Binding sdk sandbox for " + callingInfo); 82 83 ComponentName componentName = getServiceComponentName(); 84 if (componentName == null) { 85 Log.e(TAG, "Failed to find sdk sandbox service"); 86 notifyFailedBinding(serviceConnection); 87 return; 88 } 89 Intent intent = new Intent().setComponent(componentName); 90 91 String callingPackageName = callingInfo.getPackageName(); 92 String sandboxProcessName = null; 93 try { 94 sandboxProcessName = toSandboxProcessName(callingInfo); 95 } catch (PackageManager.NameNotFoundException e) { 96 Log.e(TAG, "bindService failed for: " + callingInfo.toString(), e); 97 notifyFailedBinding(serviceConnection); 98 return; 99 } 100 try { 101 boolean bound; 102 // For U+, we start the sandbox and then bind to it to prevent restarts. For T, 103 // the sandbox service is directly bound to using BIND_AUTO_CREATE flag which brings 104 // up the sandbox but also restarts it if the sandbox dies when bound. 105 if (SdkLevel.isAtLeastU()) { 106 ComponentName name = 107 mActivityManagerLocal.startSdkSandboxService( 108 intent, 109 callingInfo.getUid(), 110 callingPackageName, 111 sandboxProcessName); 112 if (name == null) { 113 notifyFailedBinding(serviceConnection); 114 return; 115 } 116 bound = 117 mActivityManagerLocal.bindSdkSandboxService( 118 intent, 119 serviceConnection, 120 callingInfo.getUid(), 121 callingInfo.getAppProcessToken(), 122 callingPackageName, 123 sandboxProcessName, 124 0); 125 } else { 126 // Using BIND_AUTO_CREATE will create the sandbox process. 127 bound = 128 mActivityManagerLocal.bindSdkSandboxService( 129 intent, 130 serviceConnection, 131 callingInfo.getUid(), 132 callingPackageName, 133 sandboxProcessName, 134 Context.BIND_AUTO_CREATE); 135 } 136 if (!bound) { 137 mContext.unbindService(serviceConnection); 138 notifyFailedBinding(serviceConnection); 139 return; 140 } 141 } catch (RemoteException e) { 142 notifyFailedBinding(serviceConnection); 143 return; 144 } 145 sdkSandboxConnection = new SdkSandboxConnection(serviceConnection, sandboxProcessName); 146 mAppSdkSandboxConnections.put(callingInfo, sdkSandboxConnection); 147 Log.i(TAG, "Sdk sandbox has been bound"); 148 } 149 } 150 151 // a way to notify manager that binding never happened notifyFailedBinding(ServiceConnection serviceConnection)152 private void notifyFailedBinding(ServiceConnection serviceConnection) { 153 serviceConnection.onNullBinding(null); 154 } 155 156 @Override dump(PrintWriter writer)157 public void dump(PrintWriter writer) { 158 synchronized (mLock) { 159 if (mAppSdkSandboxConnections.size() == 0) { 160 writer.println("mAppSdkSandboxConnections is empty"); 161 } else { 162 writer.print("mAppSdkSandboxConnections size: "); 163 writer.println(mAppSdkSandboxConnections.size()); 164 for (int i = 0; i < mAppSdkSandboxConnections.size(); i++) { 165 CallingInfo callingInfo = mAppSdkSandboxConnections.keyAt(i); 166 SdkSandboxConnection sdkSandboxConnection = 167 mAppSdkSandboxConnections.get(callingInfo); 168 writer.printf( 169 "Sdk sandbox for UID: %s, app package: %s, isConnected: %s Status: %d", 170 callingInfo.getUid(), 171 callingInfo.getPackageName(), 172 Objects.requireNonNull(sdkSandboxConnection).isConnected(), 173 sdkSandboxConnection.getStatus()); 174 writer.println(); 175 } 176 } 177 } 178 } 179 180 @Override unbindService(CallingInfo callingInfo)181 public void unbindService(CallingInfo callingInfo) { 182 synchronized (mLock) { 183 SdkSandboxConnection sandbox = getSdkSandboxConnectionLocked(callingInfo); 184 185 if (sandbox == null) { 186 return; 187 } 188 189 if (sandbox.isBound) { 190 try { 191 mContext.unbindService(sandbox.getServiceConnection()); 192 } catch (Exception e) { 193 // Sandbox has already unbound previously. 194 } 195 sandbox.onUnbind(); 196 Log.i(TAG, "Sdk sandbox for " + callingInfo + " has been unbound"); 197 } 198 } 199 } 200 201 @Override stopSandboxService(CallingInfo callingInfo)202 public void stopSandboxService(CallingInfo callingInfo) { 203 synchronized (mLock) { 204 SdkSandboxConnection sandbox = getSdkSandboxConnectionLocked(callingInfo); 205 206 if (!SdkLevel.isAtLeastU() || sandbox == null || sandbox.getStatus() == NON_EXISTENT) { 207 return; 208 } 209 210 ComponentName componentName = getServiceComponentName(); 211 if (componentName == null) { 212 Log.e(TAG, "Failed to find sdk sandbox service"); 213 return; 214 } 215 Intent intent = new Intent().setComponent(componentName); 216 String callingPackageName = callingInfo.getPackageName(); 217 String sandboxProcessName = sandbox.getSandboxProcessName(); 218 219 mActivityManagerLocal.stopSdkSandboxService( 220 intent, callingInfo.getUid(), callingPackageName, sandboxProcessName); 221 } 222 } 223 224 @Override 225 @Nullable getSdkSandboxServiceForApp(CallingInfo callingInfo)226 public ISdkSandboxService getSdkSandboxServiceForApp(CallingInfo callingInfo) { 227 synchronized (mLock) { 228 SdkSandboxConnection connection = getSdkSandboxConnectionLocked(callingInfo); 229 if (connection != null && connection.getStatus() == CREATED) { 230 return connection.getSdkSandboxService(); 231 } 232 } 233 return null; 234 } 235 236 @Override onServiceConnected(CallingInfo callingInfo, @NonNull ISdkSandboxService service)237 public void onServiceConnected(CallingInfo callingInfo, @NonNull ISdkSandboxService service) { 238 synchronized (mLock) { 239 SdkSandboxConnection connection = getSdkSandboxConnectionLocked(callingInfo); 240 if (connection != null) { 241 connection.onServiceConnected(service); 242 } 243 } 244 } 245 246 @Override onServiceDisconnected(CallingInfo callingInfo)247 public void onServiceDisconnected(CallingInfo callingInfo) { 248 synchronized (mLock) { 249 SdkSandboxConnection connection = getSdkSandboxConnectionLocked(callingInfo); 250 if (connection != null) { 251 connection.onServiceDisconnected(); 252 } 253 } 254 } 255 256 @Override onAppDeath(CallingInfo callingInfo)257 public void onAppDeath(CallingInfo callingInfo) { 258 synchronized (mLock) { 259 mAppSdkSandboxConnections.remove(callingInfo); 260 } 261 } 262 263 @Override onSandboxDeath(CallingInfo callingInfo)264 public void onSandboxDeath(CallingInfo callingInfo) { 265 synchronized (mLock) { 266 SdkSandboxConnection connection = getSdkSandboxConnectionLocked(callingInfo); 267 if (connection != null) { 268 connection.onSdkSandboxDeath(); 269 } 270 } 271 } 272 273 @Override isSandboxBoundForApp(CallingInfo callingInfo)274 public boolean isSandboxBoundForApp(CallingInfo callingInfo) { 275 synchronized (mLock) { 276 SdkSandboxConnection connection = getSdkSandboxConnectionLocked(callingInfo); 277 if (connection != null) { 278 synchronized (connection.mLock) { 279 return connection.isBound; 280 } 281 } 282 return false; 283 } 284 } 285 286 @Override getSandboxStatusForApp(CallingInfo callingInfo)287 public int getSandboxStatusForApp(CallingInfo callingInfo) { 288 synchronized (mLock) { 289 SdkSandboxConnection connection = getSdkSandboxConnectionLocked(callingInfo); 290 if (connection == null) { 291 return NON_EXISTENT; 292 } else { 293 return connection.getStatus(); 294 } 295 } 296 } 297 298 @Override 299 @NonNull toSandboxProcessName(@onNull CallingInfo callingInfo)300 public String toSandboxProcessName(@NonNull CallingInfo callingInfo) 301 throws PackageManager.NameNotFoundException { 302 return getProcessName(callingInfo) + SANDBOX_PROCESS_NAME_SUFFIX; 303 } 304 305 @Override 306 @NonNull toSandboxProcessNameForInstrumentation(@onNull CallingInfo callingInfo)307 public String toSandboxProcessNameForInstrumentation(@NonNull CallingInfo callingInfo) 308 throws PackageManager.NameNotFoundException { 309 return getProcessName(callingInfo) + SANDBOX_INSTR_PROCESS_NAME_SUFFIX; 310 } 311 312 @Nullable getServiceComponentName()313 private ComponentName getServiceComponentName() { 314 final Intent intent = new Intent(SdkSandboxManagerLocal.SERVICE_INTERFACE); 315 intent.setPackage(mContext.getPackageManager().getSdkSandboxPackageName()); 316 317 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, 318 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); 319 if (resolveInfo == null) { 320 Log.e(TAG, "Failed to find resolveInfo for sdk sandbox service"); 321 return null; 322 } 323 324 final ServiceInfo serviceInfo = resolveInfo.serviceInfo; 325 if (serviceInfo == null) { 326 Log.e(TAG, "Failed to find serviceInfo for sdk sandbox service"); 327 return null; 328 } 329 330 return new ComponentName(serviceInfo.packageName, serviceInfo.name); 331 } 332 333 @GuardedBy("mLock") 334 @Nullable getSdkSandboxConnectionLocked(CallingInfo callingInfo)335 private SdkSandboxConnection getSdkSandboxConnectionLocked(CallingInfo callingInfo) { 336 return mAppSdkSandboxConnections.get(callingInfo); 337 } 338 getProcessName(CallingInfo callingInfo)339 private String getProcessName(CallingInfo callingInfo) 340 throws PackageManager.NameNotFoundException { 341 UserHandle userHandle = UserHandle.getUserHandleForUid(callingInfo.getUid()); 342 return mContext.getPackageManager() 343 .getApplicationInfoAsUser(callingInfo.getPackageName(), /*flags=*/ 0, userHandle) 344 .processName; 345 } 346 347 // Represents the connection to an SDK sandbox service. 348 private static class SdkSandboxConnection { 349 350 private final Object mLock = new Object(); 351 352 @GuardedBy("mLock") 353 @SandboxStatus 354 private int mStatus = CREATE_PENDING; 355 356 // The connection used to bind and unbind from the SDK sandbox service. 357 private final ServiceConnection mServiceConnection; 358 359 // The binder returned by the SDK sandbox service on connection. 360 @GuardedBy("mLock") 361 @Nullable 362 private ISdkSandboxService mSdkSandboxService = null; 363 364 // Set to true when requested to bind to the SDK sandbox service. It is reset back to false 365 // when unbinding the sandbox service. 366 @GuardedBy("mLock") 367 public boolean isBound = true; 368 369 private final String mSandboxProcessName; 370 SdkSandboxConnection(ServiceConnection serviceConnection, String sandboxProcessName)371 SdkSandboxConnection(ServiceConnection serviceConnection, String sandboxProcessName) { 372 mServiceConnection = serviceConnection; 373 mSandboxProcessName = sandboxProcessName; 374 } 375 376 @SandboxStatus getStatus()377 public int getStatus() { 378 synchronized (mLock) { 379 return mStatus; 380 } 381 } 382 onUnbind()383 public void onUnbind() { 384 synchronized (mLock) { 385 isBound = false; 386 } 387 } 388 onServiceConnected(ISdkSandboxService service)389 public void onServiceConnected(ISdkSandboxService service) { 390 synchronized (mLock) { 391 mStatus = CREATED; 392 mSdkSandboxService = service; 393 } 394 } 395 onServiceDisconnected()396 public void onServiceDisconnected() { 397 synchronized (mLock) { 398 mSdkSandboxService = null; 399 } 400 } 401 onSdkSandboxDeath()402 public void onSdkSandboxDeath() { 403 synchronized (mLock) { 404 // For U+, the sandbox does not restart after dying. 405 if (SdkLevel.isAtLeastU()) { 406 mStatus = NON_EXISTENT; 407 return; 408 } 409 410 if (isBound) { 411 // If the sandbox was bound at the time of death, the system will automatically 412 // restart it. 413 mStatus = CREATE_PENDING; 414 } else { 415 // If the sandbox was not bound at the time of death, the sandbox is dead for 416 // good. 417 mStatus = NON_EXISTENT; 418 } 419 } 420 } 421 422 @Nullable getSdkSandboxService()423 public ISdkSandboxService getSdkSandboxService() { 424 synchronized (mLock) { 425 return mSdkSandboxService; 426 } 427 } 428 getSandboxProcessName()429 public String getSandboxProcessName() { 430 return mSandboxProcessName; 431 } 432 getServiceConnection()433 public ServiceConnection getServiceConnection() { 434 return mServiceConnection; 435 } 436 isConnected()437 boolean isConnected() { 438 synchronized (mLock) { 439 return mSdkSandboxService != null; 440 } 441 } 442 } 443 } 444