diff options
9 files changed, 411 insertions, 0 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index f9d505c358ca..7fb4821a883f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13806,6 +13806,7 @@ package android.content.pm { public final class SharedLibraryInfo implements android.os.Parcelable { method public int describeContents(); + method @FlaggedApi("android.content.pm.sdk_dependency_installer") @NonNull public java.util.List<java.lang.String> getCertDigests(); method @NonNull public android.content.pm.VersionedPackage getDeclaringPackage(); method @NonNull public java.util.List<android.content.pm.VersionedPackage> getDependentPackages(); method @IntRange(from=0xffffffff) public long getLongVersion(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index e4f158202125..61f9b892c4dd 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4594,6 +4594,24 @@ package android.content.pm { } +package android.content.pm.dependencyinstaller { + + @FlaggedApi("android.content.pm.sdk_dependency_installer") public final class DependencyInstallerCallback implements android.os.Parcelable { + method public int describeContents(); + method public void onAllDependenciesResolved(@NonNull int[]); + method public void onFailureToResolveAllDependencies(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.dependencyinstaller.DependencyInstallerCallback> CREATOR; + } + + @FlaggedApi("android.content.pm.sdk_dependency_installer") public abstract class DependencyInstallerService extends android.app.Service { + ctor public DependencyInstallerService(); + method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); + method public abstract void onDependenciesRequired(@NonNull java.util.List<android.content.pm.SharedLibraryInfo>, @NonNull android.content.pm.dependencyinstaller.DependencyInstallerCallback); + } + +} + package android.content.pm.dex { public class ArtManager { diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index f7191e605fb8..5dfec9809f6e 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -105,6 +105,8 @@ public final class SharedLibraryInfo implements Parcelable { private final List<VersionedPackage> mOptionalDependentPackages; private List<SharedLibraryInfo> mDependencies; + private final List<String> mCertDigests; + /** * Creates a new instance. * @@ -134,6 +136,7 @@ public final class SharedLibraryInfo implements Parcelable { mDependencies = dependencies; mIsNative = isNative; mOptionalDependentPackages = null; + mCertDigests = null; } /** @@ -165,6 +168,7 @@ public final class SharedLibraryInfo implements Parcelable { mDeclaringPackage = declaringPackage; mDependencies = dependencies; mIsNative = isNative; + mCertDigests = null; var allDependents = allDependentPackages.first; var usesLibOptional = allDependentPackages.second; @@ -206,6 +210,7 @@ public final class SharedLibraryInfo implements Parcelable { mIsNative = parcel.readBoolean(); mOptionalDependentPackages = parcel.readParcelableList(new ArrayList<>(), VersionedPackage.class.getClassLoader(), VersionedPackage.class); + mCertDigests = parcel.createStringArrayList(); } /** @@ -214,6 +219,29 @@ public final class SharedLibraryInfo implements Parcelable { * @param versionMajor */ public SharedLibraryInfo(String name, long versionMajor, int type) { + //TODO: change to this(name, versionMajor, type, /* certDigest= */null); after flag removal + mPath = null; + mPackageName = null; + mName = name; + mVersion = versionMajor; + mType = type; + mDeclaringPackage = null; + mDependentPackages = null; + mDependencies = null; + mIsNative = false; + mOptionalDependentPackages = null; + mCertDigests = null; + } + + /** + * @hide + * @param name The lib name. + * @param versionMajor The lib major version. + * @param type The type of shared library. + * @param certDigests The list of certificate digests for this shared library. + */ + @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) + public SharedLibraryInfo(String name, long versionMajor, int type, List<String> certDigests) { mPath = null; mPackageName = null; mName = name; @@ -224,6 +252,7 @@ public final class SharedLibraryInfo implements Parcelable { mDependencies = null; mIsNative = false; mOptionalDependentPackages = null; + mCertDigests = certDigests; } /** @@ -433,6 +462,19 @@ public final class SharedLibraryInfo implements Parcelable { return mOptionalDependentPackages; } + /** + * Gets the list of certificate digests for the shared library. + * + * @return The list of certificate digests + */ + @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) + public @NonNull List<String> getCertDigests() { + if (mCertDigests == null) { + return Collections.emptyList(); + } + return mCertDigests; + } + @Override public int describeContents() { return 0; @@ -463,6 +505,7 @@ public final class SharedLibraryInfo implements Parcelable { parcel.writeTypedList(mDependencies); parcel.writeBoolean(mIsNative); parcel.writeParcelableList(mOptionalDependentPackages, flags); + parcel.writeStringList(mCertDigests); } private static String typeToString(int type) { diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl new file mode 100644 index 000000000000..06fcabcf55e9 --- /dev/null +++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2024 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.dependencyinstaller; + +parcelable DependencyInstallerCallback;
\ No newline at end of file diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java new file mode 100644 index 000000000000..ba089f7fd33e --- /dev/null +++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2024 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.dependencyinstaller; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.pm.Flags; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; + +/** + * Callbacks for {@link DependencyInstallerService}. The implementation of + * DependencyInstallerService uses this interface to indicate completion of the session creation + * request given by the system server. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) +public final class DependencyInstallerCallback implements Parcelable { + private final IBinder mBinder; + private final IDependencyInstallerCallback mCallback; + + /** @hide */ + public DependencyInstallerCallback(IBinder binder) { + mBinder = binder; + mCallback = IDependencyInstallerCallback.Stub.asInterface(binder); + } + + private DependencyInstallerCallback(Parcel in) { + mBinder = in.readStrongBinder(); + mCallback = IDependencyInstallerCallback.Stub.asInterface(mBinder); + } + + /** + * Callback to indicate that all the requested dependencies have been resolved and their + * sessions created. See {@link DependencyInstallerService#onDependenciesRequired}. + * + * @param sessionIds the install session IDs for all requested dependencies + */ + public void onAllDependenciesResolved(@NonNull int[] sessionIds) { + try { + mCallback.onAllDependenciesResolved(sessionIds); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Callback to indicate that at least one of the required dependencies could not be resolved + * and any associated sessions have been abandoned. + */ + public void onFailureToResolveAllDependencies() { + try { + mCallback.onFailureToResolveAllDependencies(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeStrongBinder(mBinder); + } + + public static final @NonNull Creator<DependencyInstallerCallback> CREATOR = + new Creator<>() { + @Override + public DependencyInstallerCallback createFromParcel(Parcel in) { + return new DependencyInstallerCallback(in); + } + + @Override + public DependencyInstallerCallback[] newArray(int size) { + return new DependencyInstallerCallback[size]; + } + }; +} diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java new file mode 100644 index 000000000000..11864150e072 --- /dev/null +++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerService.java @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2024 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.dependencyinstaller; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.content.pm.Flags; +import android.content.pm.SharedLibraryInfo; +import android.os.IBinder; + +import java.util.List; + +/** + * Service that needs to be implemented by the holder of the DependencyInstaller role. This service + * will be invoked by the system during application installations if it depends on + * {@link android.content.pm.SharedLibraryInfo#TYPE_STATIC} or + * {@link android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE} and those dependencies aren't + * already installed. + * <p> + * Below is an example manifest registration for a {@code DependencyInstallerService}. + * <pre> + * {@code + * <service android:name=".ExampleDependencyInstallerService" + * android:permission="android.permission.BIND_DEPENDENCY_INSTALLER" > + * ... + * <intent-filter> + * <action android:name="android.content.pm.action.INSTALL_DEPENDENCY" /> + * </intent-filter> + * </service> + * } + * </pre> + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) +public abstract class DependencyInstallerService extends Service { + + private IDependencyInstallerService mBinder; + + @Override + public final @NonNull IBinder onBind(@Nullable Intent intent) { + if (mBinder == null) { + mBinder = new IDependencyInstallerService.Stub() { + @Override + public void onDependenciesRequired(List<SharedLibraryInfo> neededLibraries, + DependencyInstallerCallback callback) { + DependencyInstallerService.this.onDependenciesRequired(neededLibraries, + callback); + } + }; + } + return mBinder.asBinder(); + } + + /** + * Notify the holder of the DependencyInstaller role of the missing dependencies required for + * the completion of an active install session. + * + * @param neededLibraries the list of shared library dependencies needed to be obtained and + * installed. + */ + public abstract void onDependenciesRequired(@NonNull List<SharedLibraryInfo> neededLibraries, + @NonNull DependencyInstallerCallback callback); +} diff --git a/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl new file mode 100644 index 000000000000..92d1d9e118e6 --- /dev/null +++ b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2024 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.dependencyinstaller; + +import java.util.List; + +/** +* Callbacks for Dependency Installer. The app side invokes on this interface to indicate +* completion of the async dependency install request given by the system server. +* +* {@hide} +*/ +oneway interface IDependencyInstallerCallback { + /** + * Callback to indicate that all the requested dependencies have been resolved and have been + * committed for installation. See {@link DependencyInstallerService#onDependenciesRequired}. + * + * @param sessionIds the install session IDs for all requested dependencies + */ + void onAllDependenciesResolved(in int[] sessionIds); + + /** + * Callback to indicate that at least one of the required dependencies could not be resolved + * and any associated sessions have been abandoned. + */ + void onFailureToResolveAllDependencies(); +}
\ No newline at end of file diff --git a/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl new file mode 100644 index 000000000000..94f5bf45ca9c --- /dev/null +++ b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerService.aidl @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2024 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.dependencyinstaller; + +import android.content.pm.dependencyinstaller.DependencyInstallerCallback; +import android.content.pm.SharedLibraryInfo; +import java.util.List; + +/** +* Interface used to communicate with the application code that holds the Dependency Installer role. +* {@hide} +*/ +oneway interface IDependencyInstallerService { + /** + * Notify dependency installer of the required dependencies to complete the current install + * session. + * + * @param neededLibraries the list of shared library dependencies needed to be obtained and + * installed. + */ + void onDependenciesRequired(in List<SharedLibraryInfo> neededLibraries, + in DependencyInstallerCallback callback); + }
\ No newline at end of file diff --git a/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java b/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java new file mode 100644 index 000000000000..df5cd4e95418 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/SharedLibraryInfoTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 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 static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; +import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import com.google.common.collect.ImmutableList; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.List; + +@Presubmit +@RunWith(JUnit4.class) +public final class SharedLibraryInfoTest { + + @Rule + public final CheckFlagsRule checkFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + + private static final String LIBRARY_NAME = "name"; + private static final long VERSION_MAJOR = 1L; + private static final List<String> CERT_DIGESTS = ImmutableList.of("digest1", "digest2"); + + @Test + @RequiresFlagsEnabled(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) + public void sharedLibraryInfo_serializedAndDeserialized_retainsCertDigestInfo() { + SharedLibraryInfo toParcel = new SharedLibraryInfo(LIBRARY_NAME, VERSION_MAJOR, + SharedLibraryInfo.TYPE_SDK_PACKAGE, CERT_DIGESTS); + + SharedLibraryInfo fromParcel = parcelAndUnparcel(toParcel); + + assertThat(fromParcel.getCertDigests().size()).isEqualTo(toParcel.getCertDigests().size()); + assertThat(fromParcel.getCertDigests().get(0)).isEqualTo(toParcel.getCertDigests().get(0)); + assertThat(fromParcel.getCertDigests().get(1)).isEqualTo(toParcel.getCertDigests().get(1)); + } + + private SharedLibraryInfo parcelAndUnparcel(SharedLibraryInfo sharedLibraryInfo) { + Parcel parcel = Parcel.obtain(); + parcel.setDataPosition(0); + sharedLibraryInfo.writeToParcel(parcel, /* flags= */0); + + parcel.setDataPosition(0); + return SharedLibraryInfo.CREATOR.createFromParcel(parcel); + } +} |