From 0da6fe5759436a48555d21a83a8e12e1a2e579f0 Mon Sep 17 00:00:00 2001 From: Eugene Susla Date: Thu, 7 Jan 2021 14:05:39 -0800 Subject: [PATCH] Replace CDM profiles hardcoded strings with real values Test: manual Bug: 165951651 Change-Id: Ie3771df49dca43b9c91f64b32a6b56b211e754ad --- .../android/companion/AssociationRequest.java | 55 ++++++++++++++++--- .../PermissionControllerManager.java | 28 ++++++++++ .../PermissionControllerService.java | 11 ++-- .../res/values/strings.xml | 5 +- .../DeviceChooserActivity.java | 34 +++++++++--- .../CompanionDeviceManagerService.java | 46 +++++++++++++--- 6 files changed, 148 insertions(+), 31 deletions(-) diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 57b0828b334c5..083ce968d66fe 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -96,11 +96,25 @@ public final class AssociationRequest implements Parcelable { */ private @Nullable String mCallingPackage = null; + /** + * The user-readable description of the device profile's privileges. + * + * Populated by the system. + * + * @hide + */ + private @Nullable String mDeviceProfilePrivilegesDescription = null; + /** @hide */ public void setCallingPackage(@NonNull String pkg) { mCallingPackage = pkg; } + /** @hide */ + public void setDeviceProfilePrivilegesDescription(@NonNull String desc) { + mDeviceProfilePrivilegesDescription = desc; + } + private void onConstructed() { if (mDeviceProfile != null && !Objects.equals(mDeviceProfile, DEVICE_PROFILE_WATCH)) { @@ -178,14 +192,14 @@ public final class AssociationRequest implements Parcelable { markUsed(); return new AssociationRequest( mSingleDevice, emptyIfNull(mDeviceFilters), - mDeviceProfile, null); + mDeviceProfile, null, null); } } - // Code below generated by codegen v1.0.20. + // Code below generated by codegen v1.0.22. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -215,6 +229,10 @@ public final class AssociationRequest implements Parcelable { * The app package making the request. * * Populated by the system. + * @param deviceProfilePrivilegesDescription + * The user-readable description of the device profile's privileges. + * + * Populated by the system. * @hide */ @DataClass.Generated.Member @@ -222,7 +240,8 @@ public final class AssociationRequest implements Parcelable { boolean singleDevice, @NonNull List> deviceFilters, @Nullable @DeviceProfile String deviceProfile, - @Nullable String callingPackage) { + @Nullable String callingPackage, + @Nullable String deviceProfilePrivilegesDescription) { this.mSingleDevice = singleDevice; this.mDeviceFilters = deviceFilters; com.android.internal.util.AnnotationValidations.validate( @@ -231,6 +250,7 @@ public final class AssociationRequest implements Parcelable { com.android.internal.util.AnnotationValidations.validate( DeviceProfile.class, null, mDeviceProfile); this.mCallingPackage = callingPackage; + this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; onConstructed(); } @@ -257,6 +277,18 @@ public final class AssociationRequest implements Parcelable { return mCallingPackage; } + /** + * The user-readable description of the device profile's privileges. + * + * Populated by the system. + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable String getDeviceProfilePrivilegesDescription() { + return mDeviceProfilePrivilegesDescription; + } + @Override @DataClass.Generated.Member public String toString() { @@ -267,7 +299,8 @@ public final class AssociationRequest implements Parcelable { "singleDevice = " + mSingleDevice + ", " + "deviceFilters = " + mDeviceFilters + ", " + "deviceProfile = " + mDeviceProfile + ", " + - "callingPackage = " + mCallingPackage + + "callingPackage = " + mCallingPackage + ", " + + "deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription + " }"; } @@ -287,7 +320,8 @@ public final class AssociationRequest implements Parcelable { && mSingleDevice == that.mSingleDevice && Objects.equals(mDeviceFilters, that.mDeviceFilters) && Objects.equals(mDeviceProfile, that.mDeviceProfile) - && Objects.equals(mCallingPackage, that.mCallingPackage); + && Objects.equals(mCallingPackage, that.mCallingPackage) + && Objects.equals(mDeviceProfilePrivilegesDescription, that.mDeviceProfilePrivilegesDescription); } @Override @@ -301,6 +335,7 @@ public final class AssociationRequest implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mDeviceFilters); _hash = 31 * _hash + Objects.hashCode(mDeviceProfile); _hash = 31 * _hash + Objects.hashCode(mCallingPackage); + _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription); return _hash; } @@ -314,10 +349,12 @@ public final class AssociationRequest implements Parcelable { if (mSingleDevice) flg |= 0x1; if (mDeviceProfile != null) flg |= 0x4; if (mCallingPackage != null) flg |= 0x8; + if (mDeviceProfilePrivilegesDescription != null) flg |= 0x10; dest.writeByte(flg); dest.writeParcelableList(mDeviceFilters, flags); if (mDeviceProfile != null) dest.writeString(mDeviceProfile); if (mCallingPackage != null) dest.writeString(mCallingPackage); + if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription); } @Override @@ -337,6 +374,7 @@ public final class AssociationRequest implements Parcelable { in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader()); String deviceProfile = (flg & 0x4) == 0 ? null : in.readString(); String callingPackage = (flg & 0x8) == 0 ? null : in.readString(); + String deviceProfilePrivilegesDescription = (flg & 0x10) == 0 ? null : in.readString(); this.mSingleDevice = singleDevice; this.mDeviceFilters = deviceFilters; @@ -346,6 +384,7 @@ public final class AssociationRequest implements Parcelable { com.android.internal.util.AnnotationValidations.validate( DeviceProfile.class, null, mDeviceProfile); this.mCallingPackage = callingPackage; + this.mDeviceProfilePrivilegesDescription = deviceProfilePrivilegesDescription; onConstructed(); } @@ -365,10 +404,10 @@ public final class AssociationRequest implements Parcelable { }; @DataClass.Generated( - time = 1604534468409L, - codegenVersion = "1.0.20", + time = 1610132130920L, + codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java", - inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\npublic void setCallingPackage(java.lang.String)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)") + inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\nprivate void onConstructed()\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index 0ba09fdab8082..f306805ac3a4b 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -662,6 +662,34 @@ public final class PermissionControllerManager { }, executor); } + /** + * Gets the description of the privileges associated with the given device profiles + * + * @param profileName Name of the device profile + * @param executor Executor on which to invoke the callback + * @param callback Callback to receive the result + * + * @hide + */ + @RequiresPermission(Manifest.permission.MANAGE_COMPANION_DEVICES) + public void getPrivilegesDescriptionStringForProfile( + @NonNull String profileName, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer callback) { + mRemoteService.postAsync(service -> { + AndroidFuture future = new AndroidFuture<>(); + service.getPrivilegesDescriptionStringForProfile(profileName, future); + return future; + }).whenCompleteAsync((description, err) -> { + if (err != null) { + Log.e(TAG, "Error from getPrivilegesDescriptionStringForProfile", err); + callback.accept(null); + } else { + callback.accept(description); + } + }, executor); + } + /** * @see PermissionControllerManager#updateUserSensitiveForApp * @hide diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index 8441fea1095a4..8105b65170158 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -540,12 +540,13 @@ public abstract class PermissionControllerService extends Service { public void getPrivilegesDescriptionStringForProfile( @NonNull String deviceProfileName, @NonNull AndroidFuture callback) { - checkStringNotEmpty(deviceProfileName); - Objects.requireNonNull(callback); - - enforceSomePermissionsGrantedToCaller(Manifest.permission.MANAGE_COMPANION_DEVICES); - try { + checkStringNotEmpty(deviceProfileName); + Objects.requireNonNull(callback); + + enforceSomePermissionsGrantedToCaller( + Manifest.permission.MANAGE_COMPANION_DEVICES); + callback.complete(PermissionControllerService .this .getPrivilegesDescriptionStringForProfile(deviceProfileName)); diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 1b96b0045ef0b..731bdccb914f2 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -25,11 +25,14 @@ device + + watch + Set <strong>%1$s</strong> to manage your %2$s - <strong>%3$s</strong> - %1$s is needed to manage your %2$s. %3$s will get access to %4$s while the %5$s is connected. + %1$s is needed to manage your %2$s. %3$s Yes diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java index f42a51d6593a5..620c7ae96ebfe 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java @@ -17,11 +17,13 @@ package com.android.companiondevicemanager; import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress; +import static android.text.TextUtils.emptyIfNull; import static android.text.TextUtils.withoutPrefix; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static java.util.Objects.requireNonNull; +import android.annotation.Nullable; import android.app.Activity; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; @@ -65,10 +67,7 @@ public class DeviceChooserActivity extends Activity { getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); String deviceProfile = getRequest().getDeviceProfile(); - String profileName = deviceProfile == null - ? getString(R.string.profile_name_generic) - //TODO introduce PermissionController APIs to resolve UI values - : withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile).toLowerCase(); + String profileName = getDeviceProfileName(deviceProfile); if (getRequest().isSingleDevice()) { setContentView(R.layout.device_confirmation); @@ -112,15 +111,14 @@ public class DeviceChooserActivity extends Activity { TextView profileSummary = findViewById(R.id.profile_summary); if (deviceProfile != null) { - //TODO introduce PermissionController APIs to resolve UI values - String privileges = "Notifications, Phone, Contacts and Calendar"; + String privacyDisclaimer = emptyIfNull(getRequest() + .getDeviceProfilePrivilegesDescription()) + .replace("APP_NAME", getCallingAppName()); profileSummary.setVisibility(View.VISIBLE); profileSummary.setText(getString(R.string.profile_summary, getCallingAppName(), profileName, - getCallingAppName(), - privileges, - profileName)); + privacyDisclaimer)); } else { profileSummary.setVisibility(View.GONE); } @@ -135,6 +133,24 @@ public class DeviceChooserActivity extends Activity { return getService().mRequest; } + private String getDeviceProfileName(@Nullable String deviceProfile) { + if (deviceProfile == null) { + return getString(R.string.profile_name_generic); + } + switch (deviceProfile) { + case AssociationRequest.DEVICE_PROFILE_WATCH: { + return getString(R.string.profile_name_watch); + } + default: { + Log.wtf(LOG_TAG, + "No localized profile name found for device profile: " + deviceProfile); + return withoutPrefix("android.app.role.COMPANION_DEVICE_", deviceProfile) + .toLowerCase() + .replace('_', ' '); + } + } + } + private void cancel() { getService().onCancel(); setResult(RESULT_CANCELED); diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 728e829007306..04e08aeaf78ea 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -27,6 +27,7 @@ import static com.android.internal.util.Preconditions.checkState; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; +import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MINUTES; import android.annotation.CheckResult; @@ -72,6 +73,7 @@ import android.os.ShellCallback; import android.os.ShellCommand; import android.os.UserHandle; import android.os.UserManager; +import android.permission.PermissionControllerManager; import android.provider.Settings; import android.provider.SettingsStringUtil.ComponentNameSet; import android.text.BidiFormatter; @@ -159,6 +161,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private AssociationRequest mRequest; private String mCallingPackage; private AndroidFuture mOngoingDeviceDiscovery; + private PermissionControllerManager mPermissionControllerManager; private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener = new BluetoothDeviceConnectedListener(); @@ -184,6 +187,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mAmInternal = LocalServices.getService(ActivityManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); + mPermissionControllerManager = requireNonNull( + context.getSystemService(PermissionControllerManager.class)); Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO); mServiceConnectors = new PerUser>() { @@ -344,13 +349,25 @@ public class CompanionDeviceManagerService extends SystemService implements Bind request.setCallingPackage(callingPackage); callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0); - final long callingIdentity = Binder.clearCallingIdentity(); - try { - mOngoingDeviceDiscovery = mServiceConnectors.forUser(userId).postAsync(service -> { + AndroidFuture fetchProfileDescription = + request.getDeviceProfile() == null + ? AndroidFuture.completedFuture(null) + : getDeviceProfilePermissionDescription( + request.getDeviceProfile()); + + mOngoingDeviceDiscovery = fetchProfileDescription.thenComposeAsync(description -> { + request.setDeviceProfilePrivilegesDescription(description); + + return mServiceConnectors.forUser(userId).postAsync(service -> { AndroidFuture future = new AndroidFuture<>(); service.startDiscovery(request, callingPackage, callback, future); return future; - }).cancelTimeout().whenComplete(uncheckExceptions((association, err) -> { + }).cancelTimeout(); + + }, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> { + + final long callingIdentity = Binder.clearCallingIdentity(); + try { if (err == null) { addAssociation(association); } else { @@ -358,10 +375,10 @@ public class CompanionDeviceManagerService extends SystemService implements Bind callback.onFailure("No devices found: " + err.getMessage()); } cleanup(); - })); - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + })); } @Override @@ -904,6 +921,19 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mCurrentlyConnectedDevices.remove(address); } + private AndroidFuture getDeviceProfilePermissionDescription(String deviceProfile) { + AndroidFuture result = new AndroidFuture<>(); + mPermissionControllerManager.getPrivilegesDescriptionStringForProfile( + deviceProfile, FgThread.getExecutor(), desc -> { + try { + result.complete(requireNonNull(desc)); + } catch (Exception e) { + result.completeExceptionally(e); + } + }); + return result; + } + private class ShellCmd extends ShellCommand { public static final String USAGE = "help\n" + "list USER_ID\n"