summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/provider/Settings.java29
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java5
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java4
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java12
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java18
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java170
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java16
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java20
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java97
9 files changed, 326 insertions, 45 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 90ec5aad235d..4f5b67c34845 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11173,6 +11173,35 @@ public final class Settings {
"visual_query_accessibility_detection_enabled";
/**
+ * Timeout to be used for unbinding to the configured remote
+ * {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService} if there are no
+ * requests in the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS =
+ "on_device_intelligence_unbind_timeout_ms";
+
+
+ /**
+ * Timeout that represents maximum idle time before which a callback should be populated.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS =
+ "on_device_intelligence_idle_timeout_ms";
+
+ /**
+ * Timeout to be used for unbinding to the configured remote
+ * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} if there
+ * are no requests in the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS =
+ "on_device_inference_unbind_timeout_ms";
+
+ /**
* Control whether Night display is currently activated.
* @hide
*/
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index be3f4108fdd1..888e39593a2e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -274,6 +274,9 @@ public class SecureSettings {
Settings.Secure.SCREEN_RESOLUTION_MODE,
Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
- Settings.Secure.CHARGE_OPTIMIZATION_MODE
+ Settings.Secure.CHARGE_OPTIMIZATION_MODE,
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+ Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b1feede57506..b992ddc8a397 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -18,6 +18,7 @@ package android.provider.settings.validators;
import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_LONG_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.AUTOFILL_SERVICE_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
@@ -433,5 +434,8 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
new InclusiveIntegerRangeValidator(0, 10));
VALIDATORS.put(Secure.CHARGE_OPTIMIZATION_MODE, new InclusiveIntegerRangeValidator(0, 10));
+ VALIDATORS.put(Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 677c81ad9271..255b1ad3b3d2 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -239,6 +239,18 @@ public class SettingsValidators {
}
};
+ static final Validator ANY_LONG_VALIDATOR = value -> {
+ if (value == null) {
+ return true;
+ }
+ try {
+ Long.parseLong(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ };
+
static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() {
@Override
public boolean validate(String value) {
diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
index 681dd0b49f4e..cbede233a110 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -41,7 +41,10 @@ import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
+import com.android.internal.infra.AndroidFuture;
+
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* Util methods for ensuring the Bundle passed in various methods are read-only and restricted to
@@ -183,7 +186,8 @@ public class BundleUtil {
public static IStreamingResponseCallback wrapWithValidation(
IStreamingResponseCallback streamingResponseCallback,
- Executor resourceClosingExecutor) {
+ Executor resourceClosingExecutor,
+ AndroidFuture future) {
return new IStreamingResponseCallback.Stub() {
@Override
public void onNewContent(Bundle processedResult) throws RemoteException {
@@ -203,6 +207,7 @@ public class BundleUtil {
streamingResponseCallback.onSuccess(resultBundle);
} finally {
resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+ future.complete(null);
}
}
@@ -210,6 +215,7 @@ public class BundleUtil {
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) throws RemoteException {
streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
@Override
@@ -237,7 +243,8 @@ public class BundleUtil {
}
public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback,
- Executor resourceClosingExecutor) {
+ Executor resourceClosingExecutor,
+ AndroidFuture future) {
return new IResponseCallback.Stub() {
@Override
public void onSuccess(Bundle resultBundle)
@@ -247,6 +254,7 @@ public class BundleUtil {
responseCallback.onSuccess(resultBundle);
} finally {
resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+ future.complete(null);
}
}
@@ -254,6 +262,7 @@ public class BundleUtil {
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) throws RemoteException {
responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
@Override
@@ -280,17 +289,20 @@ public class BundleUtil {
}
- public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback) {
+ public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback,
+ AndroidFuture future) {
return new ITokenInfoCallback.Stub() {
@Override
public void onSuccess(TokenInfo tokenInfo) throws RemoteException {
responseCallback.onSuccess(tokenInfo);
+ future.complete(null);
}
@Override
public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams)
throws RemoteException {
responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
};
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 235e3cd7c9d2..b2e861cf2876 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -33,6 +33,7 @@ import android.annotation.RequiresPermission;
import android.app.AppGlobals;
import android.app.ondeviceintelligence.DownloadCallback;
import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
import android.app.ondeviceintelligence.IDownloadCallback;
import android.app.ondeviceintelligence.IFeatureCallback;
import android.app.ondeviceintelligence.IFeatureDetailsCallback;
@@ -64,6 +65,7 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
@@ -82,13 +84,17 @@ import com.android.internal.infra.ServiceConnector;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* This is the system service for handling calls on the
@@ -135,7 +141,6 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
@GuardedBy("mLock")
private String[] mTemporaryServiceNames;
-
@GuardedBy("mLock")
private String[] mTemporaryBroadcastKeys;
@GuardedBy("mLock")
@@ -145,6 +150,8 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
* Handler used to reset the temporary service names.
*/
private Handler mTemporaryHandler;
+ private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper());
+
public OnDeviceIntelligenceManagerService(Context context) {
super(context);
@@ -204,8 +211,16 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
return;
}
ensureRemoteIntelligenceServiceInitialized();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getVersion(remoteCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getVersion(new RemoteCallback(
+ result -> {
+ remoteCallback.sendResult(result);
+ future.complete(null);
+ }));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -225,8 +240,25 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getFeature(callerUid, id, featureCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getFeature(callerUid, id, new IFeatureCallback.Stub() {
+ @Override
+ public void onSuccess(Feature result) throws RemoteException {
+ featureCallback.onSuccess(result);
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) throws RemoteException {
+ featureCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -246,9 +278,29 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.listFeatures(callerUid,
- listFeaturesCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.listFeatures(callerUid,
+ new IListFeaturesCallback.Stub() {
+ @Override
+ public void onSuccess(List<Feature> result)
+ throws RemoteException {
+ listFeaturesCallback.onSuccess(result);
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams)
+ throws RemoteException {
+ listFeaturesCallback.onFailure(errorCode, errorMessage,
+ errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -270,9 +322,29 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getFeatureDetails(callerUid, feature,
- featureDetailsCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getFeatureDetails(callerUid, feature,
+ new IFeatureDetailsCallback.Stub() {
+ @Override
+ public void onSuccess(FeatureDetails result)
+ throws RemoteException {
+ future.complete(null);
+ featureDetailsCallback.onSuccess(result);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams)
+ throws RemoteException {
+ future.completeExceptionally(null);
+ featureDetailsCallback.onFailure(errorCode,
+ errorMessage, errorParams);
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -293,10 +365,20 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.requestFeatureDownload(callerUid, feature,
- wrapCancellationFuture(cancellationSignalFuture),
- downloadCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ ListenableDownloadCallback listenableDownloadCallback =
+ new ListenableDownloadCallback(
+ downloadCallback,
+ mMainHandler, future, getIdleTimeoutMs());
+ service.requestFeatureDownload(callerUid, feature,
+ wrapCancellationFuture(cancellationSignalFuture),
+ listenableDownloadCallback);
+ return future; // this future has no timeout because, actual download
+ // might take long, but we fail early if there is no progress callbacks.
+ }
+ );
}
@@ -323,11 +405,15 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.requestTokenInfo(callerUid, feature,
- request,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapWithValidation(tokenInfoCallback)));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.requestTokenInfo(callerUid, feature,
+ request,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapWithValidation(tokenInfoCallback, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -362,13 +448,18 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.processRequest(callerUid, feature,
- request,
- requestType,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapProcessingFuture(processingSignalFuture),
- wrapWithValidation(responseCallback, resourceClosingExecutor)));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.processRequest(callerUid, feature,
+ request,
+ requestType,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapProcessingFuture(processingSignalFuture),
+ wrapWithValidation(responseCallback,
+ resourceClosingExecutor, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -402,13 +493,18 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.processRequestStreaming(callerUid,
- feature,
- request, requestType,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapProcessingFuture(processingSignalFuture),
- streamingCallback));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.processRequestStreaming(callerUid,
+ feature,
+ request, requestType,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapProcessingFuture(processingSignalFuture),
+ wrapWithValidation(streamingCallback,
+ resourceClosingExecutor, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -859,4 +955,10 @@ public class OnDeviceIntelligenceManagerService extends SystemService {
return mTemporaryHandler;
}
+
+ private long getIdleTimeoutMs() {
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1),
+ mContext.getUserId());
+ }
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
index 48258d7bea72..ac9747aa83b3 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -22,17 +22,21 @@ import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
import com.android.internal.infra.ServiceConnector;
+import java.util.concurrent.TimeUnit;
+
/**
* Manages the connection to the remote on-device intelligence service. Also, handles unbinding
* logic set by the service implementation via a Secure Settings flag.
*/
public class RemoteOnDeviceIntelligenceService extends
ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+ private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(4);
private static final String TAG =
RemoteOnDeviceIntelligenceService.class.getSimpleName();
@@ -48,9 +52,15 @@ public class RemoteOnDeviceIntelligenceService extends
}
@Override
+ protected long getRequestTimeoutMs() {
+ return LONG_TIMEOUT;
+ }
+
+ @Override
protected long getAutoDisconnectTimeoutMs() {
- // Disable automatic unbinding.
- // TODO: add logic to fetch this flag via SecureSettings.
- return -1;
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30),
+ mContext.getUserId());
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
index 69ba1d2fb599..18b13838ea7c 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
@@ -22,18 +22,24 @@ import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
import com.android.internal.infra.ServiceConnector;
+import java.util.concurrent.TimeUnit;
+
/**
- * Manages the connection to the remote on-device sand boxed inference service. Also, handles unbinding
+ * Manages the connection to the remote on-device sand boxed inference service. Also, handles
+ * unbinding
* logic set by the service implementation via a SecureSettings flag.
*/
public class RemoteOnDeviceSandboxedInferenceService extends
ServiceConnector.Impl<IOnDeviceSandboxedInferenceService> {
+ private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(1);
+
/**
* Creates an instance of {@link ServiceConnector}
*
@@ -54,11 +60,17 @@ public class RemoteOnDeviceSandboxedInferenceService extends
connect();
}
+ @Override
+ protected long getRequestTimeoutMs() {
+ return LONG_TIMEOUT;
+ }
+
@Override
protected long getAutoDisconnectTimeoutMs() {
- // Disable automatic unbinding.
- // TODO: add logic to fetch this flag via SecureSettings.
- return -1;
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30),
+ mContext.getUserId());
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
new file mode 100644
index 000000000000..32f0698a8f9c
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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 com.android.server.ondeviceintelligence.callbacks;
+
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This class extends the {@link IDownloadCallback} and adds a timeout Runnable to the callback
+ * such that, in the case where the callback methods are not invoked, we do not have to wait for
+ * timeout based on {@link #onDownloadCompleted} which might take minutes or hours to complete in
+ * some cases. Instead, in such cases we rely on the remote service sending progress updates and if
+ * there are *no* progress callbacks in the duration of {@link #idleTimeoutMs}, we can assume the
+ * download will not complete and enabling faster cleanup.
+ */
+public class ListenableDownloadCallback extends IDownloadCallback.Stub implements Runnable {
+ private final IDownloadCallback callback;
+ private final Handler handler;
+ private final AndroidFuture future;
+ private final long idleTimeoutMs;
+
+ /**
+ * Constructor to create a ListenableDownloadCallback.
+ *
+ * @param callback callback to send download updates to caller.
+ * @param handler handler to schedule timeout runnable.
+ * @param future future to complete to signal the callback has reached a terminal state.
+ * @param idleTimeoutMs timeout within which download updates should be received.
+ */
+ public ListenableDownloadCallback(IDownloadCallback callback, Handler handler,
+ AndroidFuture future,
+ long idleTimeoutMs) {
+ this.callback = callback;
+ this.handler = handler;
+ this.future = future;
+ this.idleTimeoutMs = idleTimeoutMs;
+ handler.postDelayed(this,
+ idleTimeoutMs); // init the timeout runnable in case no callback is ever invoked
+ }
+
+ @Override
+ public void onDownloadStarted(long bytesToDownload) throws RemoteException {
+ callback.onDownloadStarted(bytesToDownload);
+ handler.removeCallbacks(this);
+ handler.postDelayed(this, idleTimeoutMs);
+ }
+
+ @Override
+ public void onDownloadProgress(long bytesDownloaded) throws RemoteException {
+ callback.onDownloadProgress(bytesDownloaded);
+ handler.removeCallbacks(this); // remove previously queued timeout tasks.
+ handler.postDelayed(this, idleTimeoutMs); // queue fresh timeout task for next update.
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus,
+ String errorMessage, PersistableBundle errorParams) throws RemoteException {
+ callback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+ handler.removeCallbacks(this);
+ future.completeExceptionally(new TimeoutException());
+ }
+
+ @Override
+ public void onDownloadCompleted(
+ android.os.PersistableBundle downloadParams) throws RemoteException {
+ callback.onDownloadCompleted(downloadParams);
+ handler.removeCallbacks(this);
+ future.complete(null);
+ }
+
+ @Override
+ public void run() {
+ future.completeExceptionally(
+ new TimeoutException()); // complete the future as we haven't received updates
+ // for download progress.
+ }
+} \ No newline at end of file