diff --git a/core/api/current.txt b/core/api/current.txt index 3d00bda551bc9..1780404e5c342 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7128,7 +7128,7 @@ package android.app.admin { method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName); method @NonNull public java.util.List getInstalledCaCerts(@Nullable android.content.ComponentName); method @Nullable public java.util.List getKeepUninstalledPackages(@Nullable android.content.ComponentName); - method @NonNull public java.util.Set> getKeyPairGrants(@NonNull String); + method @NonNull public java.util.Map> getKeyPairGrants(@NonNull String); method public int getKeyguardDisabledFeatures(@Nullable android.content.ComponentName); method public int getLockTaskFeatures(@NonNull android.content.ComponentName); method @NonNull public String[] getLockTaskPackages(@NonNull android.content.ComponentName); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 843aa2ecb4925..65f10d4e5aec8 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -119,6 +119,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -6458,12 +6459,14 @@ public class DevicePolicyManager { * to a given KeyChain key. * * Key are granted on a per-UID basis, so if several apps share the same UID, granting access to - * one of them automatically grants it to others. This method returns a set of sets of package - * names, where each internal set contains all packages sharing the same UID. Grantee packages - * that don't share UID with other packages are represented by singleton sets. + * one of them automatically grants it to others. This method returns a map containing one entry + * per grantee UID. Entries have UIDs as keys and sets of corresponding package names as values. + * In particular, grantee packages that don't share UID with other packages are represented by + * entries having singleton sets as values. * * @param alias The alias of the key to grant access to. - * @return package names of apps that have access to a given key, grouped by UIDs + * @return apps that have access to a given key, arranged in a map from UID to sets of + * package names. * * @throws SecurityException if the caller is not a device owner, a profile owner or * delegated certificate chooser. @@ -6471,26 +6474,11 @@ public class DevicePolicyManager { * * @see #grantKeyPairToApp(ComponentName, String, String) */ - public @NonNull Set> getKeyPairGrants(@NonNull String alias) { + public @NonNull Map> getKeyPairGrants(@NonNull String alias) { throwIfParentInstance("getKeyPairGrants"); try { - // Set of sets is flattened into a null-separated list. - final List flattened = - mService.getKeyPairGrants(mContext.getPackageName(), alias); - final Set> result = new HashSet<>(); - Set pkgsForOneUid = new HashSet<>(); - for (final String pkg : flattened) { - if (pkg == null) { - result.add(pkgsForOneUid); - pkgsForOneUid = new HashSet<>(); - } else { - pkgsForOneUid.add(pkg); - } - } - if (!pkgsForOneUid.isEmpty()) { - result.add(pkgsForOneUid); - } - return result; + // The result is wrapped into intermediate parcelable representation. + return mService.getKeyPairGrants(mContext.getPackageName(), alias).getPackagesByUid(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 8a8c69c10379a..b5820e861ba6a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -20,6 +20,7 @@ package android.app.admin; import android.app.admin.NetworkEvent; import android.app.IApplicationThread; import android.app.IServiceConnection; +import android.app.admin.ParcelableGranteeMap; import android.app.admin.StartInstallingUpdateCallback; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; @@ -485,7 +486,7 @@ interface IDevicePolicyManager { boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, long start, long end, boolean allDay, int flags); boolean setKeyGrantForApp(in ComponentName admin, String callerPackage, String alias, String packageName, boolean hasGrant); - List getKeyPairGrants(in String callerPackage, in String alias); + ParcelableGranteeMap getKeyPairGrants(in String callerPackage, in String alias); boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant); boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias); diff --git a/core/java/android/app/admin/ParcelableGranteeMap.aidl b/core/java/android/app/admin/ParcelableGranteeMap.aidl new file mode 100644 index 0000000000000..cd15b4901472f --- /dev/null +++ b/core/java/android/app/admin/ParcelableGranteeMap.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.app.admin; + +parcelable ParcelableGranteeMap; \ No newline at end of file diff --git a/core/java/android/app/admin/ParcelableGranteeMap.java b/core/java/android/app/admin/ParcelableGranteeMap.java new file mode 100644 index 0000000000000..be348ad067c91 --- /dev/null +++ b/core/java/android/app/admin/ParcelableGranteeMap.java @@ -0,0 +1,85 @@ +/* + * 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 android.app.admin; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.ArraySet; + +import java.util.Map; +import java.util.Set; + +/** + * Class for marshalling keypair grantees for a given KeyChain key via Binder. + * + * @hide + */ +public class ParcelableGranteeMap implements Parcelable { + + private final Map> mPackagesByUid; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPackagesByUid.size()); + for (final Map.Entry> uidEntry : mPackagesByUid.entrySet()) { + dest.writeInt(uidEntry.getKey()); + dest.writeStringArray(uidEntry.getValue().toArray(new String[0])); + } + } + + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public ParcelableGranteeMap createFromParcel(Parcel source) { + final Map> packagesByUid = new ArrayMap<>(); + final int numUids = source.readInt(); + for (int i = 0; i < numUids; i++) { + final int uid = source.readInt(); + final String[] pkgs = source.readStringArray(); + packagesByUid.put(uid, new ArraySet<>(pkgs)); + } + return new ParcelableGranteeMap(packagesByUid); + } + + @Override + public ParcelableGranteeMap[] newArray(int size) { + return new ParcelableGranteeMap[size]; + } + }; + + /** + * Creates an instance holding a reference (not a copy) to the given map. + */ + public ParcelableGranteeMap(@NonNull Map> packagesByUid) { + mPackagesByUid = packagesByUid; + } + + /** + * Returns a reference (not a copy) to the stored map. + */ + @NonNull + public Map> getPackagesByUid() { + return mPackagesByUid; + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 807edca939d7b..357346c86a92f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -173,6 +173,7 @@ import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.FullyManagedDeviceProvisioningParams; import android.app.admin.ManagedProfileProvisioningParams; import android.app.admin.NetworkEvent; +import android.app.admin.ParcelableGranteeMap; import android.app.admin.PasswordMetrics; import android.app.admin.PasswordPolicy; import android.app.admin.SecurityLog; @@ -278,6 +279,7 @@ import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.text.format.DateUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.IndentingPrintWriter; @@ -5618,41 +5620,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public List getKeyPairGrants(String callerPackage, String alias) { + public ParcelableGranteeMap getKeyPairGrants(String callerPackage, String alias) { final CallerIdentity caller = getCallerIdentity(callerPackage); Preconditions.checkCallAuthorization(canManageCertificates(caller)); - return mInjector.binderWithCleanCallingIdentity(() -> { + final ArrayMap> result = new ArrayMap<>(); + mInjector.binderWithCleanCallingIdentity(() -> { try (KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, caller.getUserHandle())) { - final List result = new ArrayList<>(); final int[] granteeUids = keyChainConnection.getService().getGrants(alias); final PackageManager pm = mInjector.getPackageManager(caller.getUserId()); - // TODO: Return Set> when AIDL supports it: b/136048684 - // Public API returns a set of sets, where each internal set contains all package - // names corresponding to the same UID. For now a set of sets is marshalled as a - // null-separated list. for (final int uid : granteeUids) { final String[] packages = pm.getPackagesForUid(uid); if (packages == null) { Slog.wtf(LOG_TAG, "No packages found for uid " + uid); continue; } - if (!result.isEmpty()) { - result.add(null); - } - result.addAll(Arrays.asList(packages)); + result.put(uid, new ArraySet(packages)); } - return result; } catch (RemoteException e) { Slog.e(LOG_TAG, "Querying keypair grants", e); } catch (InterruptedException e) { Slog.w(LOG_TAG, "Interrupted while querying keypair grants", e); Thread.currentThread().interrupt(); } - return Collections.emptyList(); }); + return new ParcelableGranteeMap(result); } /**