diff options
| author | 2020-01-28 01:59:58 +0000 | |
|---|---|---|
| committer | 2020-01-28 01:59:58 +0000 | |
| commit | 86c1458a7068c091439ac74c1a65c3ab5d2392a4 (patch) | |
| tree | 8780bf359e91f1a8d6a41f854da2161661c2c8d8 | |
| parent | f83559781f8f793694b5895b86e809e1837ae472 (diff) | |
| parent | 0430237873dc5b14bcea027687005c2764774688 (diff) | |
Merge "API for Inline Presentation Renderer in ExtServices."
8 files changed, 335 insertions, 0 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index b3e403a3475c..1a039dc3ce09 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -10046,6 +10046,13 @@ package android.service.autofill { method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); } + public abstract class InlineSuggestionRenderService extends android.app.Service { + ctor public InlineSuggestionRenderService(); + method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int); + field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService"; + } + } package android.service.autofill.augmented { diff --git a/api/test-current.txt b/api/test-current.txt index d0ac7232e20f..fec5f3df9669 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2990,6 +2990,13 @@ package android.service.autofill { method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception; } + public abstract class InlineSuggestionRenderService extends android.app.Service { + ctor public InlineSuggestionRenderService(); + method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); + method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int); + field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService"; + } + public abstract class InternalOnClickAction implements android.service.autofill.OnClickAction android.os.Parcelable { ctor public InternalOnClickAction(); method public abstract void onClick(@NonNull android.view.ViewGroup); diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl new file mode 100644 index 000000000000..decdcf586ee1 --- /dev/null +++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 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 android.service.autofill; + +import android.service.autofill.IInlineSuggestionUiCallback; +import android.service.autofill.InlinePresentation; + +/** + * Interface from system to the inline suggestion render service. + * + * @hide + */ +oneway interface IInlineSuggestionRenderService { + void renderSuggestion(in IInlineSuggestionUiCallback callback, in InlinePresentation presentation, + int width, int height); +} diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl new file mode 100644 index 000000000000..a55a2ced0b89 --- /dev/null +++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 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 android.service.autofill; + +import android.view.SurfaceControl; + +/** + * Interface to receive events from inline suggestions. + * + * @hide + */ +oneway interface IInlineSuggestionUiCallback { + void autofill(); + void onContent(in SurfaceControl surface); +} diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java new file mode 100644 index 000000000000..2593aab1eb63 --- /dev/null +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 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 android.service.autofill; + +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.app.Service; +import android.app.slice.Slice; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.util.Log; +import android.view.View; + +/** + * A service that renders an inline presentation given the {@link InlinePresentation} containing + * a {@link Slice} built using the {@link androidx.autofill.AutofillSliceBuilder}. + * + * {@hide} + */ +@SystemApi +@TestApi +public abstract class InlineSuggestionRenderService extends Service { + + private static final String TAG = "InlineSuggestionRenderService"; + + /** + * The {@link Intent} that must be declared as handled by the service. + * + * <p>To be supported, the service must also require the + * {@link android.Manifest.permission#BIND_INLINE_SUGGESTION_RENDER_SERVICE} permission so + * that other applications can not abuse it. + */ + public static final String SERVICE_INTERFACE = + "android.service.autofill.InlineSuggestionRenderService"; + + private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); + + private void handleRenderSuggestion(IInlineSuggestionUiCallback callback, + InlinePresentation presentation, int width, int height) { + //TODO(b/146453086): implementation in ExtService + } + + @Override + @Nullable + public final IBinder onBind(@NonNull Intent intent) { + if (SERVICE_INTERFACE.equals(intent.getAction())) { + return new IInlineSuggestionRenderService.Stub() { + @Override + public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback, + @NonNull InlinePresentation presentation, int width, int height) { + mHandler.sendMessage(obtainMessage( + InlineSuggestionRenderService::handleRenderSuggestion, + InlineSuggestionRenderService.this, callback, presentation, + width, height)); + } + }.asBinder(); + } + + Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent); + return null; + } + + /** + * Renders the slice into a view. + */ + @Nullable + public View onRenderSuggestion(@NonNull InlinePresentation presentation, + int width, int height) { + Log.e(TAG, "service implementation (" + getClass() + " does not implement " + + "onRenderSuggestion()"); + return null; + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 020a8352ad07..e9d5b2b918f7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3339,6 +3339,13 @@ <permission android:name="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE" android:protectionLevel="signature" /> + <!-- Must be required by an {@link android.service.autofill.InlineSuggestionRenderService} + to ensure that only the system can bind to it. + @hide This is not a third-party API (intended for OEMs and system apps). + --> + <permission android:name="android.permission.BIND_INLINE_SUGGESTION_RENDER_SERVICE" + android:protectionLevel="signature" /> + <!-- Must be required by a android.service.textclassifier.TextClassifierService, to ensure that only the system can bind to it. @SystemApi @hide This is not a third-party API (intended for OEMs and system apps). diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index bf801fc93809..e28ef0f920e9 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -57,6 +57,7 @@ import android.service.autofill.FillEventHistory; import android.service.autofill.FillEventHistory.Event; import android.service.autofill.FillResponse; import android.service.autofill.IAutoFillService; +import android.service.autofill.InlineSuggestionRenderService; import android.service.autofill.SaveInfo; import android.service.autofill.UserData; import android.util.ArrayMap; @@ -117,6 +118,7 @@ final class AutofillManagerServiceImpl private final LocalLog mUiLatencyHistory; private final LocalLog mWtfHistory; private final FieldClassificationStrategy mFieldClassificationStrategy; + private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService; /** * Apps disabled by the service; key is package name, value is when they will be enabled again. @@ -212,6 +214,17 @@ final class AutofillManagerServiceImpl sendStateToClients(/* resetClient= */ false); } updateRemoteAugmentedAutofillService(); + + final ComponentName componentName = RemoteInlineSuggestionRenderService + .getServiceComponentName(getContext()); + if (componentName != null) { + mRemoteInlineSuggestionRenderService = new RemoteInlineSuggestionRenderService( + getContext(), componentName, InlineSuggestionRenderService.SERVICE_INTERFACE, + mUserId, new InlineSuggestionRenderCallbacksImpl(), + mMaster.isBindInstantServiceAllowed(), mMaster.verbose); + } else { + mRemoteInlineSuggestionRenderService = null; + } return enabledChanged; } @@ -1542,6 +1555,21 @@ final class AutofillManagerServiceImpl return mFieldClassificationStrategy.getDefaultAlgorithm(); } + RemoteInlineSuggestionRenderService getRemoteInlineSuggestionRenderService() { + return mRemoteInlineSuggestionRenderService; + } + + private class InlineSuggestionRenderCallbacksImpl implements + RemoteInlineSuggestionRenderService.InlineSuggestionRenderCallbacks { + + @Override // from InlineSuggestionRenderCallbacksImpl + public void onServiceDied(@NonNull RemoteInlineSuggestionRenderService service) { + // Don't do anything; eventually the system will bind to it again... + Slog.w(TAG, "remote service died: " + service); + mRemoteInlineSuggestionRenderService = null; + } + } + @Override public String toString() { return "AutofillManagerServiceImpl: [userId=" + mUserId diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java new file mode 100644 index 000000000000..f9e08e683b6c --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2020 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.autofill; + +import static com.android.server.autofill.Helper.sVerbose; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.IBinder; +import android.service.autofill.IInlineSuggestionRenderService; +import android.service.autofill.IInlineSuggestionUiCallback; +import android.service.autofill.InlinePresentation; +import android.service.autofill.InlineSuggestionRenderService; +import android.util.Slog; + +import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService; + +final class RemoteInlineSuggestionRenderService extends + AbstractMultiplePendingRequestsRemoteService<RemoteInlineSuggestionRenderService, + IInlineSuggestionRenderService> { + + private static final String TAG = "RemoteInlineSuggestionRenderService"; + + private final int mIdleUnbindTimeoutMs = 5000; + + RemoteInlineSuggestionRenderService(Context context, ComponentName componentName, + String serviceInterface, int userId, InlineSuggestionRenderCallbacks callback, + boolean bindInstantServiceAllowed, boolean verbose) { + super(context, serviceInterface, componentName, userId, callback, + context.getMainThreadHandler(), + bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, verbose, + /* initialCapacity= */ 2); + + ensureBound(); + } + + @Override // from AbstractRemoteService + protected IInlineSuggestionRenderService getServiceInterface(@NonNull IBinder service) { + return IInlineSuggestionRenderService.Stub.asInterface(service); + } + + @Override // from AbstractRemoteService + protected long getTimeoutIdleBindMillis() { + return mIdleUnbindTimeoutMs; + } + + @Override // from AbstractRemoteService + protected void handleOnConnectedStateChanged(boolean connected) { + if (connected && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) { + scheduleUnbind(); + } + super.handleOnConnectedStateChanged(connected); + } + + public void ensureBound() { + scheduleBind(); + } + + /** + * Called by {@link Session} to generate a call to the + * {@link RemoteInlineSuggestionRenderService} to request rendering a slice . + */ + void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback, + @NonNull InlinePresentation presentation, int width, int height) { + scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height)); + } + + @Nullable + private static ServiceInfo getServiceInfo(Context context) { + final String packageName = + context.getPackageManager().getServicesSystemSharedLibraryPackageName(); + if (packageName == null) { + Slog.w(TAG, "no external services package!"); + return null; + } + + final Intent intent = new Intent(InlineSuggestionRenderService.SERVICE_INTERFACE); + intent.setPackage(packageName); + final ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + final ServiceInfo serviceInfo = resolveInfo == null ? null : resolveInfo.serviceInfo; + if (resolveInfo == null || serviceInfo == null) { + Slog.w(TAG, "No valid components found."); + return null; + } + + if (!Manifest.permission.BIND_INLINE_SUGGESTION_RENDER_SERVICE + .equals(serviceInfo.permission)) { + Slog.w(TAG, serviceInfo.name + " does not require permission " + + Manifest.permission.BIND_INLINE_SUGGESTION_RENDER_SERVICE); + return null; + } + + return serviceInfo; + } + + @Nullable + public static ComponentName getServiceComponentName(Context context) { + final ServiceInfo serviceInfo = getServiceInfo(context); + if (serviceInfo == null) return null; + + final ComponentName componentName = new ComponentName(serviceInfo.packageName, + serviceInfo.name); + + if (sVerbose) Slog.v(TAG, "getServiceComponentName(): " + componentName); + return componentName; + } + + interface InlineSuggestionRenderCallbacks + extends VultureCallback<RemoteInlineSuggestionRenderService> { + // NOTE: so far we don't need to notify the callback implementation + // (AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this + // callback interface is empty. + } +} |