blob: e615804e3f2b0cd4703b63d6e9a2fd9ff9350c53 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.supplementalprocess;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.supplementalprocess.IRemoteCodeCallback;
import android.supplementalprocess.ISupplementalProcessManager;
import android.supplementalprocess.SupplementalProcessManager;
import android.util.ArrayMap;
import android.util.Log;
import android.view.SurfaceControlViewHost;
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import com.android.supplemental.process.ISupplementalProcessManagerToSupplementalProcessCallback;
import com.android.supplemental.process.ISupplementalProcessService;
import com.android.supplemental.process.ISupplementalProcessToSupplementalProcessManagerCallback;
/**
* Implementation of Supplemental Process Manager service.
*
* @hide
*/
public class SupplementalProcessManagerService extends ISupplementalProcessManager.Stub {
private static final String TAG = "SupplementalProcessManager";
private final Context mContext;
@GuardedBy("mServiceProvider")
private final SupplementalProcessServiceProvider mServiceProvider;
// For communication between app<-ManagerService->RemoteCode for each codeToken
// TODO(b/208824602): Remove from this map when an app dies.
@GuardedBy("mAppAndRemoteCodeLinks")
private final ArrayMap<IBinder, AppAndRemoteCodeLink> mAppAndRemoteCodeLinks = new ArrayMap();
SupplementalProcessManagerService(Context context,
SupplementalProcessServiceProvider provider) {
mContext = context;
mServiceProvider = provider;
}
@Override
public void loadCode(String name, String version, Bundle params, IRemoteCodeCallback callback) {
final int callingUid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
loadCodeWithClearIdentity(callingUid, name, version, params, callback);
} finally {
Binder.restoreCallingIdentity(token);
}
}
private void loadCodeWithClearIdentity(int callingUid, String name, String version,
Bundle params, IRemoteCodeCallback callback) {
// Step 1: create identity for the code
// TODO(b/204991850): what if app tries to load same code twice? Identity should be
// unique for each <uid, code> pair.
final IBinder codeToken = new Binder();
// Use the code token to establish a link between the app<-Manager->RemoteCode
final AppAndRemoteCodeLink link = new AppAndRemoteCodeLink(codeToken, callback);
synchronized (mAppAndRemoteCodeLinks) {
mAppAndRemoteCodeLinks.put(codeToken, link);
}
// Step 2: fetch the installed code in device
final ApplicationInfo info = getCodeInfo(name);
if (info == null) {
String errorMsg = name + " not found for loading";
Log.w(TAG, errorMsg);
link.sendLoadCodeErrorToApp(SupplementalProcessManager.LOAD_CODE_NOT_FOUND, errorMsg);
return;
}
// TODO(b/204991850): ensure requested code is included in the AndroidManifest.xml
// Step 3: invoke CodeLoaderService to load the code
try {
synchronized (mServiceProvider) {
// TODO(b/204991850): we should merge bindService() and getService() together
mServiceProvider.bindService(callingUid, callback.asBinder());
ISupplementalProcessService service = mServiceProvider.getService(callingUid);
if (service == null) {
link.sendLoadCodeErrorToApp(SupplementalProcessManager.LOAD_CODE_INTERNAL_ERROR,
"Failed to bind to SupplementalProcess service");
return;
}
// TODO(b/208631926): Pass a meaningful value for codeProviderClassName
service.loadCode(codeToken, info, "", params, link);
}
} catch (RemoteException e) {
String errorMsg = "Failed to contact SupplementalProcessService";
Log.w(TAG, errorMsg, e);
link.sendLoadCodeErrorToApp(SupplementalProcessManager.LOAD_CODE_INTERNAL_ERROR,
errorMsg);
}
}
private ApplicationInfo getCodeInfo(String packageName) {
// TODO(b/204991850): code info should be version specific too
try {
// TODO(b/204991850): update this when PM provides better API for getting code info
return mContext.getPackageManager().getApplicationInfo(packageName, /*flags=*/0);
} catch (PackageManager.NameNotFoundException ignored) {
return null;
}
}
@Override
public void requestSurfacePackage(IBinder codeToken, IBinder hostToken,
int displayId, Bundle params) {
//TODO(b/204991850): verify that codeToken belongs to the callingUser
final long token = Binder.clearCallingIdentity();
try {
requestSurfacePackageWithClearIdentity(codeToken,
hostToken, displayId, params);
} finally {
Binder.restoreCallingIdentity(token);
}
}
private void requestSurfacePackageWithClearIdentity(IBinder codeToken,
IBinder hostToken, int displayId, Bundle params) {
synchronized (mAppAndRemoteCodeLinks) {
if (!mAppAndRemoteCodeLinks.containsKey(codeToken)) {
throw new SecurityException("codeToken is invalid");
}
final AppAndRemoteCodeLink link = mAppAndRemoteCodeLinks.get(codeToken);
link.requestSurfacePackageToCode(hostToken, displayId, params);
}
}
@Override
public void sendData(int id, Bundle params) {}
@Override
public void destroyCode(int id) {}
/**
* A callback object to establish a link between the app calling into manager service
* and the remote code being loaded in SupplementalProcess.
*
* Overview of communication:
* 1. App to ManagerService: App calls into this service via app context
* 2. ManagerService to App: {@link AppAndRemoteCodeLink} holds reference to
* {@link IRemoteCodeCallback} object which provides call back into the app.
* 3. RemoteCode to ManagerService: {@link AppAndRemoteCodeLink} extends
* {@link ISupplementalProcessToSupplementalProcessManagerCallback} interface. We
* pass on this object to {@link ISupplementalProcessService} so that remote code
* can call back into ManagerService
* 4. ManagerService to RemoteCode: When cod is loaded for the first time and remote
* code calls back with successful result, it also sends reference to
* {@link ISupplementalProcessManagerToSupplementalProcessCallback} callback object.
* ManagerService uses this to callback into the remote code.
*
* We maintain a link for each unique {app, remoteCode} pair, which is identified with
* {@code codeToken}.
*/
private class AppAndRemoteCodeLink extends
ISupplementalProcessToSupplementalProcessManagerCallback.Stub {
// The codeToken for which this channel has been created
private final IBinder mCodeToken;
private final IRemoteCodeCallback mManagerToAppCallback;
@GuardedBy("this")
private ISupplementalProcessManagerToSupplementalProcessCallback mManagerToCodeCallback;
AppAndRemoteCodeLink(IBinder codeToken, IRemoteCodeCallback managerToAppCallback) {
mCodeToken = codeToken;
mManagerToAppCallback = managerToAppCallback;
}
@Override
public void onLoadCodeSuccess(Bundle params,
ISupplementalProcessManagerToSupplementalProcessCallback callback) {
// Keep reference to callback so that manager service can
// callback to remote code loaded.
synchronized (this) {
mManagerToCodeCallback = callback;
}
sendLoadCodeSuccessToApp(params);
}
@Override
public void onLoadCodeError(int errorCode, String errorMsg) {}
@Override
public void onSurfacePackageReady(SurfaceControlViewHost.SurfacePackage surfacePackage,
int surfacePackageId, Bundle params) {
sendSurfacePackageReadyToApp(surfacePackage, surfacePackageId, params);
}
private void sendLoadCodeSuccessToApp(Bundle params) {
try {
mManagerToAppCallback.onLoadCodeSuccess(mCodeToken, params);
} catch (RemoteException e) {
Log.w(TAG, "Failed to send onLoadCodeSuccess", e);
}
}
void sendLoadCodeErrorToApp(int errorCode, String errorMsg) {
try {
mManagerToAppCallback.onLoadCodeFailure(errorCode, errorMsg);
} catch (RemoteException e) {
Log.w(TAG, "Failed to send onLoadCodeFailure", e);
}
}
private void sendSurfacePackageReadyToApp(
SurfaceControlViewHost.SurfacePackage surfacePackage,
int surfacePackageId, Bundle params) {
try {
mManagerToAppCallback.onSurfacePackageReady(surfacePackage,
surfacePackageId, params);
} catch (RemoteException e) {
Log.w(TAG, "Failed to send onSurfacePackageReady callback", e);
}
}
void requestSurfacePackageToCode(IBinder hostToken, int displayId, Bundle params) {
try {
synchronized (this) {
mManagerToCodeCallback.onSurfacePackageRequested(hostToken, displayId, params);
}
} catch (RemoteException e) {
Log.w(TAG, "Failed to requestSurfacePackage", e);
sendLoadCodeErrorToApp(SupplementalProcessManager.LOAD_CODE_INTERNAL_ERROR,
"Failed to requestSurfacePackage" + e.getMessage());
}
}
}
/** @hide */
public static class Lifecycle extends SystemService {
public Lifecycle(Context context) {
super(context);
}
@Override
public void onStart() {
SupplementalProcessServiceProvider provider =
new SupplementalProcessServiceProviderImpl(getContext());
SupplementalProcessManagerService service =
new SupplementalProcessManagerService(getContext(), provider);
publishBinderService(Context.SUPPLEMENTAL_PROCESS_SERVICE, service);
Log.i(TAG, "SupplementalProcessManagerService started!");
}
}
}