OSDN Git Service

Added a callback for AutoFillService.
authorFelipe Leme <felipeal@google.com>
Tue, 25 Oct 2016 21:57:11 +0000 (14:57 -0700)
committerFelipe Leme <felipeal@google.com>
Sat, 12 Nov 2016 00:02:55 +0000 (16:02 -0800)
So far AutoFillService only received the assist data from framework; in
this CL, it also offers a method where the auto-fill provider can send
the auto-fill data back to framework.

The workflow is:

- AFMSI calls a new AM method (requestAutoFillData(), instead
  of requestAssistContextExtras()).
- The assist receiver is located in the app, not on system service.
- AM uses a new request type (ASSIST_CONTEXT_AUTOFILL) to request the
  assist data to the activity.
- ViewStructure has a new setAutoFillId() method which is used to set an
  unique id for the view.
- View uses the accessibility id to implement the auto-fill id.
- When the activity fullfills the request, it creates an IAutoFillCallback
  remote object - that will be used to set the auto-fill fields - and
  returns it in the assist bundle (using the
  VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK key).
- The app-visible AutoFillService class offers an onFillRequest() method,
  which contains the assist data and a FillCallback used to handle it.

BUG: 31001899
Test: manually built and ran it

Change-Id: I3d208c14e81022dc96dd03f38bbe25a778b24a67

24 files changed:
Android.mk
api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/app/Activity.java
core/java/android/app/ActivityManager.java
core/java/android/app/ActivityManagerNative.java
core/java/android/app/ActivityThread.java
core/java/android/app/IActivityManager.aidl
core/java/android/app/assist/AssistStructure.java
core/java/android/service/autofill/AutoFillService.java
core/java/android/service/autofill/FillCallback.java [new file with mode: 0644]
core/java/android/service/autofill/FillableInputField.java [new file with mode: 0644]
core/java/android/service/autofill/IAutoFillCallback.aidl [new file with mode: 0644]
core/java/android/service/autofill/IAutoFillManagerService.aidl
core/java/android/service/autofill/IAutoFillService.aidl
core/java/android/service/voice/VoiceInteractionSession.java
core/java/android/view/View.java
core/java/android/view/ViewStructure.java
services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
services/autofill/java/com/android/server/autofill/AutoFillSession.java [deleted file]
services/core/java/com/android/server/am/ActivityManagerService.java

index bd04214..f6e4b0a 100644 (file)
@@ -251,6 +251,7 @@ LOCAL_SRC_FILES += \
        core/java/android/os/storage/IObbActionListener.aidl \
        core/java/android/security/IKeystoreService.aidl \
        core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl \
+       core/java/android/service/autofill/IAutoFillCallback.aidl \
        core/java/android/service/autofill/IAutoFillManagerService.aidl \
        core/java/android/service/autofill/IAutoFillService.aidl \
        core/java/android/service/carrier/ICarrierService.aidl \
