diff options
author | 2024-09-28 18:10:52 +0100 | |
---|---|---|
committer | 2024-09-30 19:53:58 +0000 | |
commit | a620ff83578512539a25b6351d034290850acc7a (patch) | |
tree | 86c72919b3ddf463121c7744a10fac13f5351c82 /services/appfunctions/java | |
parent | 456b5e0388a1d32cad82af0557be7d93792f94ed (diff) |
Add a timeout for CancellationSignal issued to AppFunctionService before which it is unbound.
Change-Id: If9c66bf7951b72e66d7780a58595b58c4f495f3d
Flag: android.app.appfunctions.flags.enable_app_function_manager
Test: atest CtsAppFunctionTestCases -c
Bug: 360864791
Diffstat (limited to 'services/appfunctions/java')
5 files changed, 67 insertions, 17 deletions
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java index b6c8fc7b80c7..14298f416d87 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java @@ -430,6 +430,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { serviceIntent, bindFlags, targetUser, + mServiceConfig.getExecuteAppFunctionCancellationTimeoutMillis(), + cancellationSignal, new RunServiceCallCallback<IAppFunctionService>() { @Override public void onServiceConnected( @@ -470,6 +472,14 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { "Failed to connect to AppFunctionService", /* extras= */ null)); } + + @Override + public void onCancelled() { + // Do not forward the result back to the caller once it has been + // canceled. The caller does not need a notification and should + // proceed after initiating a cancellation. + safeExecuteAppFunctionCallback.disable(); + } }); if (!bindServiceResult) { diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java index cd5c3831bc0d..55173c350e71 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java +++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java @@ -17,12 +17,13 @@ package com.android.server.appfunctions; import android.annotation.NonNull; import android.content.Intent; +import android.os.CancellationSignal; import android.os.UserHandle; /** - * Defines a contract for establishing temporary connections to services and executing operations - * within a specified timeout. Implementations of this interface provide mechanisms to ensure that - * services are properly unbound after the operation completes or a timeout occurs. + * Defines a contract for establishing temporary connections to services and executing operations. + * Implementations of this interface provide mechanisms to ensure that services are properly unbound + * after the operation completes or a cancellation timeout occurs. * * @param <T> Class of wrapped service. */ @@ -30,20 +31,25 @@ public interface RemoteServiceCaller<T> { /** * Initiates service binding and executes a provided method when the service connects. Unbinds - * the service after execution or upon timeout. Returns the result of the bindService API. + * the service after execution or upon cancellation timeout. Returns the result of the + * bindService API. * * <p>When the service connection was made successfully, it's the caller responsibility to * report the usage is completed and can be unbound by calling {@link * ServiceUsageCompleteListener#onCompleted()}. * - * <p>This method includes a timeout mechanism to prevent the system from being stuck in a state - * where a service is bound indefinitely (for example, if the binder method never returns). This - * helps ensure that the calling app does not remain alive unnecessarily. + * <p>This method includes a timeout mechanism for cancellation to prevent the system from being + * stuck in a state where a service is bound indefinitely. If the app to be bound does not + * return the result within `cancellationTimeoutMillis` after the cancellation signal is sent, + * this method will unbind the service connection. * * @param intent An Intent object that describes the service that should be bound. * @param bindFlags Flags used to control the binding process See {@link * android.content.Context#bindService}. * @param userHandle The UserHandle of the user for which the service should be bound. + * @param cancellationTimeoutMillis The timeout before the service is unbound after a + * cancellation signal is issued. + * @param cancellationSignal The cancellation signal forwarded to the service. * @param callback A callback to be invoked for various events. See {@link * RunServiceCallCallback}. */ @@ -51,6 +57,8 @@ public interface RemoteServiceCaller<T> { @NonNull Intent intent, int bindFlags, @NonNull UserHandle userHandle, + long cancellationTimeoutMillis, + @NonNull CancellationSignal cancellationSignal, @NonNull RunServiceCallCallback<T> callback); /** An interface for clients to signal that they have finished using a bound service. */ @@ -73,5 +81,8 @@ public interface RemoteServiceCaller<T> { /** Called when the service connection was failed to establish. */ void onFailedToConnect(); + + /** Called when the caller has cancelled this remote service call. */ + void onCancelled(); } } diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java index ffca8491abcd..7e3a29afc01c 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java +++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -63,9 +64,17 @@ public class RemoteServiceCallerImpl<T> implements RemoteServiceCaller<T> { @NonNull Intent intent, int bindFlags, @NonNull UserHandle userHandle, + long cancellationTimeoutMillis, + @NonNull CancellationSignal cancellationSignal, @NonNull RunServiceCallCallback<T> callback) { OneOffServiceConnection serviceConnection = - new OneOffServiceConnection(intent, bindFlags, userHandle, callback); + new OneOffServiceConnection( + intent, + bindFlags, + userHandle, + cancellationTimeoutMillis, + cancellationSignal, + callback); return serviceConnection.bindAndRun(); } @@ -76,23 +85,38 @@ public class RemoteServiceCallerImpl<T> implements RemoteServiceCaller<T> { private final int mFlags; private final UserHandle mUserHandle; private final RunServiceCallCallback<T> mCallback; + private final long mCancellationTimeoutMillis; + private final CancellationSignal mCancellationSignal; + private final Runnable mCancellationTimeoutRunnable; OneOffServiceConnection( @NonNull Intent intent, int flags, @NonNull UserHandle userHandle, + long cancellationTimeoutMillis, + @NonNull CancellationSignal cancellationSignal, @NonNull RunServiceCallCallback<T> callback) { mIntent = intent; mFlags = flags; mCallback = callback; mUserHandle = userHandle; + mCancellationTimeoutMillis = cancellationTimeoutMillis; + mCancellationSignal = cancellationSignal; + mCancellationTimeoutRunnable = this::safeUnbind; } public boolean bindAndRun() { boolean bindServiceResult = mContext.bindServiceAsUser(mIntent, this, mFlags, mUserHandle); - if (!bindServiceResult) { + if (bindServiceResult) { + mCancellationSignal.setOnCancelListener( + () -> { + mCallback.onCancelled(); + mHandler.postDelayed( + mCancellationTimeoutRunnable, mCancellationTimeoutMillis); + }); + } else { safeUnbind(); } @@ -126,6 +150,7 @@ public class RemoteServiceCallerImpl<T> implements RemoteServiceCaller<T> { private void safeUnbind() { try { + mHandler.removeCallbacks(mCancellationTimeoutRunnable); mContext.unbindService(this); } catch (Exception ex) { Log.w(TAG, "Failed to unbind", ex); diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java index caa4bf0e9d27..8d2d1b2dfd00 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java +++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java @@ -21,6 +21,9 @@ public interface ServiceConfig { // TODO(b/357551503): Obtain namespace from DeviceConfig. String NAMESPACE_APP_FUNCTIONS = "appfunctions"; - /** Returns the maximum time to wait for an app function execution to be complete. */ - long getExecuteAppFunctionTimeoutMillis(); + /** + * Returns the timeout for which the system server waits for the app function service to + * successfully cancel the execution of an app function before forcefully unbinding the service. + */ + long getExecuteAppFunctionCancellationTimeoutMillis(); } diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java index f18789be5ce8..787f52afda4b 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java +++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java @@ -20,15 +20,16 @@ import android.provider.DeviceConfig; /** Implementation of {@link ServiceConfig} */ public class ServiceConfigImpl implements ServiceConfig { - static final String DEVICE_CONFIG_PROPERTY_EXECUTION_TIMEOUT = - "execute_app_function_timeout_millis"; - static final long DEFAULT_EXECUTE_APP_FUNCTION_TIMEOUT_MS = 5000L; + static final String DEVICE_CONFIG_PROPERTY_EXECUTION_CANCELLATION_TIMEOUT = + "execute_app_function_cancellation_timeout_millis"; + static final long DEFAULT_EXECUTE_APP_FUNCTION_CANCELLATION_TIMEOUT_MS = 5000L; + @Override - public long getExecuteAppFunctionTimeoutMillis() { + public long getExecuteAppFunctionCancellationTimeoutMillis() { return DeviceConfig.getLong( NAMESPACE_APP_FUNCTIONS, - DEVICE_CONFIG_PROPERTY_EXECUTION_TIMEOUT, - DEFAULT_EXECUTE_APP_FUNCTION_TIMEOUT_MS); + DEVICE_CONFIG_PROPERTY_EXECUTION_CANCELLATION_TIMEOUT, + DEFAULT_EXECUTE_APP_FUNCTION_CANCELLATION_TIMEOUT_MS); } } |