summaryrefslogtreecommitdiff
path: root/framework-s/java
diff options
context:
space:
mode:
author Yi-an Chen <theianchen@google.com> 2024-02-22 20:53:15 +0000
committer Yi-an Chen <theianchen@google.com> 2024-03-22 18:28:53 +0000
commit0800361893d5f665026155911404efc2315ab5a3 (patch)
treecf375d1187620c95ae60a5c497d07789e5e247b2 /framework-s/java
parentaf18ea4e3d7179a9da0e1f0a6a670dece2065fbd (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.java81
-rw-r--r--framework-s/java/android/app/role/RoleControllerService.java127
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();
}
}