index df8a0eb..3ae120a 100644 (file)
@@ -6265,6 +6265,7 @@ package android.app.assist {
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
+    method public int getAutoFillId();
     method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
     method public int getChildCount();
     method public java.lang.String getClassName();
@@ -34641,13 +34642,26 @@ package android.service.autofill {
   public abstract class AutoFillService extends android.app.Service {
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public void onNewSession(java.lang.String, android.os.Bundle, int, android.app.assist.AssistStructure);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public void onReady();
-    method public void onSessionFinished(java.lang.String);
     method public void onShutdown();
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
   }
 
+  public final class FillCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(android.service.autofill.FillCallback.FillData);
+  }
+
+  public static final class FillCallback.FillData {
+  }
+
+  public static class FillCallback.FillData.Builder {
+    ctor public FillCallback.FillData.Builder();
+    method public android.service.autofill.FillCallback.FillData build();
+    method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
+  }
+
 }
 
 package android.service.carrier {
index c6d08e4..b45ee69 100644 (file)
@@ -6456,6 +6456,7 @@ package android.app.assist {
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
+    method public int getAutoFillId();
     method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
     method public int getChildCount();
     method public java.lang.String getClassName();
@@ -37434,13 +37435,26 @@ package android.service.autofill {
   public abstract class AutoFillService extends android.app.Service {
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public void onNewSession(java.lang.String, android.os.Bundle, int, android.app.assist.AssistStructure);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public void onReady();
-    method public void onSessionFinished(java.lang.String);
     method public void onShutdown();
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
   }
 
+  public final class FillCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(android.service.autofill.FillCallback.FillData);
+  }
+
+  public static final class FillCallback.FillData {
+  }
+
+  public static class FillCallback.FillData.Builder {
+    ctor public FillCallback.FillData.Builder();
+    method public android.service.autofill.FillCallback.FillData build();
+    method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
+  }
+
 }
 
 package android.service.carrier {
index 0287612..e7037e6 100644 (file)
@@ -6281,6 +6281,7 @@ package android.app.assist {
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
+    method public int getAutoFillId();
     method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
     method public int getChildCount();
     method public java.lang.String getClassName();
@@ -34731,13 +34732,26 @@ package android.service.autofill {
   public abstract class AutoFillService extends android.app.Service {
     ctor public AutoFillService();
     method public final android.os.IBinder onBind(android.content.Intent);
-    method public void onNewSession(java.lang.String, android.os.Bundle, int, android.app.assist.AssistStructure);
+    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public void onReady();
-    method public void onSessionFinished(java.lang.String);
     method public void onShutdown();
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
   }
 
+  public final class FillCallback {
+    method public void onFailure(java.lang.CharSequence);
+    method public void onSuccess(android.service.autofill.FillCallback.FillData);
+  }
+
+  public static final class FillCallback.FillData {
+  }
+
+  public static class FillCallback.FillData.Builder {
+    ctor public FillCallback.FillData.Builder();
+    method public android.service.autofill.FillCallback.FillData build();
+    method public android.service.autofill.FillCallback.FillData.Builder setTextField(int, java.lang.String);
+  }
+
 }
 
 package android.service.carrier {
index b381339..6f53f13 100644 (file)
@@ -53,7 +53,6 @@ import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
-import android.hardware.input.InputManager;
 import android.media.AudioManager;
 import android.media.session.MediaController;
 import android.net.Uri;
@@ -70,6 +69,9 @@ import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.StrictMode;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.service.autofill.FillableInputField;
+import android.service.autofill.AutoFillService;
+import android.service.autofill.IAutoFillCallback;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -90,8 +92,6 @@ import android.view.ContextMenu.ContextMenuInfo;
 import android.view.ContextThemeWrapper;
 import android.view.DragAndDropPermissions;
 import android.view.DragEvent;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
@@ -113,9 +113,11 @@ import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.AdapterView;
+import android.widget.EditText;
 import android.widget.Toast;
 import android.widget.Toolbar;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.app.WindowDecorActionBar;
@@ -802,11 +804,13 @@ public class Activity extends ContextThemeWrapper
         private boolean mReleased;
         private boolean mUpdated;
     }
-    private final ArrayList<ManagedCursor> mManagedCursors =
-        new ArrayList<ManagedCursor>();
 
-    // protected by synchronized (this)
+    @GuardedBy("mManagedCursors")
+    private final ArrayList<ManagedCursor> mManagedCursors = new ArrayList<>();
+
+    @GuardedBy("this")
     int mResultCode = RESULT_CANCELED;
+    @GuardedBy("this")
     Intent mResultData = null;
 
     private TranslucentConversionListener mTranslucentCallback;
@@ -837,6 +841,9 @@ public class Activity extends ContextThemeWrapper
     private boolean mHasCurrentPermissionsRequest;
     private boolean mEatKeyUpEvent;
 
+    @GuardedBy("this")
+    private IAutoFillCallback mAutoFillCallback;
+
     private static native String getDlWarning();
 
     /** Return the intent that started this activity. */
@@ -1688,6 +1695,53 @@ public class Activity extends ContextThemeWrapper
     }
 
     /**
+     * Lazily gets the {@code IAutoFillCallback} for this activitity.
+     *
+     * <p>This callback is used by the {@link AutoFillService} app to auto-fill the activity fields.
+     */
+    IAutoFillCallback getAutoFillCallback() {
+        synchronized (this) {
+            if (mAutoFillCallback == null) {
+                mAutoFillCallback = new IAutoFillCallback.Stub() {
+                    @Override
+                    public void autofill(@SuppressWarnings("rawtypes") List fields)
+                            throws RemoteException {
+                        runOnUiThread(() -> {
+                            final View root = getWindow().getDecorView().getRootView();
+                            for (Object field : fields) {
+                                if (!(field instanceof FillableInputField)) {
+                                    Slog.w(TAG,  "autofill(): invalid type " + field.getClass());
+                                    continue;
+                                }
+                                FillableInputField autoFillField = (FillableInputField) field;
+                                final int viewId = autoFillField.getId();
+                                final View view = root.findViewByAccessibilityIdTraversal(viewId);
+                                // TODO: should handle other types of view as well, but that will
+                                // require:
+                                // - a new interface like AutoFillable
+                                // - a way for the views to define the type of the autofield value
+                                if ((view instanceof EditText)) {
+                                    ((EditText) view).setText(autoFillField.getValue());
+                                }
+                            }
+                        });
+                    }
+
+                    @Override
+                    public void showError(String message) {
+                        runOnUiThread(() -> {
+                            // TODO: temporary show a toast until it uses the Snack bar.
+                            Toast.makeText(Activity.this, "Auto-fill request failed: " + message,
+                                    Toast.LENGTH_LONG).show();
+                        });
+                    }
+                };
+            }
+        }
+        return mAutoFillCallback;
+    }
+
+    /**
      * Request the Keyboard Shortcuts screen to show up. This will trigger
      * {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity.
      */
@@ -5974,6 +6028,11 @@ public class Activity extends ContextThemeWrapper
             getWindow().peekDecorView().getViewRootImpl().dump(prefix, fd, writer, args);
         }
 
+        if (mAutoFillCallback != null) {
+            writer.print(prefix); writer.print("mAutoFillCallback: " );
+                    writer.println(mAutoFillCallback);
+        }
+
         mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
     }
 
index 4a39e4a..c55d3b8 100644 (file)
@@ -447,6 +447,9 @@ public class ActivityManager {
     /** @hide requestType for assist context: generate full AssistStructure. */
     public static final int ASSIST_CONTEXT_FULL = 1;
 
+    /** @hide requestType for assist context: generate full AssistStructure for auto-fill. */
+    public static final int ASSIST_CONTEXT_AUTOFILL = 2;
+
     /** @hide Flag for registerUidObserver: report changes in process state. */
     public static final int UID_OBSERVER_PROCSTATE = 1<<0;
 
index 199fda4..cabdd4b 100644 (file)
@@ -109,4 +109,4 @@ public abstract class ActivityManagerNative {
     static public void noteAlarmFinish(PendingIntent ps, int sourceUid, String tag) {
         ActivityManager.noteAlarmFinish(ps, sourceUid, tag);
     }
-}
\ No newline at end of file
+}
index a3414f4..d41a7e8 100644 (file)
@@ -88,6 +88,8 @@ import android.provider.Downloads;
 import android.provider.Settings;
 import android.security.NetworkSecurityPolicy;
 import android.security.net.config.NetworkSecurityConfigProvider;
+import android.service.autofill.IAutoFillCallback;
+import android.service.voice.VoiceInteractionSession;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
@@ -2881,16 +2883,24 @@ public final class ActivityThread {
                 mLastAssistStructures.remove(i);
             }
         }
+        // Filling for auto-fill has a few differences:
+        // - it does not need an AssistContent
+        // - it does not call onProvideAssistData()
+        // - it needs an IAutoFillCallback
+        boolean forAutofill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTOFILL;
+
         Bundle data = new Bundle();
         AssistStructure structure = null;
