From dfa7fbc8702fae62e6b3f78c4d9245995baee04e Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Fri, 11 Nov 2016 12:20:09 -0800 Subject: [PATCH] Bind AutoFillService on demand. BUG: 31001899 Test: manually built and ran it Change-Id: Ied7028e41c273f5e30f88fc92f919249046877eb --- api/current.txt | 4 +- api/system-current.txt | 4 +- api/test-current.txt | 4 +- .../service/autofill/AutoFillService.java | 51 ++-- .../service/autofill/FillCallback.java | 3 - .../autofill/IAutoFillManagerService.aidl | 5 +- .../service/autofill/IAutoFillService.aidl | 5 +- .../autofill/AutoFillManagerService.java | 236 +++++++++--------- .../autofill/AutoFillManagerServiceImpl.java | 134 ++++++---- .../AutoFillManagerServiceShellCommand.java | 4 +- 10 files changed, 248 insertions(+), 202 deletions(-) diff --git a/api/current.txt b/api/current.txt index 3ae120af6843d..8a68076f870bc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -34642,9 +34642,9 @@ 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 onConnected(); + method public void onDisconnected(); method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback); - method public void onReady(); - method public void onShutdown(); field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; } diff --git a/api/system-current.txt b/api/system-current.txt index b45ee69ba0ec3..a8cee670d4279 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -37435,9 +37435,9 @@ 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 onConnected(); + method public void onDisconnected(); method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback); - method public void onReady(); - method public void onShutdown(); field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; } diff --git a/api/test-current.txt b/api/test-current.txt index e7037e69d45ca..56da49c915543 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -34732,9 +34732,9 @@ 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 onConnected(); + method public void onDisconnected(); method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.CancellationSignal, android.service.autofill.FillCallback); - method public void onReady(); - method public void onShutdown(); field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; } diff --git a/core/java/android/service/autofill/AutoFillService.java b/core/java/android/service/autofill/AutoFillService.java index 83b2065f44fe8..3734831b0db29 100644 --- a/core/java/android/service/autofill/AutoFillService.java +++ b/core/java/android/service/autofill/AutoFillService.java @@ -15,7 +15,6 @@ */ package android.service.autofill; -import android.annotation.IntDef; import android.annotation.SdkConstant; import android.app.Activity; import android.app.Service; @@ -34,9 +33,6 @@ 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. * @@ -56,9 +52,9 @@ public abstract class AutoFillService extends Service { @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) public static final String SERVICE_INTERFACE = "android.service.autofill.AutoFillService"; - private static final int MSG_READY = 1; - private static final int MSG_AUTO_FILL = 2; - private static final int MSG_SHUTDOWN = 3; + private static final int MSG_CONNECT = 1; + private static final int MSG_AUTO_FILL_ACTIVITY = 2; + private static final int MSG_DISCONNECT = 3; private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { @Override @@ -70,15 +66,15 @@ public abstract class AutoFillService extends Service { .getBinder(VoiceInteractionSession.KEY_AUTO_FILL_CALLBACK); mHandlerCaller - .obtainMessageOO(MSG_AUTO_FILL, structure, binder).sendToTarget(); + .obtainMessageOO(MSG_AUTO_FILL_ACTIVITY, structure, binder).sendToTarget(); } }; private final IAutoFillService mInterface = new IAutoFillService.Stub() { @Override - public void ready() { - mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_READY)); + public void onConnected() { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CONNECT)); } @Override @@ -87,8 +83,8 @@ public abstract class AutoFillService extends Service { } @Override - public void shutdown() { - mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_SHUTDOWN)); + public void onDisconnected() { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DISCONNECT)); } }; @@ -97,17 +93,17 @@ public abstract class AutoFillService extends Service { @Override public void executeMessage(Message msg) { switch (msg.what) { - case MSG_READY: { - onReady(); + case MSG_CONNECT: { + onConnected(); break; - } case MSG_AUTO_FILL: { + } case MSG_AUTO_FILL_ACTIVITY: { final SomeArgs args = (SomeArgs) msg.obj; final AssistStructure structure = (AssistStructure) args.arg1; final IBinder binder = (IBinder) args.arg2; - autoFillActivity(structure, binder); + requestAutoFill(structure, binder); break; - } case MSG_SHUTDOWN: { - onShutdown(); + } case MSG_DISCONNECT: { + onDisconnected(); break; } default: { Log.w(TAG, "MyCallbacks received invalid message type: " + msg); @@ -135,14 +131,12 @@ public abstract class AutoFillService extends Service { } /** - * Called during service initialization to tell you when the system is ready - * to receive interaction from it. + * Called when the Android System connects to service. * *

You should generally do initialization here rather than in {@link #onCreate}. */ - // TODO: rename to onConnect() / update javadoc - public void onReady() { - if (DEBUG) Log.d(TAG, "onReady()"); + public void onConnected() { + if (DEBUG) Log.d(TAG, "onConnected()"); } /** @@ -155,21 +149,18 @@ public abstract class AutoFillService extends Service { public abstract void onFillRequest(AssistStructure structure, CancellationSignal cancellationSignal, FillCallback callback); - private void autoFillActivity(AssistStructure structure, IBinder binder) { + private void requestAutoFill(AssistStructure structure, IBinder binder) { final FillCallback callback = new FillCallback(binder); // TODO: hook up the cancelationSignal onFillRequest(structure, new CancellationSignal(), callback); } /** - * Called during service de-initialization to tell you when the system is shutting the - * service down. + * Called when the Android System disconnects from the service. * *

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()"); + public void onDisconnected() { + if (DEBUG) Log.d(TAG, "onDisconnected()"); } - } diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java index bdcc93b0397c1..23084409569f6 100644 --- a/core/java/android/service/autofill/FillCallback.java +++ b/core/java/android/service/autofill/FillCallback.java @@ -62,9 +62,6 @@ public final class FillCallback { } } - /** - * Notifies the activity that the auto-fill request failed. - */ public void onFailure(CharSequence message) { if (DEBUG) Log.d(TAG, "onFailure(): message=" + message); diff --git a/core/java/android/service/autofill/IAutoFillManagerService.aidl b/core/java/android/service/autofill/IAutoFillManagerService.aidl index a91841b984534..cab073f7045f6 100644 --- a/core/java/android/service/autofill/IAutoFillManagerService.aidl +++ b/core/java/android/service/autofill/IAutoFillManagerService.aidl @@ -30,9 +30,6 @@ interface IAutoFillManagerService { * * @param userId user handle. * @param activityToken optional token of activity that needs to be on top. - * - * @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 requestAutoFill(int userId, IBinder activityToken); + void requestAutoFill(int userId, IBinder activityToken); } diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl index dca3c700c0a99..e3e911c392158 100644 --- a/core/java/android/service/autofill/IAutoFillService.aidl +++ b/core/java/android/service/autofill/IAutoFillService.aidl @@ -25,8 +25,7 @@ import com.android.internal.os.IResultReceiver; * @hide */ interface IAutoFillService { - // TODO: rename to onConnected() / onDisconnected() - void ready(); - void shutdown(); + void onConnected(); + void onDisconnected(); IResultReceiver getAssistReceiver(); } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java index 8b3775657da0a..9157e83a3dd18 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java @@ -33,16 +33,21 @@ import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Message; import android.os.Parcel; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.service.autofill.IAutoFillManagerService; import android.text.TextUtils; +import android.text.format.DateUtils; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; @@ -62,7 +67,13 @@ import java.io.PrintWriter; public final class AutoFillManagerService extends SystemService { private static final String TAG = "AutoFillManagerService"; - private static final boolean DEBUG = true; // TODO: change to false once stable + static final boolean DEBUG = true; // TODO: change to false once stable + + private static final long SERVICE_BINDING_LIFETIME_MS = 5 * DateUtils.MINUTE_IN_MILLIS; + + private static final int ARG_NOT_USED = 0; + + protected static final int MSG_UNBIND = 1; private final AutoFillManagerServiceStub mServiceStub; private final Context mContext; @@ -70,30 +81,36 @@ public final class AutoFillManagerService extends SystemService { private final Object mLock = new Object(); - @GuardedBy("mLock") - private boolean mSafeMode; + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UNBIND: + removeStaleServiceForUser(msg.arg1); + return; + default: + Slog.w(TAG, "Invalid message: " + msg); + } + } + + }; /** - * Map of {@link AutoFillManagerServiceImpl} per user id. + * Cache of {@link AutoFillManagerServiceImpl} per user id. *

* It has to be mapped by user id because the same current user could have simultaneous sessions - * associated to different user profiles (for example, in a multi-window environment). + * associated to different user profiles (for example, in a multi-window environment or when + * device has work profiles). *

- * This map is filled on demand in the following scenarios: + * Entries on this cache are added on demand and removed when: *

    - *
  1. On start, it sets the value for the default user. - *
  2. When an auto-fill service app is removed, its entries are removed. - *
  3. When the current user changes. - *
  4. When the {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} changes. + *
  5. An auto-fill service app is removed. + *
  6. The {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} for an user change. + *
  7. It has not been interacted with for {@link #SERVICE_BINDING_LIFETIME_MS} ms. *
*/ - // TODO: make sure all cases listed above are handled - // TODO: should entries be removed when there is no section and have not be used for a while? @GuardedBy("mLock") - private SparseArray mImplByUser = new SparseArray<>(); - - // TODO: should disable it on low-memory devices? if not, this attribute should be removed... - private final boolean mEnableService = true; + private SparseArray mServicesCache = new SparseArray<>(); public AutoFillManagerService(Context context) { super(context); @@ -105,124 +122,121 @@ public final class AutoFillManagerService extends SystemService { @Override public void onStart() { - if (DEBUG) - Slog.d(TAG, "onStart(): binding as " + AUTO_FILL_MANAGER_SERVICE); + if (DEBUG) Slog.d(TAG, "onStart(): binding as " + AUTO_FILL_MANAGER_SERVICE); publishBinderService(AUTO_FILL_MANAGER_SERVICE, mServiceStub); } - // TODO: refactor so it's bound on demand, in which case it can use isSafeMode() from PM. @Override public void onBootPhase(int phase) { if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { - systemRunning(isSafeMode()); + new SettingsObserver(BackgroundThread.getHandler()); } } - // TODO: refactor so it's bound on demand, in which case it can use isSafeMode() from PM. - @Override - public void onStartUser(int userHandle) { - if (DEBUG) Slog.d(TAG, "onStartUser(): userHandle=" + userHandle); - - updateImplementationIfNeeded(userHandle, false); - } - - @Override - public void onUnlockUser(int userHandle) { - if (DEBUG) Slog.d(TAG, "onUnlockUser(): userHandle=" + userHandle); - - updateImplementationIfNeeded(userHandle, false); - } - - @Override - public void onSwitchUser(int userHandle) { - if (DEBUG) Slog.d(TAG, "onSwitchUser(): userHandle=" + userHandle); - - updateImplementationIfNeeded(userHandle, false); - } - - private void systemRunning(boolean safeMode) { - if (DEBUG) Slog.d(TAG, "systemRunning(): safeMode=" + safeMode); - - // TODO: register a PackageMonitor - new SettingsObserver(BackgroundThread.getHandler()); - - synchronized (mLock) { - mSafeMode = safeMode; - updateImplementationIfNeededLocked(ActivityManager.getCurrentUser(), false); - } - } - - private void updateImplementationIfNeeded(int user, boolean force) { - synchronized (mLock) { - updateImplementationIfNeededLocked(user, force); - } - } - - private void updateImplementationIfNeededLocked(int user, boolean force) { - if (DEBUG) - Slog.d(TAG, "updateImplementationIfNeededLocked(" + user + ", " + force + ")"); - - if (mSafeMode) { - if (DEBUG) Slog.d(TAG, "skipping on safe mode"); - return; - } - - final String curService = Settings.Secure.getStringForUser( - mResolver, Settings.Secure.AUTO_FILL_SERVICE, user); - if (DEBUG) - Slog.d(TAG, "Current service settings for user " + user + ": " + curService); + private AutoFillManagerServiceImpl newServiceForUser(int userId) { ComponentName serviceComponent = null; ServiceInfo serviceInfo = null; - if (!TextUtils.isEmpty(curService)) { + final String componentName = Settings.Secure.getStringForUser( + mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId); + if (!TextUtils.isEmpty(componentName)) { try { - serviceComponent = ComponentName.unflattenFromString(curService); + serviceComponent = ComponentName.unflattenFromString(componentName); serviceInfo = - AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, user); + AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId); } catch (RuntimeException | RemoteException e) { - Slog.wtf(TAG, "Bad auto-fill service name " + curService, e); - serviceComponent = null; - serviceInfo = null; + Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e); + return null; } } - final AutoFillManagerServiceImpl impl = mImplByUser.get(user); - if (DEBUG) Slog.d(TAG, "Current impl: " + impl + " component: " + serviceComponent - + " info: " + serviceInfo); + if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component=" + + serviceComponent + ", info: " + serviceInfo); + if (serviceInfo == null) { + Slog.w(TAG, "no service info for " + serviceComponent); + return null; + } + return new AutoFillManagerServiceImpl(this, mContext, mLock, FgThread.getHandler(), userId, + serviceInfo.applicationInfo.uid, serviceComponent, SERVICE_BINDING_LIFETIME_MS); + } - if (force || impl == null || !impl.mComponent.equals(serviceComponent)) { - if (impl != null) { - impl.shutdownLocked(); - } - if (serviceInfo != null) { - final AutoFillManagerServiceImpl newImpl = new AutoFillManagerServiceImpl(mContext, - mLock, mServiceStub, FgThread.getHandler(), user, serviceComponent); - if (DEBUG) Slog.d(TAG, "Setting impl for user " + user + " as: " + newImpl); - mImplByUser.put(user, newImpl); - newImpl.startLocked(); - } else { - if (DEBUG) Slog.d(TAG, "Removing impl for user " + user + ": " + impl); - mImplByUser.remove(user); + /** + * Gets the service instance for an user. + * + *

First it tries to return the existing instance from the cache; if it's not cached, it + * creates a new instance and caches it. + */ + private AutoFillManagerServiceImpl getServiceForUserLocked(int userId) { + AutoFillManagerServiceImpl service = mServicesCache.get(userId); + if (service != null) { + if (DEBUG) Log.d(TAG, "reusing cached service for userId " + userId); + service.setLifeExpectancy(SERVICE_BINDING_LIFETIME_MS); + } else { + service = newServiceForUser(userId); + if (service == null) { + // Already logged + return null; } + if (DEBUG) Log.d(TAG, "creating new cached service for userId " + userId); + service.startLocked(); + mServicesCache.put(userId, service); + } + // Keep service connection alive for a while, in case user needs to interact with it + // (for example, to save the data that was inputted in) + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UNBIND, userId, ARG_NOT_USED), + SERVICE_BINDING_LIFETIME_MS); + return service; + } + + /** + * Removes a cached service, but respecting its TTL. + */ + private void removeStaleServiceForUser(int userId) { + synchronized (mLock) { + removeCachedService(userId, false); } } - // TODO: might need to return null instead of throw exception - private AutoFillManagerServiceImpl getImplOrThrowLocked(int userId) { - final AutoFillManagerServiceImpl impl = mImplByUser.get(userId); - if (impl == null) { - throw new IllegalStateException("no auto-fill service for user " + userId); + /** + * Removes a cached service, even if it has TTL. + */ + void removeCachedServiceForUserLocked(int userId) { + removeCachedService(userId, true); + } + + private void removeCachedService(int userId, boolean force) { + if (DEBUG) Log.d(TAG, "removing cached service for userId " + userId); + final AutoFillManagerServiceImpl service = mServicesCache.get(userId); + if (service == null) { + Log.w(TAG, "removeCachedServiceForUser(): no cached service for userId " + userId); + return; } - return impl; + if (!force) { + // Check TTL first. + final long now = SystemClock.uptimeMillis(); + if (service.mEstimateTimeOfDeath > now) { + if (DEBUG) { + final StringBuilder msg = new StringBuilder("service has some TTL left: "); + TimeUtils.formatDuration(service.mEstimateTimeOfDeath - now, msg); + Log.d(TAG, msg.toString()); + } + return; + } + } + mServicesCache.delete(userId); + service.stopLocked(); } final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub { @Override - public boolean requestAutoFill(int userId, IBinder activityToken) { + public void requestAutoFill(int userId, IBinder activityToken) { mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG); synchronized (mLock) { - return getImplOrThrowLocked(userId).requestAutoFill(activityToken); + final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); + if (service != null) { + service.requestAutoFill(activityToken); + } } } @@ -236,17 +250,15 @@ public final class AutoFillManagerService extends SystemService { return; } synchronized (mLock) { - pw.print("mEnableService: "); pw.println(mEnableService); - pw.print("mSafeMode: "); pw.println(mSafeMode); - final int size = mImplByUser.size(); - pw.print("Number of implementations: "); + final int size = mServicesCache.size(); + pw.print("Cached services: "); if (size == 0) { pw.println("none"); } else { pw.println(size); for (int i = 0; i < size; i++) { - pw.print("\nImplementation at index "); pw.println(i); - final AutoFillManagerServiceImpl impl = mImplByUser.valueAt(i); + pw.print("\nService at index "); pw.println(i); + final AutoFillManagerServiceImpl impl = mServicesCache.valueAt(i); impl.dumpLocked(" ", pw); } } @@ -267,14 +279,14 @@ public final class AutoFillManagerService extends SystemService { super(handler); ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.AUTO_FILL_SERVICE), false, this, - UserHandle.USER_ALL); + Settings.Secure.AUTO_FILL_SERVICE), false, this, UserHandle.USER_ALL); } @Override public void onChange(boolean selfChange, Uri uri, int userId) { + if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId); synchronized (mLock) { - updateImplementationIfNeededLocked(userId, false); + removeCachedServiceForUserLocked(userId); } } } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java index ae687da43cd0f..e409cb072ef51 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java @@ -16,6 +16,8 @@ package com.android.server.autofill; +import static com.android.server.autofill.AutoFillManagerService.DEBUG; + import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.IActivityManager; @@ -27,10 +29,11 @@ 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.DeadObjectException; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.service.autofill.AutoFillService; import android.service.autofill.AutoFillServiceInfo; @@ -38,14 +41,15 @@ import android.service.autofill.IAutoFillService; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Slog; +import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; -import com.android.server.autofill.AutoFillManagerService.AutoFillManagerServiceStub; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Date; +import java.util.LinkedList; import java.util.List; /** @@ -56,21 +60,23 @@ import java.util.List; final class AutoFillManagerServiceImpl { private static final String TAG = "AutoFillManagerServiceImpl"; - private static final boolean DEBUG = true; // TODO: change to false once stable - - final int mUser; - final ComponentName mComponent; + private final int mUserId; + private final int mUid; + private final ComponentName mComponent; private final Context mContext; private final IActivityManager mAm; private final Object mLock; - private final AutoFillManagerServiceStub mServiceStub; private final AutoFillServiceInfo mInfo; + private final AutoFillManagerService mManagerService; // TODO: improve its usage // - set maximum number of entries // - disable on low-memory devices. - private final List mRequestHistory = new ArrayList<>(); + private final List mRequestHistory = new LinkedList<>(); + + @GuardedBy("mLock") + private final List mQueuedRequests = new LinkedList<>(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -90,9 +96,16 @@ final class AutoFillManagerServiceImpl { synchronized (mLock) { mService = IAutoFillService.Stub.asInterface(service); try { - mService.ready(); + mService.onConnected(); } catch (RemoteException e) { - Slog.w(TAG, "Exception on service.ready(): " + e); + Slog.w(TAG, "Exception on service.onConnected(): " + e); + return; + } + if (!mQueuedRequests.isEmpty()) { + if (DEBUG) Log.d(TAG, "queued requests:" + mQueuedRequests.size()); + } + for (IBinder activityToken : mQueuedRequests) { + requestAutoFillLocked(activityToken, false); } } } @@ -100,7 +113,10 @@ final class AutoFillManagerServiceImpl { @Override public void onServiceDisconnected(ComponentName name) { if (DEBUG) Log.d(TAG, name + " disconnected"); - mService = null; + synchronized (mLock) { + mService = null; + mManagerService.removeCachedServiceForUserLocked(mUserId); + } } }; @@ -109,18 +125,23 @@ final class AutoFillManagerServiceImpl { private boolean mBound; private boolean mValid; - AutoFillManagerServiceImpl(Context context, Object lock, AutoFillManagerServiceStub stub, - Handler handler, int user, ComponentName component) { + // Estimated time when the service will be evicted from the cache. + long mEstimateTimeOfDeath; + + AutoFillManagerServiceImpl(AutoFillManagerService managerService, Context context, Object lock, + Handler handler, int userId, int uid,ComponentName component, long ttl) { + mManagerService = managerService; mContext = context; mLock = lock; - mServiceStub = stub; - mUser = user; + mUserId = userId; + mUid = uid; mComponent = component; mAm = ActivityManager.getService(); + setLifeExpectancy(ttl); final AutoFillServiceInfo info; try { - info = new AutoFillServiceInfo(component, mUser); + info = new AutoFillServiceInfo(component, mUserId); } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Auto-fill service not found: " + component, e); mInfo = null; @@ -140,13 +161,18 @@ final class AutoFillManagerServiceImpl { mContext.registerReceiver(mBroadcastReceiver, filter, null, handler); } + void setLifeExpectancy(long ttl) { + mEstimateTimeOfDeath = SystemClock.uptimeMillis() + ttl; + } + void startLocked() { if (DEBUG) Slog.d(TAG, "startLocked()"); final Intent intent = new Intent(AutoFillService.SERVICE_INTERFACE); intent.setComponent(mComponent); mBound = mContext.bindServiceAsUser(intent, mConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUser)); + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, new UserHandle(mUserId)); + if (!mBound) { Slog.w(TAG, "Failed binding to auto-fill service " + mComponent); return; @@ -154,11 +180,12 @@ final class AutoFillManagerServiceImpl { if (DEBUG) Slog.d(TAG, "Bound to " + mComponent); } - boolean requestAutoFill(IBinder activityToken) { - if (!mBound) { - // TODO: should it bind on demand? Or perhaps always run when on on low-memory? - Slog.w(TAG, "requestAutoFill() failed because it's not bound to service"); - return false; + void requestAutoFill(IBinder activityToken) { + synchronized (mLock) { + if (!mBound) { + Slog.w(TAG, "requestAutoFill() failed because it's not bound to service"); + return; + } } // TODO: activityToken should probably not be null, but we need to wait until the UI is @@ -175,25 +202,28 @@ final class AutoFillManagerServiceImpl { Slog.d(TAG, "Top activities (" + topActivities.size() + "): " + topActivities); if (topActivities.isEmpty()) { Slog.w(TAG, "Could not get top activity"); - return false; + return; } activityToken = topActivities.get(0); } + final String historyItem = + DateFormat.getDateTimeInstance().format(new Date()) + " - " + activityToken; synchronized (mLock) { - return requestAutoFillLocked(activityToken); + mRequestHistory.add(historyItem); + requestAutoFillLocked(activityToken, true); } } - 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); - - // Sanity check + private void requestAutoFillLocked(IBinder activityToken, boolean queueIfNecessary) { if (mService == null) { - Slog.w(TAG, "requestAutoFillLocked(: service is null"); - return false; + if (!queueIfNecessary) { + Slog.w(TAG, "requestAutoFillLocked(): service is null"); + return; + } + if (DEBUG) Slog.d(TAG, "requestAutoFill(): service not set yet, queuing it"); + mQueuedRequests.add(activityToken); + return; } /* @@ -206,23 +236,30 @@ final class AutoFillManagerServiceImpl { try { // TODO: add MetricsLogger call if (!mAm.requestAutoFillData(mService.getAssistReceiver(), null, activityToken)) { - return false; + // TODO: might need a way to warn user (perhaps a new method on AutoFillService). + Slog.w(TAG, "failed to request auto-fill data for " + activityToken); } } catch (RemoteException e) { // Should happen, it's a local call. } - return true; } - void shutdownLocked() { - if (DEBUG) Slog.d(TAG, "shutdownLocked()"); + void stopLocked() { + if (DEBUG) Slog.d(TAG, "stopLocked()"); + // Sanity check. + if (mService == null) { + Log.w(TAG, "service already null on shutdown"); + return; + } try { - if (mService != null) { - mService.shutdown(); - } + mService.onDisconnected(); } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in shutdown", e); + if (! (e instanceof DeadObjectException)) { + Slog.w(TAG, "Exception calling service.onDisconnected(): " + e); + } + } finally { + mService = null; } if (mBound) { @@ -245,10 +282,14 @@ final class AutoFillManagerServiceImpl { return; } - pw.print(prefix); pw.print("mUser="); pw.println(mUser); + pw.print(prefix); pw.print("mUserId="); pw.println(mUserId); + pw.print(prefix); pw.print("mUid="); pw.println(mUid); pw.print(prefix); pw.print("mComponent="); pw.println(mComponent.flattenToShortString()); pw.print(prefix); pw.print("mBound="); pw.println(mBound); pw.print(prefix); pw.print("mService="); pw.println(mService); + pw.print(prefix); pw.print("mEstimateTimeOfDeath="); + TimeUtils.formatDuration(mEstimateTimeOfDeath, SystemClock.uptimeMillis(), pw); + pw.println(); if (DEBUG) { // ServiceInfo dump is too noisy and redundant (it can be obtained through other dumps) @@ -265,11 +306,20 @@ final class AutoFillManagerServiceImpl { pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mRequestHistory.get(i)); } } + if (mQueuedRequests.isEmpty()) { + pw.print(prefix); pw.println("No queued requests"); + } else { + pw.print(prefix); pw.println("Queued requests:"); + final String prefix2 = prefix + prefix; + for (int i = 0; i < mQueuedRequests.size(); i++) { + pw.print(prefix2); pw.print(i); pw.print(": "); pw.println(mQueuedRequests.get(i)); + } + } } @Override public String toString() { - return "[AutoFillManagerServiceImpl: user=" + mUser + return "[AutoFillManagerServiceImpl: userId=" + mUserId + ", uid=" + mUid + ", component=" + mComponent.flattenToShortString() + "]"; } } diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java index c9037fc9df907..6406b8ac9a318 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java @@ -66,8 +66,8 @@ public final class AutoFillManagerServiceShellCommand extends ShellCommand { private int requestAutoFill() throws RemoteException { final int userId = getUserIdFromArgs(); - final boolean ok = mService.requestAutoFill(userId, null); - return ok ? 0 : 1; + mService.requestAutoFill(userId, null); + return 0; } private int getUserIdFromArgs() {