Merge "[incremental] data loader manager interface"

This commit is contained in:
Songchun Fan
2019-12-05 20:55:50 +00:00
committed by Android (Google) Code Review
12 changed files with 580 additions and 45 deletions

View File

@@ -803,10 +803,9 @@ filegroup {
}
filegroup {
name: "incremental_data_loader_aidl",
name: "dataloader_aidl",
srcs: [
"core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl",
"core/java/android/service/incremental/IIncrementalDataLoaderService.aidl",
"core/java/android/content/pm/IDataLoaderStatusListener.aidl",
],
path: "core/java",
}
@@ -815,7 +814,27 @@ aidl_interface {
name: "libincremental_aidl",
srcs: [
":incremental_aidl",
":incremental_data_loader_aidl",
],
imports: [
"libdataloader_aidl",
],
backend: {
java: {
sdk_version: "28",
},
cpp: {
enabled: true,
},
ndk: {
enabled: true,
},
},
}
aidl_interface {
name: "libdataloader_aidl",
srcs: [
":dataloader_aidl",
],
backend: {
java: {

View File

@@ -3427,7 +3427,6 @@ public abstract class Context {
//@hide: TIME_DETECTOR_SERVICE,
//@hide: TIME_ZONE_DETECTOR_SERVICE,
PERMISSION_SERVICE,
INCREMENTAL_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -4969,6 +4968,13 @@ public abstract class Context {
@SystemApi
public static final String APP_INTEGRITY_SERVICE = "app_integrity";
/**
* Use with {@link #getSystemService(String)} to retrieve an
* {@link android.content.pm.DataLoaderManager}.
* @hide
*/
public static final String DATA_LOADER_MANAGER_SERVICE = "dataloadermanager";
/**
* Use with {@link #getSystemService(String)} to retrieve an
* {@link android.os.incremental.IncrementalManager}.

View File

@@ -4052,6 +4052,13 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
/**
* Used for looking up a Data Loader Service providers.
* @hide
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
/**
* An int extra used with {@link #ACTION_SERVICE_STATE} which indicates voice registration
* state.

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2019 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 android.content.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.RemoteException;
/**
* Data loader manager takes care of data loaders of different packages. It provides methods to
* initialize a data loader binder service (binding and creating it), to return a binder of the data
* loader binder service and to destroy a data loader binder service.
* @see com.android.server.pm.DataLoaderManagerService
* @hide
*/
public class DataLoaderManager {
private static final String TAG = "DataLoaderManager";
private final IDataLoaderManager mService;
public DataLoaderManager(IDataLoaderManager service) {
mService = service;
}
/**
* Finds a data loader binder service and binds to it. This requires PackageManager.
*
* @param dataLoaderId ID for the new data loader binder service.
* @param params Bundle that contains parameters to configure the data loader service.
* Must contain:
* key: "packageName", value: String, package name of data loader service
* package;
* key: "extras", value: Bundle, client-specific data structures
*
* @param listener Callback for the data loader service to report status back to the
* caller.
* @return false if 1) target ID collides with a data loader that is already bound to data
* loader manager; 2) package name is not specified; 3) fails to find data loader package;
* or 4) fails to bind to the specified data loader service, otherwise return true.
*/
public boolean initializeDataLoader(int dataLoaderId, @NonNull Bundle params,
@NonNull IDataLoaderStatusListener listener) {
try {
return mService.initializeDataLoader(dataLoaderId, params, listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns a binder interface of the data loader binder service, given its ID.
*/
@Nullable
public IDataLoader getDataLoader(int dataLoaderId) {
try {
return mService.getDataLoader(dataLoaderId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Destroys the data loader binder service and removes it from data loader manager service.
*/
@Nullable
public void destroyDataLoader(int dataLoaderId) {
try {
mService.destroyDataLoader(dataLoaderId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 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 android.content.pm;
import android.os.Bundle;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.InstallationFile;
import java.util.List;
/**
* TODO: update with new APIs
* @hide
*/
oneway interface IDataLoader {
void create(int id, in Bundle params, IDataLoaderStatusListener listener);
void start(in List<InstallationFile> fileInfos);
void stop();
void destroy();
void onFileCreated(long inode, in byte[] metadata);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2019 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 android.content.pm;
import android.os.Bundle;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderStatusListener;
import java.util.List;
/** @hide */
interface IDataLoaderManager {
boolean initializeDataLoader(int id, in Bundle params, IDataLoaderStatusListener listener);
IDataLoader getDataLoader(int dataLoaderId);
void destroyDataLoader(int dataLoaderId);
}

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
package android.service.incremental;
package android.content.pm;
/**
* Callbacks from DataLoaderService to IncrementalService to report data loader status.
* Callbacks from a data loader binder service to report data loader status.
* @hide
*/
oneway interface IIncrementalDataLoaderStatusListener {
oneway interface IDataLoaderStatusListener {
/** Data loader status */
const int DATA_LOADER_READY = 0;
const int DATA_LOADER_NOT_READY = 1;
@@ -31,6 +31,6 @@ oneway interface IIncrementalDataLoaderStatusListener {
const int DATA_LOADER_CONNECTION_OK = 6;
/** Data loader status callback */
void onStatusChanged(in int storageId, in int status);
void onStatusChanged(in int dataLoaderId, in int status);
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2019 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 android.content.pm;
/**
* Describes a file which is part of a package installation.
*/
parcelable InstallationFile;

View File

@@ -0,0 +1,118 @@
/*
* Copyright (C) 2019 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 android.content.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Defines the properties of a file in an installation session.
* TODO(b/136132412): update with new APIs.
*
* @hide
*/
public final class InstallationFile implements Parcelable {
public static final int FILE_TYPE_UNKNOWN = -1;
public static final int FILE_TYPE_APK = 0;
public static final int FILE_TYPE_LIB = 1;
public static final int FILE_TYPE_OBB = 2;
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FILE_TYPE_"}, value = {
FILE_TYPE_APK,
FILE_TYPE_LIB,
FILE_TYPE_OBB,
})
public @interface FileType {
}
private String mFileName;
private @FileType int mFileType;
private long mFileSize;
private byte[] mMetadata;
public InstallationFile(@NonNull String fileName, long fileSize,
@Nullable byte[] metadata) {
mFileName = fileName;
mFileSize = fileSize;
mMetadata = metadata;
if (fileName.toLowerCase().endsWith(".apk")) {
mFileType = FILE_TYPE_APK;
} else if (fileName.toLowerCase().endsWith(".obb")) {
mFileType = FILE_TYPE_OBB;
} else if (fileName.toLowerCase().endsWith(".so") && fileName.toLowerCase().startsWith(
"lib/")) {
mFileType = FILE_TYPE_LIB;
} else {
mFileType = FILE_TYPE_UNKNOWN;
}
}
public @FileType int getFileType() {
return mFileType;
}
public @NonNull String getName() {
return mFileName;
}
public long getSize() {
return mFileSize;
}
public @Nullable byte[] getMetadata() {
return mMetadata;
}
private InstallationFile(Parcel source) {
mFileName = source.readString();
mFileType = source.readInt();
mFileSize = source.readLong();
mMetadata = source.createByteArray();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mFileName);
dest.writeInt(mFileType);
dest.writeLong(mFileSize);
dest.writeByteArray(mMetadata);
}
public static final @NonNull Creator<InstallationFile> CREATOR =
new Creator<InstallationFile>() {
public InstallationFile createFromParcel(Parcel source) {
return new InstallationFile(source);
}
public InstallationFile[] newArray(int size) {
return new InstallationFile[size];
}
};
}

View File

@@ -18,7 +18,7 @@ package android.os.incremental;
import android.os.incremental.IncrementalFileSystemControlParcel;
import android.os.incremental.IncrementalDataLoaderParamsParcel;
import android.service.incremental.IIncrementalDataLoaderStatusListener;
import android.content.pm.IDataLoaderStatusListener;
/**
* Binder service to receive calls from native Incremental Service and handle Java tasks such as
@@ -29,7 +29,7 @@ interface IIncrementalServiceProxy {
boolean prepareDataLoader(int mountId,
in IncrementalFileSystemControlParcel control,
in IncrementalDataLoaderParamsParcel params,
in IIncrementalDataLoaderStatusListener listener);
in IDataLoaderStatusListener listener);
boolean startDataLoader(int mountId);
void showHealthBlockedUI(int mountId);
void destroyDataLoader(int mountId);

View File

@@ -1,34 +0,0 @@
/*
* Copyright (C) 2019 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 android.service.incremental;
import android.os.incremental.IncrementalDataLoaderParamsParcel;
import android.os.incremental.IncrementalFileSystemControlParcel;
import android.service.incremental.IIncrementalDataLoaderStatusListener;
/** @hide */
oneway interface IIncrementalDataLoaderService {
void createDataLoader(in int storageId,
in IncrementalFileSystemControlParcel control,
in IncrementalDataLoaderParamsParcel params,
in IIncrementalDataLoaderStatusListener listener,
in boolean start);
void startDataLoader(in int storageId);
void stopDataLoader(in int storageId);
void destroyDataLoader(in int storageId);
void onFileCreated(in int storageId, in long inode, in byte[] metadata);
}

View File

@@ -0,0 +1,246 @@
/*
* Copyright (C) 2019 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.pm;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderManager;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import java.util.List;
/**
* Data loader manager service manages data loader binder services.
*
* @hide
*/
public class DataLoaderManagerService extends SystemService {
private static final String TAG = "DataLoaderManager";
private final Context mContext;
private final DataLoaderManagerBinderService mBinderService;
private final Object mLock = new Object();
@GuardedBy("mLock")
private SparseArray<DataLoaderServiceConnection> mServiceConnections;
public DataLoaderManagerService(Context context) {
super(context);
mContext = context;
mBinderService = new DataLoaderManagerBinderService();
}
@Override
public void onStart() {
publishBinderService(Context.DATA_LOADER_MANAGER_SERVICE, mBinderService);
}
final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub {
@Override
public boolean initializeDataLoader(int dataLoaderId, Bundle params,
IDataLoaderStatusListener listener) {
synchronized (mLock) {
if (mServiceConnections == null) {
mServiceConnections = new SparseArray<>();
}
if (mServiceConnections.get(dataLoaderId) != null) {
Slog.e(TAG, "Data loader of ID=" + dataLoaderId + " already exists.");
return false;
}
}
CharSequence packageNameSeq = params.getCharSequence("packageName");
if (packageNameSeq == null) {
Slog.e(TAG, "Must specify package name.");
return false;
}
String packageName = packageNameSeq.toString();
ComponentName dataLoaderComponent = getDataLoaderServiceName(packageName);
if (dataLoaderComponent == null) {
return false;
}
// Binds to the specific data loader service
DataLoaderServiceConnection connection =
new DataLoaderServiceConnection(dataLoaderId, params, listener);
Intent intent = new Intent();
intent.setComponent(dataLoaderComponent);
if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
UserHandle.of(UserHandle.getCallingUserId()))) {
Slog.e(TAG, "Failed to bind to data loader binder service.");
mContext.unbindService(connection);
return false;
}
return true;
}
/**
* Find the ComponentName of the data loader service provider, given its package name.
*
* @param packageName the package name of the provider.
* @return ComponentName of the data loader service provider. Null if provider not found.
*/
private @Nullable ComponentName getDataLoaderServiceName(String packageName) {
final PackageManager pm = mContext.getPackageManager();
if (pm == null) {
Slog.e(TAG, "PackageManager is not available.");
return null;
}
Intent intent = new Intent(Intent.ACTION_LOAD_DATA);
intent.setPackage(packageName);
List<ResolveInfo> services =
pm.queryIntentServicesAsUser(intent, 0, UserHandle.getCallingUserId());
if (services == null || services.isEmpty()) {
Slog.e(TAG,
"Failed to find data loader service provider in package " + packageName);
return null;
}
// TODO(b/136132412): better way to enable privileged data loaders in tests
boolean checkLoader =
android.os.SystemProperties.getBoolean("incremental.check_loader", false);
int numServices = services.size();
for (int i = 0; i < numServices; i++) {
ResolveInfo ri = services.get(i);
ComponentName componentName = new ComponentName(
ri.serviceInfo.packageName, ri.serviceInfo.name);
// There should only be one matching provider inside the given package.
// If there's more than one, return the first one found.
try {
ApplicationInfo ai = pm.getApplicationInfo(componentName.getPackageName(), 0);
if (checkLoader && !ai.isPrivilegedApp()) {
Slog.w(TAG,
"Data loader: " + componentName.getPackageName()
+ " is not a privileged app, skipping.");
continue;
}
return componentName;
} catch (PackageManager.NameNotFoundException ex) {
Slog.w(TAG,
"Privileged data loader: " + componentName.getPackageName()
+ " not found, skipping.");
}
}
Slog.e(TAG, "Didn't find any matching data loader service provider.");
return null;
}
/**
* Returns the binder object of a data loader, specified by its ID.
*/
@Override
public @Nullable IDataLoader getDataLoader(int dataLoaderId) {
synchronized (mLock) {
if (mServiceConnections == null) {
return null;
}
DataLoaderServiceConnection serviceConnection = mServiceConnections.get(
dataLoaderId, null);
if (serviceConnection == null) {
return null;
}
return serviceConnection.getDataLoader();
}
}
/**
* Destroys a data loader binder service, specified by its ID.
*/
@Override
public void destroyDataLoader(int dataLoaderId) {
synchronized (mLock) {
if (mServiceConnections == null) {
return;
}
DataLoaderServiceConnection serviceConnection = mServiceConnections.get(
dataLoaderId, null);
if (serviceConnection == null) {
return;
}
serviceConnection.destroy();
}
}
}
class DataLoaderServiceConnection implements ServiceConnection {
final int mId;
final Bundle mParams;
final IDataLoaderStatusListener mListener;
IDataLoader mDataLoader;
DataLoaderServiceConnection(int id, Bundle params, IDataLoaderStatusListener listener) {
mId = id;
mParams = params;
mListener = listener;
mDataLoader = null;
}
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
mDataLoader = IDataLoader.Stub.asInterface(service);
synchronized (mLock) {
mServiceConnections.append(mId, this);
}
try {
mDataLoader.create(mId, mParams, mListener);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to create data loader service.", e);
}
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
remove();
}
IDataLoader getDataLoader() {
return mDataLoader;
}
void destroy() {
try {
mDataLoader.destroy();
} catch (RemoteException ignored) {
}
mContext.unbindService(this);
}
private void remove() {
synchronized (mLock) {
mServiceConnections.remove(mId);
if (mServiceConnections.size() == 0) {
mServiceConnections = null;
}
}
mParams.clear();
}
}
}