-        AssistContent content = new AssistContent();
+        AssistContent content = forAutofill ? null : new AssistContent();
         ActivityClientRecord r = mActivities.get(cmd.activityToken);
         Uri referrer = null;
         if (r != null) {
             r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
-            r.activity.onProvideAssistData(data);
+            if (!forAutofill) {
+                r.activity.onProvideAssistData(data);
+            }
             referrer = r.activity.onProvideReferrer();
-            if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
+            if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutofill) {
                 structure = new AssistStructure(r.activity);
                 Intent activityIntent = r.activity.getIntent();
                 if (activityIntent != null && (r.window == null ||
@@ -2900,11 +2910,21 @@ public final class ActivityThread {
                     intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                             | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
                     intent.removeUnsafeExtras();
-                    content.setDefaultIntent(intent);
+                    if (forAutofill) {
+                        IAutoFillCallback autoFillCallback = r.activity.getAutoFillCallback();
+                        data.putBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK,
+                                autoFillCallback.asBinder());
+                    } else {
+                        content.setDefaultIntent(intent);
+                    }
                 } else {
-                    content.setDefaultIntent(new Intent());
+                    if (!forAutofill) {
+                        content.setDefaultIntent(new Intent());
+                    }
+                }
+                if (!forAutofill) {
+                    r.activity.onProvideAssistContent(content);
                 }
-                r.activity.onProvideAssistContent(content);
             }
         }
         if (structure == null) {
index 9fe34c0..d63d37b 100644 (file)
@@ -564,6 +564,8 @@ interface IActivityManager {
     void moveStackToDisplay(int stackId, int displayId) = 403;
     void enterPictureInPictureModeWithAspectRatio(in IBinder token, float aspectRatio) = 404;
     void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio) = 405;
+    boolean requestAutoFillData(in IResultReceiver receiver, in Bundle receiverExtras,
+            in IBinder activityToken) = 406;
 
     // Please keep these transaction codes the same -- they are also
     // sent by C++ code. when a new method is added, use the next available transaction id.
index 6b720c0..a6f2366 100644 (file)
@@ -515,6 +515,7 @@ public class AssistStructure implements Parcelable {
         String mIdPackage;
         String mIdType;
         String mIdEntry;
+        int mAutoFillId = View.NO_ID;
         int mX;
         int mY;
         int mScrollX;
@@ -539,6 +540,7 @@ public class AssistStructure implements Parcelable {
         static final int FLAGS_ACTIVATED = 0x00002000;
         static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
 
+        static final int FLAGS_HAS_AUTO_FILL_ID = 0x80000000;
         static final int FLAGS_HAS_MATRIX = 0x40000000;
         static final int FLAGS_HAS_ALPHA = 0x20000000;
         static final int FLAGS_HAS_ELEVATION = 0x10000000;
@@ -582,6 +584,9 @@ public class AssistStructure implements Parcelable {
                     }
                 }
             }
+            if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) {
+                mAutoFillId = in.readInt();
+            }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
                 mX = in.readInt();
                 mY = in.readInt();
@@ -637,6 +642,9 @@ public class AssistStructure implements Parcelable {
             if (mId != View.NO_ID) {
                 flags |= FLAGS_HAS_ID;
             }
+            if (mAutoFillId != View.NO_ID) {
+                flags |= FLAGS_HAS_AUTO_FILL_ID;
+            }
             if ((mX&~0x7fff) != 0 || (mY&~0x7fff) != 0
                     || (mWidth&~0x7fff) != 0 | (mHeight&~0x7fff) != 0) {
                 flags |= FLAGS_HAS_LARGE_COORDS;
@@ -681,6 +689,9 @@ public class AssistStructure implements Parcelable {
                     }
                 }
             }
+            if ((flags&FLAGS_HAS_AUTO_FILL_ID) != 0) {
+                out.writeInt(mAutoFillId);
+            }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
                 out.writeInt(mX);
                 out.writeInt(mY);
