Bind AutoFillService on demand.
BUG: 31001899 Test: manually built and ran it Change-Id: Ied7028e41c273f5e30f88fc92f919249046877eb
This commit is contained in:
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <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()");
|
||||
public void onDisconnected() {
|
||||
if (DEBUG) Log.d(TAG, "onDisconnected()");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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).
|
||||
* <p>
|
||||
* This map is filled on demand in the following scenarios:
|
||||
* Entries on this cache are added on demand and removed when:
|
||||
* <ol>
|
||||
* <li>On start, it sets the value for the default user.
|
||||
* <li>When an auto-fill service app is removed, its entries are removed.
|
||||
* <li>When the current user changes.
|
||||
* <li>When the {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} changes.
|
||||
* <li>An auto-fill service app is removed.
|
||||
* <li>The {@link android.provider.Settings.Secure#AUTO_FILL_SERVICE} for an user change.
|
||||
* <li>It has not been interacted with for {@link #SERVICE_BINDING_LIFETIME_MS} ms.
|
||||
* </ol>
|
||||
*/
|
||||
// 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<AutoFillManagerServiceImpl> 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<AutoFillManagerServiceImpl> 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.
|
||||
*
|
||||
* <p>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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> mRequestHistory = new ArrayList<>();
|
||||
private final List<String> mRequestHistory = new LinkedList<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final List<IBinder> 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() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user