1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.assist.classification; 18 19 import android.annotation.CallSuper; 20 import android.annotation.NonNull; 21 import android.annotation.SdkConstant; 22 import android.annotation.SystemApi; 23 import android.app.Service; 24 import android.content.ComponentName; 25 import android.content.Intent; 26 import android.os.BaseBundle; 27 import android.os.Build; 28 import android.os.CancellationSignal; 29 import android.os.IBinder; 30 import android.os.ICancellationSignal; 31 import android.os.OutcomeReceiver; 32 import android.os.RemoteException; 33 import android.util.Log; 34 35 /** 36 * A service using {@link android.app.assist.AssistStructure} to detect fields on the screen. 37 * Service may use classifiers to look at the un-stripped AssistStructure to make informed decision 38 * and classify the fields. 39 * 40 * Currently, it's used to detect the field types for the Autofill Framework to provide relevant 41 * autofill suggestions to the user. 42 * 43 * 44 * The methods are invoked on the binder threads. 45 * 46 * @hide 47 */ 48 @SystemApi 49 public abstract class FieldClassificationService extends Service { 50 51 private static final String TAG = FieldClassificationService.class.getSimpleName(); 52 53 static boolean sDebug = Build.IS_USER ? false : true; 54 static boolean sVerbose = false; 55 56 /** 57 * The {@link Intent} that must be declared as handled by the service. 58 * To be supported, the service must also require the 59 * {@link android.Manifest.permission#BIND_FIELD_CLASSIFICATION_SERVICE} permission so 60 * that other applications can not abuse it. 61 */ 62 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 63 public static final String SERVICE_INTERFACE = 64 "android.service.assist.classification.FieldClassificationService"; 65 66 // Used for metrics / debug only 67 private ComponentName mServiceComponentName; 68 69 private final class FieldClassificationServiceImpl 70 extends IFieldClassificationService.Stub { 71 72 @Override onConnected(boolean debug, boolean verbose)73 public void onConnected(boolean debug, boolean verbose) { 74 handleOnConnected(debug, verbose); 75 } 76 77 @Override onDisconnected()78 public void onDisconnected() { 79 handleOnDisconnected(); 80 } 81 82 @Override onFieldClassificationRequest( FieldClassificationRequest request, IFieldClassificationCallback callback)83 public void onFieldClassificationRequest( 84 FieldClassificationRequest request, IFieldClassificationCallback callback) { 85 handleOnClassificationRequest(request, callback); 86 } 87 }; 88 89 @CallSuper 90 @Override onCreate()91 public void onCreate() { 92 super.onCreate(); 93 BaseBundle.setShouldDefuse(true); 94 } 95 96 /** @hide */ 97 @Override onBind(Intent intent)98 public final IBinder onBind(Intent intent) { 99 if (SERVICE_INTERFACE.equals(intent.getAction())) { 100 mServiceComponentName = intent.getComponent(); 101 return new FieldClassificationServiceImpl().asBinder(); 102 } 103 Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent); 104 return null; 105 } 106 107 /** 108 * Called when the Android system connects to service. 109 * 110 * <p>You should generally do initialization here rather than in {@link #onCreate}. 111 */ onConnected()112 public void onConnected() { 113 } 114 115 /** 116 * Requests the service to handle field classification request. 117 * @param cancellationSignal signal for observing cancellation requests. The system will use 118 * this to notify you that the detection result is no longer needed and the service should 119 * stop handling this detection request in order to save resources. 120 * @param outcomeReceiver object used to notify the result of the request. Service <b>must</b> 121 * call {@link OutcomeReceiver<>#onResult(FieldClassificationResponse)}. 122 */ onClassificationRequest( @onNull FieldClassificationRequest request, @NonNull CancellationSignal cancellationSignal, @NonNull OutcomeReceiver<FieldClassificationResponse, Exception> outcomeReceiver)123 public abstract void onClassificationRequest( 124 @NonNull FieldClassificationRequest request, 125 @NonNull CancellationSignal cancellationSignal, 126 @NonNull OutcomeReceiver<FieldClassificationResponse, Exception> outcomeReceiver); 127 128 /** 129 * Called when the Android system disconnects from the service. 130 * 131 * <p> At this point this service may no longer be an active 132 * {@link FieldClassificationService}. 133 */ onDisconnected()134 public void onDisconnected() { 135 } 136 handleOnConnected(boolean debug, boolean verbose)137 private void handleOnConnected(boolean debug, boolean verbose) { 138 if (sDebug || debug) { 139 Log.d(TAG, "handleOnConnected(): debug=" + debug + ", verbose=" + verbose); 140 } 141 sDebug = debug; 142 sVerbose = verbose; 143 onConnected(); 144 } 145 handleOnDisconnected()146 private void handleOnDisconnected() { 147 onDisconnected(); 148 } 149 handleOnClassificationRequest( FieldClassificationRequest request, @NonNull IFieldClassificationCallback callback)150 private void handleOnClassificationRequest( 151 FieldClassificationRequest request, @NonNull IFieldClassificationCallback callback) { 152 153 final ICancellationSignal transport = CancellationSignal.createTransport(); 154 final CancellationSignal cancellationSignal = CancellationSignal.fromTransport(transport); 155 onClassificationRequest( 156 request, 157 cancellationSignal, 158 new OutcomeReceiver<FieldClassificationResponse, Exception>() { 159 @Override 160 public void onResult(FieldClassificationResponse result) { 161 try { 162 callback.onSuccess(result); 163 } catch (RemoteException e) { 164 e.rethrowFromSystemServer(); 165 } 166 } 167 @Override 168 public void onError(Exception e) { 169 try { 170 callback.onFailure(); 171 } catch (RemoteException ex) { 172 ex.rethrowFromSystemServer(); 173 } 174 } 175 }); 176 } 177 } 178 179