@@ -751,6 +762,16 @@ public class AssistStructure implements Parcelable {
         }
 
         /**
+         * Returns the id that can be used to auto-fill the view.
+         *
+         * <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not
+         * for assist.
+         */
+        public int getAutoFillId() {
+            return mAutoFillId;
+        }
+
+        /**
          * Returns the left edge of this view, in pixels, relative to the left edge of its parent.
          */
         public int getLeft() {
@@ -1322,6 +1343,11 @@ public class AssistStructure implements Parcelable {
         public Rect getTempRect() {
             return mAssist.mTmpRect;
         }
+
+        @Override
+        public void setAutoFillId(int autoFillId) {
+            mNode.mAutoFillId = autoFillId;
+        }
     }
 
     /** @hide */
index 14ce009..83b2065 100644 (file)
  */
 package android.service.autofill;
 
+import android.annotation.IntDef;
 import android.annotation.SdkConstant;
+import android.app.Activity;
 import android.app.Service;
 import android.app.assist.AssistStructure;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
+import android.service.voice.VoiceInteractionSession;
 import android.util.Log;
 
 import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Top-level service of the current auto-fill service for a given user.
+ *
+ * <p>Apps providing auto-fill capabilities must extend this service.
  */
-// TODO: expand documentation
 public abstract class AutoFillService extends Service {
 
-    private static final String TAG = "AutoFillService";
-    private static final boolean DEBUG = true; // TODO: set to false once stable
+    static final String TAG = "AutoFillService";
+    static final boolean DEBUG = true; // TODO: set to false once stable
 
     /**
      * The {@link Intent} that must be declared as handled by the service.
@@ -47,11 +57,23 @@ public abstract class AutoFillService extends Service {
     public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService";
 
     private static final int MSG_READY = 1;
-    private static final int MSG_NEW_SESSION = 2;
-    private static final int MSG_SESSION_FINISHED = 3;
-    private static final int MSG_SHUTDOWN = 4;
+    private static final int MSG_AUTO_FILL = 2;
+    private static final int MSG_SHUTDOWN = 3;
 
-    // TODO: add metadata?
+    private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
+        @Override
+        public void send(int resultCode, Bundle resultData) throws RemoteException {
+            final AssistStructure structure = resultData
+                    .getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
+
+            final IBinder binder = resultData
+                    .getBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK);
+
+            mHandlerCaller
+                .obtainMessageOO(MSG_AUTO_FILL, structure, binder).sendToTarget();
+        }
+
+    };
 
     private final IAutoFillService mInterface = new IAutoFillService.Stub() {
         @Override
@@ -60,15 +82,8 @@ public abstract class AutoFillService extends Service {
         }
 
         @Override
-        public void newSession(String token, Bundle data, int flags,
-                AssistStructure structure) {
-            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOOO(MSG_NEW_SESSION,
-                    flags, token, data, structure));
-        }
-
-        @Override
-        public void finishSession(String token) {
-            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_SESSION_FINISHED, token));
+        public IResultReceiver getAssistReceiver() {
+            return mAssistReceiver;
         }
 
         @Override
@@ -85,17 +100,11 @@ public abstract class AutoFillService extends Service {
                 case MSG_READY: {
                     onReady();
                     break;
-                } case MSG_NEW_SESSION: {
+                } case MSG_AUTO_FILL: {
                     final SomeArgs args = (SomeArgs) msg.obj;
-                    final int flags = args.argi1;
-                    final String token = (String) args.arg1;
-                    final Bundle data = (Bundle) args.arg2;
-                    final AssistStructure assistStructure = (AssistStructure) args.arg3;
-                    onNewSession(token, data, flags, assistStructure);
-                    break;
-                } case MSG_SESSION_FINISHED: {
-                    final String token = (String) msg.obj;
-                    onSessionFinished(token);
+                    final AssistStructure structure = (AssistStructure) args.arg1;
+                    final IBinder binder = (IBinder) args.arg2;
+                    autoFillActivity(structure, binder);
                     break;
                 } case MSG_SHUTDOWN: {
                     onShutdown();
@@ -121,6 +130,7 @@ public abstract class AutoFillService extends Service {
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
             return mInterface.asBinder();
         }
+        Log.w(TAG, "Tried to bind to wrong intent: " + intent);
         return null;
     }
 
@@ -129,41 +139,26 @@ public abstract class AutoFillService extends Service {
      * to receive interaction from it.
      *
      * <p>You should generally do initialization here rather than in {@link #onCreate}.
-     *
-     * <p>Sub-classes should call it first, since it sets the reference to the sytem-server service.
      */
-    // TODO: rename to onConnected() / add onDisconnected()?
+    // TODO: rename to onConnect() / update javadoc
     public void onReady() {
         if (DEBUG) Log.d(TAG, "onReady()");
     }
 
     /**
-     * Called to receive data from the application that the user was requested auto-fill for.
+     * Handles an auto-fill request.
      *
-     * @param token unique token identifying the auto-fill session, it should be used when providing
-     * the auto-filled fields.
-     * @param data Arbitrary data supplied by the app through
-     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
-     * May be {@code null} if data has been disabled by the user or device policy.
-     * @param startFlags currently always 0.
-     * @param structure If available, the structure definition of all windows currently
-     * displayed by the app.  May be {@code null} if auto-fill data has been disabled by the user
-     * or device policy; will be an empty stub if the application has disabled auto-fill
-     * by marking its window as secure.
+     * @param structure {@link Activity}'s view structure .
+     * @param cancellationSignal signal for observing cancel requests.
+     * @param callback object used to fulllfill the request.
      */
-    @SuppressWarnings("unused")
-    // TODO: take the factory approach where this method return a session, and move the callback
-    // methods (like autofill()) to the session.
-    public void onNewSession(String token, Bundle data, int startFlags, AssistStructure structure) {
-        if (DEBUG) Log.d(TAG, "onNewSession(): token=" + token);
-    }
+    public abstract void onFillRequest(AssistStructure structure,
+            CancellationSignal cancellationSignal, FillCallback callback);
 
-    /**
-     * Called when an auto-fill session is finished.
-     */
-    @SuppressWarnings("unused")
-    public void onSessionFinished(String token) {
-        if (DEBUG) Log.d(TAG, "onSessionFinished(): token=" + token);
+    private void autoFillActivity(AssistStructure structure, IBinder binder) {
+        final FillCallback callback = new FillCallback(binder);
+        // TODO: hook up the cancelationSignal
+        onFillRequest(structure, new CancellationSignal(), callback);
     }
 
     /**
@@ -172,7 +167,9 @@ public abstract class AutoFillService extends Service {
      *
      * <p> At this point this service may no longer be an active {@link AutoFillService}.
      */
+    // TODO: rename to onDisconnected() / update javadoc
     public void onShutdown() {
         if (DEBUG) Log.d(TAG, "onShutdown()");
     }
+
 }
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
new file mode 100644 (file)
index 0000000..bdcc93b
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 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 android.service.autofill.AutoFillService.DEBUG;
+import static android.service.autofill.AutoFillService.TAG;
+
+import android.app.Activity;
+import android.app.assist.AssistStructure.ViewNode;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Handles auto-fill requests from the {@link AutoFillService} into the {@link Activity} being
+ * auto-filled.
+ */
+public final class FillCallback {
+
+    private final IAutoFillCallback mCallback;
+
+    /** @hide */
+    FillCallback(IBinder binder) {
+        mCallback = IAutoFillCallback.Stub.asInterface(binder);
+    }
+
+    /**
+     * Auto-fills the {@link Activity}.
+     *
+     * @throws RuntimeException if an error occurred while auto-filling it.
+     */
+    public void onSuccess(FillData data) {
+        if (DEBUG) Log.d(TAG, "onSuccess(): data=" + data);
+
+        Preconditions.checkArgument(data != null, "data cannot be null");
+
+        try {
+            mCallback.autofill(data.asList());
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Notifies the activity that the auto-fill request failed.
+     */
+    public void onFailure(CharSequence message) {
+        if (DEBUG) Log.d(TAG, "onFailure(): message=" + message);
+
+        Preconditions.checkArgument(message != null, "message cannot be null");
+
+        try {
+            mCallback.showError(message.toString());
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Data used to fill the fields of an {@link Activity}.
+     *
+     * <p>This class is immutable.
+     */
+    public static final class FillData {
+
+        private final List<FillableInputField> mList;
+
+        private FillData(Builder builder) {
+            final int size = builder.mFields.size();
+            final List<FillableInputField> list = new ArrayList<>(size);
+            for (int i = 0; i < size; i++) {
+                list.add(builder.mFields.valueAt(i));
+            }
+            mList = Collections.unmodifiableList(list);
+            // TODO: use FastImmutableArraySet or a similar structure instead?
+        }
+
+        /**
+         * Gets the response as a {@code List} so it can be used in a binder call.
+         */
+        List<FillableInputField> asList() {
+            return mList;
+        }
+
+        @Override
+        public String toString() {
+            return "[AutoFillResponse: " + mList + "]";
+        }
+
+        /**
+         * Builder for {@link FillData} objects.
+         *
+         * <p>Typical usage:
+         *
+         * <pre class="prettyprint">
+         * FillCallback.FillData data = new FillCallback.FillData.Builder()
+         *     .setTextField(id1, "value 1")
+         *     .setTextField(id2, "value 2")
+         *     .build()
+         * </pre>
+         */
+        public static class Builder {
+            private final SparseArray<FillableInputField> mFields = new SparseArray<>();
+
+            /**
+             * Auto-fills a text field.
+             *
+             * @param id view id as returned by {@link ViewNode#getAutoFillId()}.
+             * @param text text to be auto-filled.
+             * @return same builder so it can be chained.
+             */
+            public Builder setTextField(int id, String text) {
+                mFields.put(id, FillableInputField.forText(id, text));
+                return this;
+            }
+
+            /**
+             * Builds a new {@link FillData} instance.
+             */
+            public FillData build() {
+                return new FillData(this);
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/autofill/FillableInputField.java b/core/java/android/service/autofill/FillableInputField.java
new file mode 100644 (file)
index 0000000..62950b4
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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.app.assist.AssistStructure.ViewNode;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a view field that can be auto-filled.
+ *
+ * <p>Currently only text-fields are supported, so the value of the field can be obtained through
+ * {@link #getValue()}.
+ *
+ * @hide
+ */
+public final class FillableInputField implements Parcelable {
+
+    private final int mId;
+    private final String mValue;
+
+    private FillableInputField(int id, String value) {
+        mId = id;
+        mValue = value;
+    }
+
+    private FillableInputField(Parcel parcel) {
+        mId = parcel.readInt();
+        mValue = parcel.readString();
+    }
+
+    /**
+     * Gets the view id as returned by {@link ViewNode#getAutoFillId()}.
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Gets the value of this field.
+     */
+    public String getValue() {
+        return mValue;
+
+    }
+
+    @Override
+    public String toString() {
+        return "[AutoFillField: " + mId + "=" + mValue + "]";
+    }
+
+    /**
+     * Creates an {@code AutoFillField} for a text field.
+     *
+     * @param id view id as returned by {@link ViewNode#getAutoFillId()}.
+     * @param text value to be auto-filled.
+     */
+    public static FillableInputField forText(int id, String text) {
+        return new FillableInputField(id, text);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mId);
+        parcel.writeString(mValue);
+    }
+
+    public static final Parcelable.Creator<FillableInputField> CREATOR =
+            new Parcelable.Creator<FillableInputField>() {
+        @Override
+        public FillableInputField createFromParcel(Parcel source) {
+            return new FillableInputField(source);
+        }
+
+        @Override
+        public FillableInputField[] newArray(int size) {
+            return new FillableInputField[size];
+        }
+    };
+}
diff --git a/core/java/android/service/autofill/IAutoFillCallback.aidl b/core/java/android/service/autofill/IAutoFillCallback.aidl
new file mode 100644 (file)
index 0000000..db8ef96
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 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 java.util.List;
+
+/**
+ * @hide
+ */
+interface IAutoFillCallback {
+    void autofill(in List values);
+    void showError(String message);
+}
index 2c06234..a91841b 100644 (file)
@@ -19,36 +19,20 @@ package android.service.autofill;
 import android.os.Bundle;
 
 /**
- * Intermediator between apps being auto-filled and auto-fill service implementations.
+ * Mediator between apps being auto-filled and auto-fill service implementations.
  *
  * {@hide}
  */
 interface IAutoFillManagerService {
 
     /**
-     * Starts an auto-fill session for the top activities for a given user.
-     *
-     * It's used to start a new session from system affordances.
+     * Request auto-fill on the top activity of a given user.
      *
      * @param userId user handle.
-     * @param args the bundle to pass as arguments to the voice interaction session.
-     * @param flags flags indicating optional session behavior.
      * @param activityToken optional token of activity that needs to be on top.
      *
-     * @return session token, or null if session was not created (for example, if the activity's
-     *         user does not have an auto-fill service associated with).
-     */
-     // TODO: pass callback providing an onAutoFill() method
-    String startSession(int userId, in Bundle args, int flags, IBinder activityToken);
-
-    /**
-     * Finishes an auto-fill session.
-     *
-     * @param userId user handle.
-     * @param token session token.
-     *
-     * @return true if session existed and was finished.
+     * @return whether the request succeeded  (for example, if the activity's
+     *         user does not have an auto-fill service associated with, it will return false).
      */
-    boolean finishSession(int userId, String token);
-
+    boolean requestAutoFill(int userId, IBinder activityToken);
 }
index 73d8d5d..dca3c70 100644 (file)
 
 package android.service.autofill;
 
-import android.os.Bundle;
 import android.app.assist.AssistStructure;
+import android.os.Bundle;
+import android.service.autofill.IAutoFillCallback;
+import com.android.internal.os.IResultReceiver;
 
 /**
  * @hide
  */
-oneway interface IAutoFillService {
+interface IAutoFillService {
+    // TODO: rename to onConnected() / onDisconnected()
     void ready();
-    void newSession(String token, in Bundle data, int flags, in AssistStructure structure);
-    void finishSession(String token);
     void shutdown();
+    IResultReceiver getAssistReceiver();
 }
index e9bbc2d..12aed25 100644 (file)
@@ -119,6 +119,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
     public static final String KEY_CONTENT = "content";
     /** @hide */
     public static final String KEY_RECEIVER_EXTRAS = "receiverExtras";
+    /** @hide */
+    public static final String KEY_AUTO_FILL_CALLBACK = "autoFillCallback";
 
     final Context mContext;
     final HandlerCaller mHandlerCaller;
index 441f330..02a8521 100644 (file)
@@ -6703,6 +6703,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         } else {
             structure.setId(id, null, null, null);
         }
+
+        // The auto-fill id needs to be unique, but its value doesn't matter, so it's better to
+        // reuse the accessibility id to save space.
+        structure.setAutoFillId(getAccessibilityViewId());
+
         structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
         if (!hasIdentityMatrix()) {
             structure.setTransformation(getMatrix());
index 2e4ba74..e9ff9d0 100644 (file)
@@ -275,4 +275,7 @@ public abstract class ViewStructure {
 
     /** @hide */
     public abstract Rect getTempRect();
+
+    /** @hide */
+    public abstract void setAutoFillId(int autoFillId);
 }
index 3b41877..8b37756 100644 (file)
@@ -218,20 +218,11 @@ public final class AutoFillManagerService extends SystemService {
     final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub {
 
         @Override
-        public String startSession(int userId, Bundle args, int flags, IBinder activityToken) {
+        public boolean requestAutoFill(int userId, IBinder activityToken) {
             mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
             synchronized (mLock) {
-                return getImplOrThrowLocked(userId).startSession(args, flags, activityToken);
-            }
-        }
-
-        @Override
-        public boolean finishSession(int userId, String token) {
-            mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-
-            synchronized (mLock) {
-                return getImplOrThrowLocked(userId).finishSessionLocked(token);
+                return getImplOrThrowLocked(userId).requestAutoFill(activityToken);
             }
         }
 
@@ -244,12 +235,6 @@ public final class AutoFillManagerService extends SystemService {
                         + ", uid=" + Binder.getCallingUid());
                 return;
             }
-            if (args.length > 0) {
-                if ("--sessions".equals(args[0])) {
-                    dumpSessions(pw);
-                    return;
-                }
-            }
             synchronized (mLock) {
                 pw.print("mEnableService: "); pw.println(mEnableService);
                 pw.print("mSafeMode: "); pw.println(mSafeMode);
@@ -268,20 +253,6 @@ public final class AutoFillManagerService extends SystemService {
             }
         }
 
-        private void dumpSessions(PrintWriter pw) {
-            boolean foundOne = false;
-            synchronized (mLock) {
-                final int size = mImplByUser.size();
-                for (int i = 0; i < size; i++) {
-                    final AutoFillManagerServiceImpl impl = mImplByUser.valueAt(i);
-                    foundOne |= impl.dumpSessionsLocked("", pw);
-                }
-            }
-            if (!foundOne) {
-                pw.println("No active sessions");
-            }
-        }
-
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
index c780062..ae687da 100644 (file)
@@ -16,7 +16,9 @@
 
 package com.android.server.autofill;
 
+import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.IActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -24,6 +26,7 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.icu.text.DateFormat;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -41,16 +44,14 @@ import com.android.server.LocalServices;
 import com.android.server.autofill.AutoFillManagerService.AutoFillManagerServiceStub;
 
 import java.io.PrintWriter;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
-import java.util.Map;
-import java.util.UUID;
 
 /**
  * Bridge between the {@code system_server}'s {@link AutoFillManagerService} and the
  * app's {@link IAutoFillService} implementation.
  *
- * <p>It keeps a list of auto-fill sessions for a specifc user.
  */
 final class AutoFillManagerServiceImpl {
 
@@ -61,13 +62,15 @@ final class AutoFillManagerServiceImpl {
     final ComponentName mComponent;
 
     private final Context mContext;
+    private final IActivityManager mAm;
     private final Object mLock;
     private final AutoFillManagerServiceStub mServiceStub;
     private final AutoFillServiceInfo mInfo;
 
-    // Map of sessions keyed by session tokens.
-    @GuardedBy("mLock")
-    private Map<String, AutoFillSession> mSessions = new HashMap<>();
+    // TODO: improve its usage
+    // - set maximum number of entries
+    // - disable on low-memory devices.
+    private final List<String> mRequestHistory = new ArrayList<>();
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -75,7 +78,7 @@ final class AutoFillManagerServiceImpl {
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
                 final String reason = intent.getStringExtra("reason");
                 if (DEBUG) Slog.d(TAG, "close system dialogs: " + reason);
-                // TODO: close any pending UI like account selection
+                // TODO: close any pending UI like account selection (or remove this receiver)
             }
         }
     };
@@ -113,8 +116,9 @@ final class AutoFillManagerServiceImpl {
         mServiceStub = stub;
         mUser = user;
         mComponent = component;
+        mAm = ActivityManager.getService();
 
-        AutoFillServiceInfo info;
+        final AutoFillServiceInfo info;
         try {
             info = new AutoFillServiceInfo(component, mUser);
         } catch (PackageManager.NameNotFoundException e) {
@@ -150,19 +154,15 @@ final class AutoFillManagerServiceImpl {
         if (DEBUG) Slog.d(TAG, "Bound to " + mComponent);
     }
 
-    String startSession(Bundle args, int flags, IBinder activityToken) {
-
+    boolean requestAutoFill(IBinder activityToken) {
         if (!mBound) {
             // TODO: should it bind on demand? Or perhaps always run when on on low-memory?
-            Slog.w(TAG, "startSession() failed because it's not bound to service");
-            return null;
+            Slog.w(TAG, "requestAutoFill() failed because it's not bound to service");
+            return false;
         }
 
-        // TODO: session should have activity ids, so same session is reused when called again
-        // for the same activity.
-
         // TODO: activityToken should probably not be null, but we need to wait until the UI is
-        // triggering the call (for now it's trough 'adb shell cmd autofill start session'
+        // triggering the call (for now it's trough 'adb shell cmd autofill request'
         if (activityToken == null) {
             // Let's get top activities from all visible stacks.
 
@@ -175,40 +175,43 @@ final class AutoFillManagerServiceImpl {
                 Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities);
             if (topActivities.isEmpty()) {
                 Slog.w(TAG, "Could not get top activity");
-                return null;
+                return false;
             }
             activityToken = topActivities.get(0);
         }
 
         synchronized (mLock) {
-            return startSessionLocked(args, flags, activityToken);
+            return requestAutoFillLocked(activityToken);
         }
     }
 
-    // TODO: remove args and flags if not needed?
-    private String startSessionLocked(Bundle args, int flags, IBinder activityToken) {
-
-        final String sessionToken = UUID.randomUUID().toString();
-
-        if (DEBUG) Slog.d(TAG, "Starting session for user " + mUser
-                + ": sessionToken=" + sessionToken + ", activityToken=" + activityToken);
+    private boolean requestAutoFillLocked(IBinder activityToken) {
+        mRequestHistory.add(
+                DateFormat.getDateTimeInstance().format(new Date()) + " - " + activityToken);
+        if (DEBUG) Slog.d(TAG, "Requesting for user " + mUser + " and activity " + activityToken);
 
-        final AutoFillSession session =
-                new AutoFillSession(mService, mLock, sessionToken, activityToken);
-        session.startLocked();
-        mSessions.put(sessionToken, session);
-
-        return sessionToken;
-    }
+        // Sanity check
+        if (mService == null) {
+            Slog.w(TAG, "requestAutoFillLocked(: service is null");
+            return false;
+        }
 
-    // TODO: need a way to automatically call it when the activity is destroyed.
-    boolean finishSessionLocked(String token) {
-        if (DEBUG) Slog.d(TAG, "Removing session " + token + " for user " + mUser);
-        final AutoFillSession session = mSessions.remove(token);
-        if (session != null) {
-            session.finishLocked();
+        /*
+         * TODO: apply security checks below:
+         * - checks if disabled by secure settings / device policy
+         * - log operation using noteOp()
+         * - check flags
+         * - display disclosure if needed
+         */
+        try {
+            // TODO: add MetricsLogger call
+            if (!mAm.requestAutoFillData(mService.getAssistReceiver(), null, activityToken)) {
+                return false;
+            }
+        } catch (RemoteException e) {
+            // Should happen, it's a local call.
         }
-        return session != null;
+        return true;
     }
 
     void shutdownLocked() {
@@ -253,23 +256,15 @@ final class AutoFillManagerServiceImpl {
             mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), prefix + prefix);
         }
 
-        if (!dumpSessionsLocked(prefix, pw)) {
-            pw.print(prefix); pw.print("No active sessions for user "); pw.println(mUser);
-        }
-    }
-
-    boolean dumpSessionsLocked(String prefix, PrintWriter pw) {
-        if (mSessions.isEmpty()) {
-            return false;
-        }
-
-        pw.print(mSessions.size());pw.println(" active sessions:");
-        final String sessionPrefix = prefix + prefix;
-        for (AutoFillSession session : mSessions.values()) {
-            pw.println();
-            session.dumpLocked(sessionPrefix, pw);
+        if (mRequestHistory.isEmpty()) {
+            pw.print(prefix); pw.println("No history");
+        } else {
+            pw.print(prefix); pw.println("History:");
+            final String prefix2 = prefix + prefix;
+            for (int i = 0; i < mRequestHistory.size(); i++) {
+                pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mRequestHistory.get(i));
+            }
         }
-        return true;
     }
 
     @Override
index 4e08ed6..c9037fc 100644 (file)
@@ -40,10 +40,8 @@ public final class AutoFillManagerServiceShellCommand extends ShellCommand {
         final PrintWriter pw = getOutPrintWriter();
         try {
             switch (cmd) {
-                case "start":
-                    return runStart(pw);
-                case "finish":
-                    return runFinish(pw);
+                case "request":
+                    return requestAutoFill();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -60,62 +58,16 @@ public final class AutoFillManagerServiceShellCommand extends ShellCommand {
             pw.println("  help");
             pw.println("    Prints this help text.");
             pw.println("");
-            pw.println("  start session [--user USER_ID]");
-            pw.println("    Starts an auto-fill session. "
-                    + "Prints 'token:SESSION_TOKEN if successful, or error message");
+            pw.println("  request [--user USER_ID]");
+            pw.println("    Request auto-fill on the top activity. ");
             pw.println("");
-            pw.println("  finish session <TOKEN> [--user USER_ID]");
-            pw.println("    Finishes a session with the given TOKEN. "
-                    + "Prints empty string if successful, or error message.");
-            pw.println("");
-        }
-    }
-
-    private int runStart(PrintWriter pw) throws RemoteException {
-        final String type = getNextArg();
-        if (type == null) {
-            pw.println("Error: didn't specify type of data to start");
-            return -1;
-        }
-        switch (type) {
-            case "session":
-                return startAutoFillSession(pw);
-        }
-        pw.println("Error: unknown start type '" + type + "'");
-        return -1;
-    }
-
-    private int runFinish(PrintWriter pw) throws RemoteException {
-        final String type = getNextArg();
-        if (type == null) {
-            pw.println("Error: didn't specify type of data to finish");
-            return -1;
-        }
-        switch (type) {
-            case "session":
-                return finishAutoFillSession(pw);
         }
-        pw.println("Error: unknown finish type '" + type + "'");
-        return -1;
-    }
-
-    private int startAutoFillSession(PrintWriter pw) throws RemoteException {
-        final int userId = getUserIdFromArgs();
-        final String token = mService.startSession(userId, null, 0, null);
-        pw.print("token:"); pw.println(token);
-        return 0;
     }
 
-    private int finishAutoFillSession(PrintWriter pw) throws RemoteException {
-        final String token = getNextArgRequired();
+    private int requestAutoFill() throws RemoteException {
         final int userId = getUserIdFromArgs();
-
-        boolean finished = mService.finishSession(userId, token);
-        if (!finished) {
-            pw.println("No such session");
-            return 1;
-        }
-        return 0;
+        final boolean ok = mService.requestAutoFill(userId, null);
+        return ok ? 0 : 1;
     }
 
     private int getUserIdFromArgs() {
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillSession.java b/services/autofill/java/com/android/server/autofill/AutoFillSession.java
deleted file mode 100644 (file)
index 44637c3..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.app.ActivityManager;
-import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
-import android.app.assist.AssistStructure;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.service.autofill.AutoFillService;
-import android.service.autofill.IAutoFillService;
-import android.service.voice.VoiceInteractionSession;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.IResultReceiver;
-
-import java.io.PrintWriter;
-
-/**
- * An auto-fill session between the system's {@link AutoFillManagerServiceImpl} and the provider's
- * {@link AutoFillService} implementation.
- */
-final class AutoFillSession {
-
-    private static final String TAG = "AutoFillSession";
-
-    private static final boolean FOCUSED = true;
-    private static final boolean NEW_SESSION_ID = true;
-
-    private final IAutoFillService mService;
-    private final String mSessionToken;
-    private final IBinder mActivityToken;
-    private final Object mLock;
-    private final IActivityManager mAm;
-
-    private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() {
-        @Override
-        public void send(int resultCode, Bundle resultData) throws RemoteException {
-            synchronized (mLock) {
-                mPendingResponse = false;
-                mAssistResponse = resultData;
-                deliverSessionDataLocked();
-            }
-        }
-    };
-
-    // Assist data is filled asynchronously.
-    @GuardedBy("mLock")
-    private Bundle mAssistResponse;
-    @GuardedBy("mLock")
-    private boolean mPendingResponse;
-
-    AutoFillSession(IAutoFillService service, Object lock, String sessionToken,
-            IBinder activityToken) {
-        mService = service;
-        mSessionToken = sessionToken;
-        mActivityToken = activityToken;
-        mLock = lock;
-        mAm = ActivityManagerNative.getDefault();
-    }
-
-    void startLocked() {
-        /*
-         * TODO: apply security checks below:
-         * - checks if disabled by secure settings / device policy
-         * - log operation using noteOp()
-         * - check flags
-         * - display disclosure if needed
-         */
-        mAssistResponse = null;
-        mPendingResponse = true;
-        try {
-            // TODO: add MetricsLogger call
-            if (!mAm.requestAssistContextExtras(ActivityManager.ASSIST_CONTEXT_FULL,
-                    mAssistReceiver, (Bundle) null, mActivityToken, FOCUSED, NEW_SESSION_ID)) {
-                mPendingResponse = false;
-                Slog.w(TAG, "requestAssistContextExtras() rejected");
-            }
-        } catch (RemoteException e) {
-            // Should happen, it's a local call.
-        }
-    }
-
-    void finishLocked() {
-        try {
-            mService.finishSession(mSessionToken);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "auto-fill service failed to finish session " + mSessionToken, e);
-        }
-    }
-
-    private void deliverSessionDataLocked() {
-        if (mAssistResponse == null) {
-            Slog.w(TAG, "No assist data for session " + mSessionToken);
-            return;
-        }
-
-        final Bundle assistData = mAssistResponse.getBundle(VoiceInteractionSession.KEY_DATA);
-        final AssistStructure structure =
-                mAssistResponse.getParcelable(VoiceInteractionSession.KEY_STRUCTURE);
-        try {
-            mService.newSession(mSessionToken, assistData, 0, structure);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "auto-fill service failed to start session " + mSessionToken, e);
-        } finally {
-            mPendingResponse = false;
-            // We could set mAssistResponse to null here, but we don't so it's shown on dump()
-        }
-    }
-
-    void dumpLocked(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("mSessionToken="); pw.println(mSessionToken);
-        pw.print(prefix); pw.print("mActivityToken="); pw.println(mActivityToken);
-        pw.print(prefix); pw.print("mPendingResponse="); pw.println(mPendingResponse);
-        pw.print(prefix); pw.print("mAssistResponse="); pw.println(mAssistResponse);
-    }
-
-}
index d60f115..e080fd9 100644 (file)
@@ -12142,6 +12142,15 @@ public class ActivityManagerService extends IActivityManager.Stub
                 != null;
     }
 
+    @Override
+    public boolean requestAutoFillData(IResultReceiver receiver, Bundle receiverExtras,
+            IBinder activityToken) {
+        return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null, receiver,
+                receiverExtras, activityToken, true, true,
+                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT)
+                != null;
+    }
+
     private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
             IResultReceiver receiver, Bundle receiverExtras, IBinder activityToken,
             boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout) {
@@ -12266,6 +12275,12 @@ public class ActivityManagerService extends IActivityManager.Stub
                 sendBundle.putParcelable(VoiceInteractionSession.KEY_CONTENT, pae.content);
                 sendBundle.putBundle(VoiceInteractionSession.KEY_RECEIVER_EXTRAS,
                         pae.receiverExtras);
+                IBinder autoFillCallback =
+                        extras.getBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK);
+                if (autoFillCallback != null) {
+                    sendBundle.putBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK,
+                            autoFillCallback);
+                }
             }
         }
         if (sendReceiver != null) {