diff options
author | 2024-02-22 20:53:15 +0000 | |
---|---|---|
committer | 2024-03-22 18:28:53 +0000 | |
commit | 0800361893d5f665026155911404efc2315ab5a3 (patch) | |
tree | cf375d1187620c95ae60a5c497d07789e5e247b2 /framework-s/java | |
parent | af18ea4e3d7179a9da0e1f0a6a670dece2065fbd (diff) |
Properly handle exceptions for IRoleController
Update the methods in IRoleController to catch the exception and
send it back to the caller's site.
This CL is a general fix for exception handling in IRoleController. It
also fixes and an issue we previously encountered where the role migration
failed when PC version mismatched. More details:
In the very rare cases that the PermissionController is not in an
updated version, it's possible that the system-server side of the role
migration code is in place but PC doesn't have the code to return the
legacyFallbackEnabledRoles. In this case currently we throw
UnsupportedOperationException. However, after this exception is thrown,
we didn't resolve the result for the cross-binder remote callback that's
being sent to PC. Hence in system-server it will wait until this call is
timedout.
Fixes: 325264710
Test: RoleManagerTest and additionally tested locally
LOW_COVERAGE_REASON=b/330904893
Change-Id: If77b32e39db5b0853cd54c8b74c871b0684611f5
Diffstat (limited to 'framework-s/java')
-rw-r--r-- | framework-s/java/android/app/role/RoleControllerManager.java | 81 | ||||
-rw-r--r-- | framework-s/java/android/app/role/RoleControllerService.java | 127 |
2 files changed, 137 insertions, 71 deletions
diff --git a/framework-s/java/android/app/role/RoleControllerManager.java b/framework-s/java/android/app/role/RoleControllerManager.java index 57da2ccd0..72228c7ca 100644 --- a/framework-s/java/android/app/role/RoleControllerManager.java +++ b/framework-s/java/android/app/role/RoleControllerManager.java @@ -50,10 +50,15 @@ import java.util.function.Consumer; public class RoleControllerManager { /** - * Bundle key for getting legacy fallback disabled roles + * Bundle key for the payload of RoleController APIs */ - public static final String KEY_LEGACY_FALLBACK_DISABLED_ROLES = - "LEGACY_FALLBACK_DISABLED_ROLES"; + public static final String KEY_RESULT = RoleControllerManager.class.getName() + ".key.RESULT"; + + /** + * Bundle key for the error of RoleController APIs + */ + public static final String KEY_EXCEPTION = RoleControllerManager.class.getName() + + ".key.EXCEPTION"; private static final String LOG_TAG = RoleControllerManager.class.getSimpleName(); @@ -145,9 +150,9 @@ public class RoleControllerManager { */ public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.grantDefaultRoles(new RemoteCallback(future::complete)); + AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> { + AndroidFuture<Boolean> future = new AndroidFuture<>(); + service.grantDefaultRoles(createBooleanRemoteCallback(future)); return future; }); propagateCallback(operation, "grantDefaultRoles", executor, callback); @@ -160,10 +165,10 @@ public class RoleControllerManager { */ public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); + AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> { + AndroidFuture<Boolean> future = new AndroidFuture<>(); service.onAddRoleHolder(roleName, packageName, flags, - new RemoteCallback(future::complete)); + createBooleanRemoteCallback(future)); return future; }); propagateCallback(operation, "onAddRoleHolder", callback); @@ -176,10 +181,10 @@ public class RoleControllerManager { */ public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); + AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> { + AndroidFuture<Boolean> future = new AndroidFuture<>(); service.onRemoveRoleHolder(roleName, packageName, flags, - new RemoteCallback(future::complete)); + createBooleanRemoteCallback(future)); return future; }); propagateCallback(operation, "onRemoveRoleHolder", callback); @@ -192,9 +197,9 @@ public class RoleControllerManager { */ public void onClearRoleHolders(@NonNull String roleName, @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.onClearRoleHolders(roleName, flags, new RemoteCallback(future::complete)); + AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> { + AndroidFuture<Boolean> future = new AndroidFuture<>(); + service.onClearRoleHolders(roleName, flags, createBooleanRemoteCallback(future)); return future; }); propagateCallback(operation, "onClearRoleHolders", callback); @@ -208,10 +213,10 @@ public class RoleControllerManager { @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); + AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> { + AndroidFuture<Boolean> future = new AndroidFuture<>(); service.isApplicationVisibleForRole(roleName, packageName, - new RemoteCallback(future::complete)); + createBooleanRemoteCallback(future)); return future; }); propagateCallback(operation, "isApplicationVisibleForRole", executor, callback); @@ -225,9 +230,9 @@ public class RoleControllerManager { @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String roleName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.isRoleVisible(roleName, new RemoteCallback(future::complete)); + AndroidFuture<Boolean> operation = mRemoteService.postAsync(service -> { + AndroidFuture<Boolean> future = new AndroidFuture<>(); + service.isRoleVisible(roleName, createBooleanRemoteCallback(future)); return future; }); propagateCallback(operation, "isRoleVisible", executor, callback); @@ -241,8 +246,15 @@ public class RoleControllerManager { public void getLegacyFallbackDisabledRoles(@NonNull @CallbackExecutor Executor executor, @NonNull Consumer<List<String>> callback) { mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.getLegacyFallbackDisabledRoles(new RemoteCallback(future::complete)); + AndroidFuture<List<String>> future = new AndroidFuture<>(); + service.getLegacyFallbackDisabledRoles(new RemoteCallback(result -> { + Exception exception = (Exception) result.getSerializable(KEY_EXCEPTION); + if (exception != null) { + future.completeExceptionally(exception); + } else { + future.complete(result.getStringArrayList(KEY_RESULT)); + } + })); return future; }).orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) .whenComplete((res, err) -> executor.execute(() -> { @@ -253,8 +265,7 @@ public class RoleControllerManager { err); callback.accept(null); } else { - callback.accept(res.getStringArrayList( - KEY_LEGACY_FALLBACK_DISABLED_ROLES)); + callback.accept(res); } } finally { Binder.restoreCallingIdentity(token); @@ -262,7 +273,19 @@ public class RoleControllerManager { })); } - private void propagateCallback(AndroidFuture<Bundle> operation, String opName, + @NonNull + private RemoteCallback createBooleanRemoteCallback(@NonNull AndroidFuture<Boolean> future) { + return new RemoteCallback(result -> { + Exception exception = (Exception) result.getSerializable(KEY_EXCEPTION); + if (exception != null) { + future.completeExceptionally(exception); + } else { + future.complete(result.getBoolean(KEY_RESULT)); + } + }); + } + + private void propagateCallback(AndroidFuture<Boolean> operation, String opName, @CallbackExecutor @NonNull Executor executor, Consumer<Boolean> destination) { operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) @@ -273,7 +296,7 @@ public class RoleControllerManager { Log.e(LOG_TAG, "Error calling " + opName + "()", err); destination.accept(false); } else { - destination.accept(res != null); + destination.accept(res); } } finally { Binder.restoreCallingIdentity(token); @@ -281,7 +304,7 @@ public class RoleControllerManager { })); } - private void propagateCallback(AndroidFuture<Bundle> operation, String opName, + private void propagateCallback(AndroidFuture<Boolean> operation, String opName, RemoteCallback destination) { operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) .whenComplete((res, err) -> { @@ -291,7 +314,7 @@ public class RoleControllerManager { Log.e(LOG_TAG, "Error calling " + opName + "()", err); destination.sendResult(null); } else { - destination.sendResult(res); + destination.sendResult(res ? Bundle.EMPTY : null); } } finally { Binder.restoreCallingIdentity(token); diff --git a/framework-s/java/android/app/role/RoleControllerService.java b/framework-s/java/android/app/role/RoleControllerService.java index 60a13f7ba..2155ee4eb 100644 --- a/framework-s/java/android/app/role/RoleControllerService.java +++ b/framework-s/java/android/app/role/RoleControllerService.java @@ -33,6 +33,7 @@ import android.os.Process; import android.os.RemoteCallback; import android.os.UserHandle; import android.permission.flags.Flags; +import android.util.Log; import com.android.internal.util.Preconditions; @@ -56,6 +57,7 @@ import java.util.concurrent.Executor; @Deprecated @SystemApi public abstract class RoleControllerService extends Service { + private static final String LOG_TAG = RoleControllerService.class.getSimpleName(); /** * The {@link Intent} that must be declared as handled by the service. @@ -89,7 +91,6 @@ public abstract class RoleControllerService extends Service { @Override public void grantDefaultRoles(RemoteCallback callback) { enforceCallerSystemUid("grantDefaultRoles"); - Objects.requireNonNull(callback, "callback cannot be null"); mWorkerHandler.post(() -> RoleControllerService.this.grantDefaultRoles(callback)); @@ -99,10 +100,6 @@ public abstract class RoleControllerService extends Service { public void onAddRoleHolder(String roleName, String packageName, int flags, RemoteCallback callback) { enforceCallerSystemUid("onAddRoleHolder"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, - "packageName cannot be null or empty"); Objects.requireNonNull(callback, "callback cannot be null"); mWorkerHandler.post(() -> RoleControllerService.this.onAddRoleHolder(roleName, @@ -113,10 +110,6 @@ public abstract class RoleControllerService extends Service { public void onRemoveRoleHolder(String roleName, String packageName, int flags, RemoteCallback callback) { enforceCallerSystemUid("onRemoveRoleHolder"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, - "packageName cannot be null or empty"); Objects.requireNonNull(callback, "callback cannot be null"); mWorkerHandler.post(() -> RoleControllerService.this.onRemoveRoleHolder(roleName, @@ -126,8 +119,6 @@ public abstract class RoleControllerService extends Service { @Override public void onClearRoleHolders(String roleName, int flags, RemoteCallback callback) { enforceCallerSystemUid("onClearRoleHolders"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Objects.requireNonNull(callback, "callback cannot be null"); mWorkerHandler.post(() -> RoleControllerService.this.onClearRoleHolders(roleName, @@ -145,39 +136,54 @@ public abstract class RoleControllerService extends Service { public void isApplicationQualifiedForRole(String roleName, String packageName, RemoteCallback callback) { enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, - "packageName cannot be null or empty"); Objects.requireNonNull(callback, "callback cannot be null"); - boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName); - callback.sendResult(qualified ? Bundle.EMPTY : null); + Bundle result = new Bundle(); + try { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkStringNotEmpty(packageName, + "packageName cannot be null or empty"); + boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName); + result.putBoolean(RoleControllerManager.KEY_RESULT, qualified); + } catch (Exception e) { + result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e); + } + callback.sendResult(result); } @Override public void isApplicationVisibleForRole(String roleName, String packageName, RemoteCallback callback) { enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, - "packageName cannot be null or empty"); Objects.requireNonNull(callback, "callback cannot be null"); - boolean visible = onIsApplicationVisibleForRole(roleName, packageName); - callback.sendResult(visible ? Bundle.EMPTY : null); + Bundle result = new Bundle(); + try { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkStringNotEmpty(packageName, + "packageName cannot be null or empty"); + boolean visible = onIsApplicationVisibleForRole(roleName, packageName); + result.putBoolean(RoleControllerManager.KEY_RESULT, visible); + } catch (Exception e) { + result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e); + } + callback.sendResult(result); } @Override public void isRoleVisible(String roleName, RemoteCallback callback) { enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Objects.requireNonNull(callback, "callback cannot be null"); - boolean visible = onIsRoleVisible(roleName); - callback.sendResult(visible ? Bundle.EMPTY : null); + Bundle result = new Bundle(); + try { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + boolean visible = onIsRoleVisible(roleName); + result.putBoolean(RoleControllerManager.KEY_RESULT, visible); + } catch (Exception e) { + result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e); + } + callback.sendResult(result); } @Override @@ -186,36 +192,71 @@ public abstract class RoleControllerService extends Service { Objects.requireNonNull(callback, "callback cannot be null"); - List<String> legacyFallbackDisabledRoles = onGetLegacyFallbackDisabledRoles(); Bundle result = new Bundle(); - result.putStringArrayList(RoleControllerManager.KEY_LEGACY_FALLBACK_DISABLED_ROLES, - new ArrayList<>(legacyFallbackDisabledRoles)); + try { + List<String> legacyFallbackDisabledRoles = onGetLegacyFallbackDisabledRoles(); + result.putStringArrayList(RoleControllerManager.KEY_RESULT, + new ArrayList<>(legacyFallbackDisabledRoles)); + } catch (Exception e) { + result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e); + } callback.sendResult(result); } }; } - private void grantDefaultRoles(@NonNull RemoteCallback callback) { - boolean successful = onGrantDefaultRoles(); - callback.sendResult(successful ? Bundle.EMPTY : null); + private void grantDefaultRoles(RemoteCallback callback) { + Bundle result = new Bundle(); + try { + boolean successful = onGrantDefaultRoles(); + result.putBoolean(RoleControllerManager.KEY_RESULT, successful); + } catch (Exception e) { + result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e); + } + callback.sendResult(result); } private void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - boolean successful = onAddRoleHolder(roleName, packageName, flags); - callback.sendResult(successful ? Bundle.EMPTY : null); + @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) { + Bundle result = new Bundle(); + try { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkStringNotEmpty(packageName, + "packageName cannot be null or empty"); + boolean successful = onAddRoleHolder(roleName, packageName, flags); + result.putBoolean(RoleControllerManager.KEY_RESULT, successful); + } catch (Exception e) { + result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e); + } + callback.sendResult(result); } private void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - boolean successful = onRemoveRoleHolder(roleName, packageName, flags); - callback.sendResult(successful ? Bundle.EMPTY : null); + @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) { + Bundle result = new Bundle(); + try { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkStringNotEmpty(packageName, + "packageName cannot be null or empty"); + boolean successful = onRemoveRoleHolder(roleName, packageName, flags); + result.putBoolean(RoleControllerManager.KEY_RESULT, successful); + } catch (Exception e) { + result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e); + } + callback.sendResult(result); } private void onClearRoleHolders(@NonNull String roleName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - boolean successful = onClearRoleHolders(roleName, flags); - callback.sendResult(successful ? Bundle.EMPTY : null); + @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) { + Bundle result = new Bundle(); + try { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + boolean successful = onClearRoleHolders(roleName, flags); + result.putBoolean(RoleControllerManager.KEY_RESULT, successful); + } catch (Exception e) { + result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e); + } + callback.sendResult(result); } /** @@ -327,6 +368,8 @@ public abstract class RoleControllerService extends Service { @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED) @NonNull public List<String> onGetLegacyFallbackDisabledRoles() { + Log.wtf(LOG_TAG, "onGetLegacyFallbackDisabledRoles is unsupported by this version of" + + " PermissionController"); throw new UnsupportedOperationException(); } } |