Refactored AutofillManager service classes into common code.

Autofill has the following workflow:

- AutofillManager talks to an IAutofillManagerService service located on
  system_server
- AutofillManagerService implements the IAutofillManagerService and delegates
  each call to the AutofillManagerServiceImpl associated with the given Android user
- AutofillManagerServiceImpl uses a RemoteFillService class that is
  responsible for binding to the service that is provided by a 3rd-party
- Plus a lot of other plumbing, like getting the name of the package that
  provides the service using Settings, listening to Settings changes, etc...

A lot of this "plumbing" is common to other system services
(like TextClassificationManager), so it makes sense to move this logic to common
code that can be used.

This CL refactors the "main" service classes (AutofillManagerService and
AutofillManagerServiceImpl), while RemoteFillService will be refactored later.

Bug: 117779333
Test: atest CtsAutoFillServiceTestCases

Change-Id: I19bae47b72096e66bd51c3cd6b68f9d3cf8ea708
This commit is contained in:
Felipe Leme
2018-10-15 10:45:01 -07:00
parent c8c634f3d6
commit ea95a6d790
7 changed files with 1023 additions and 580 deletions

View File

@@ -21,14 +21,11 @@ import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sPartitionMaxCount;
import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.content.BroadcastReceiver;
@@ -38,13 +35,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteCallback;
@@ -53,7 +47,6 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
import android.service.autofill.FillEventHistory;
import android.service.autofill.UserData;
@@ -63,7 +56,6 @@ import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManagerInternal;
@@ -73,14 +65,12 @@ import android.view.autofill.IAutoFillManagerClient;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.AbstractMasterSystemService;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.autofill.ui.AutoFillUI;
import java.io.FileDescriptor;
@@ -98,10 +88,13 @@ import java.util.Objects;
* {@link AutofillManagerServiceImpl} per user; the real work is done by
* {@link AutofillManagerServiceImpl} itself.
*/
public final class AutofillManagerService extends SystemService {
public final class AutofillManagerService
extends AbstractMasterSystemService<AutofillManagerServiceImpl> {
private static final String TAG = "AutofillManagerService";
private static final Object sLock = AutofillManagerService.class;
static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
private static final char COMPAT_PACKAGE_DELIMITER = ':';
@@ -109,27 +102,28 @@ public final class AutofillManagerService extends SystemService {
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '[';
private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']';
private final Context mContext;
/**
* Maximum number of partitions that can be allowed in a session.
*
* <p>Can be modified using {@code cmd autofill set max_partitions} or through
* {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
*/
@GuardedBy("sLock")
private static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;
/**
* Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
* value from resources.
*
* <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
* {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
*/
@GuardedBy("sLock")
private static int sVisibleDatasetsMaxCount = 0;
private final AutoFillUI mUi;
private final Object mLock = new Object();
/**
* 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 or when
* device has work profiles).
*/
@GuardedBy("mLock")
private SparseArray<AutofillManagerServiceImpl> mServicesCache = new SparseArray<>();
/**
* Users disabled due to {@link UserManager} restrictions.
*/
@GuardedBy("mLock")
private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray();
private final LocalLog mRequestsHistory = new LocalLog(20);
private final LocalLog mUiLatencyHistory = new LocalLog(20);
private final LocalLog mWtfHistory = new LocalLog(50);
@@ -148,22 +142,19 @@ public final class AutofillManagerService extends SystemService {
// beneath it is brought back to top. Ideally, we should just hide the UI and
// bring it back when the activity resumes.
synchronized (mLock) {
for (int i = 0; i < mServicesCache.size(); i++) {
mServicesCache.valueAt(i).destroyFinishedSessionsLocked();
}
visitServicesLocked((s) -> s.destroyFinishedSessionsLocked());
}
mUi.hideAll(null);
}
}
};
// TODO(b/117779333): move to superclass / create super-class for ShellCommand
@GuardedBy("mLock")
private boolean mAllowInstantService;
public AutofillManagerService(Context context) {
super(context);
mContext = context;
super(context, UserManager.DISALLOW_AUTOFILL);
mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
setLogLevelFromSettings();
@@ -172,199 +163,91 @@ public final class AutofillManagerService extends SystemService {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
context.registerReceiver(mBroadcastReceiver, filter, null, FgThread.getHandler());
}
// Hookup with UserManager to disable service when necessary.
final UserManager um = context.getSystemService(UserManager.class);
final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
final List<UserInfo> users = um.getUsers();
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
final boolean disabled = umi.getUserRestriction(userId, UserManager.DISALLOW_AUTOFILL);
if (disabled) {
Slog.i(TAG, "Disabling Autofill for user " + userId);
mDisabledUsers.put(userId, disabled);
}
@Override // from MasterSystemService
protected String getServiceSettingsProperty() {
return Settings.Secure.AUTOFILL_SERVICE;
}
@Override // from MasterSystemService
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@NonNull ContentObserver observer) {
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_LOGGING_LEVEL), false, observer,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, observer,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, observer,
UserHandle.USER_ALL);
}
@Override // from MasterSystemService
protected void onSettingsChanged(int userId, @NonNull String property) {
switch (property) {
case Settings.Global.AUTOFILL_LOGGING_LEVEL:
setLogLevelFromSettings();
break;
case Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE:
setMaxPartitionsFromSettings();
break;
case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
setMaxVisibleDatasetsFromSettings();
break;
default:
Slog.w(TAG, "Unexpected property (" + property + "); updating cache instead");
// fall through
case Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
synchronized (mLock) {
updateCachedServiceLocked(userId);
}
}
umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
final boolean disabledNow =
newRestrictions.getBoolean(UserManager.DISALLOW_AUTOFILL, false);
synchronized (mLock) {
final boolean disabledBefore = mDisabledUsers.get(userId);
if (disabledBefore == disabledNow) {
// Nothing changed, do nothing.
if (sDebug) {
Slog.d(TAG, "Autofill restriction did not change for user " + userId);
return;
}
}
Slog.i(TAG, "Updating Autofill for user " + userId + ": disabled=" + disabledNow);
mDisabledUsers.put(userId, disabledNow);
updateCachedServiceLocked(userId, disabledNow);
}
});
startTrackingPackageChanges();
}
private void startTrackingPackageChanges() {
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
synchronized (mLock) {
updateCachedServiceLocked(getChangingUserId());
}
}
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
synchronized (mLock) {
final String activePackageName = getActiveAutofillServicePackageName();
if (packageName.equals(activePackageName)) {
removeCachedServiceLocked(getChangingUserId());
} else {
handlePackageUpdateLocked(packageName);
}
}
}
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
final int userId = getChangingUserId();
final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
if (userState != null) {
final ComponentName componentName = userState.getServiceComponentName();
if (componentName != null) {
if (packageName.equals(componentName.getPackageName())) {
handleActiveAutofillServiceRemoved(userId);
}
}
}
}
}
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
synchronized (mLock) {
final String activePackageName = getActiveAutofillServicePackageName();
for (String pkg : packages) {
if (pkg.equals(activePackageName)) {
if (!doit) {
return true;
}
removeCachedServiceLocked(getChangingUserId());
} else {
handlePackageUpdateLocked(pkg);
}
}
}
return false;
}
private void handleActiveAutofillServiceRemoved(int userId) {
removeCachedServiceLocked(userId);
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.AUTOFILL_SERVICE, null, userId);
}
private String getActiveAutofillServicePackageName() {
final int userId = getChangingUserId();
final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
if (userState == null) {
return null;
}
final ComponentName serviceComponent = userState.getServiceComponentName();
if (serviceComponent == null) {
return null;
}
return serviceComponent.getPackageName();
}
@GuardedBy("mLock")
private void handlePackageUpdateLocked(String packageName) {
final int size = mServicesCache.size();
for (int i = 0; i < size; i++) {
mServicesCache.valueAt(i).handlePackageUpdateLocked(packageName);
}
}
};
// package changes
monitor.register(mContext, null, UserHandle.ALL, true);
@Override // from MasterSystemService
protected AutofillManagerServiceImpl newServiceLocked(int resolvedUserId, boolean disabled) {
return new AutofillManagerServiceImpl(this, mLock, mRequestsHistory,
mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi, mAutofillCompatState,
disabled);
}
@Override
@Override // MasterSystemService
protected AutofillManagerServiceImpl removeCachedServiceLocked(int userId) {
final AutofillManagerServiceImpl service = super.removeCachedServiceLocked(userId);
if (service != null) {
service.destroyLocked();
mAutofillCompatState.removeCompatibilityModeRequests(userId);
}
return service;
}
@Override // from MasterSystemService
protected void onServiceEnabledLocked(@NonNull AutofillManagerServiceImpl service, int userId) {
addCompatibilityModeRequestsLocked(service, userId);
}
@Override // from SystemService
public void onStart() {
publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
publishLocalService(AutofillManagerInternal.class, mLocalService);
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
new SettingsObserver(BackgroundThread.getHandler());
}
}
@Override
public void onUnlockUser(int userId) {
synchronized (mLock) {
updateCachedServiceLocked(userId);
}
}
@Override
@Override // from SystemService
public void onSwitchUser(int userHandle) {
if (sDebug) Slog.d(TAG, "Hiding UI when user switched");
mUi.hideAll(null);
}
@Override
public void onCleanupUser(int userId) {
synchronized (mLock) {
removeCachedServiceLocked(userId);
}
}
/**
* Gets the service instance for an user.
*
* @return service instance.
*/
@GuardedBy("mLock")
@NonNull
AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
if (service == null) {
service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi,
mAutofillCompatState, mDisabledUsers.get(resolvedUserId));
mServicesCache.put(userId, service);
addCompatibilityModeRequestsLocked(service, userId);
}
return service;
}
/**
* Peeks the service instance for a user.
*
* @return service instance or {@code null} if not already present
*/
@GuardedBy("mLock")
@Nullable
AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
return mServicesCache.get(resolvedUserId);
}
// Called by Shell command.
void destroySessions(int userId, IResultReceiver receiver) {
Slog.i(TAG, "destroySessions() for userId " + userId);
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
if (userId != UserHandle.USER_ALL) {
@@ -373,10 +256,7 @@ public final class AutofillManagerService extends SystemService {
service.destroySessionsLocked();
}
} else {
final int size = mServicesCache.size();
for (int i = 0; i < size; i++) {
mServicesCache.valueAt(i).destroySessionsLocked();
}
visitServicesLocked((s) -> s.destroySessionsLocked());
}
}
@@ -390,7 +270,7 @@ public final class AutofillManagerService extends SystemService {
// Called by Shell command.
void listSessions(int userId, IResultReceiver receiver) {
Slog.i(TAG, "listSessions() for userId " + userId);
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
final Bundle resultData = new Bundle();
final ArrayList<String> sessions = new ArrayList<>();
@@ -402,10 +282,7 @@ public final class AutofillManagerService extends SystemService {
service.listSessionsLocked(sessions);
}
} else {
final int size = mServicesCache.size();
for (int i = 0; i < size; i++) {
mServicesCache.valueAt(i).listSessionsLocked(sessions);
}
visitServicesLocked((s) -> s.listSessionsLocked(sessions));
}
}
@@ -420,25 +297,22 @@ public final class AutofillManagerService extends SystemService {
// Called by Shell command.
void reset() {
Slog.i(TAG, "reset()");
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
final int size = mServicesCache.size();
for (int i = 0; i < size; i++) {
mServicesCache.valueAt(i).destroyLocked();
}
mServicesCache.clear();
visitServicesLocked((s) -> s.destroyLocked());
clearCacheLocked();
}
}
// Called by Shell command.
void setLogLevel(int level) {
Slog.i(TAG, "setLogLevel(): " + level);
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
final long token = Binder.clearCallingIdentity();
try {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.putInt(getContext().getContentResolver(),
Settings.Global.AUTOFILL_LOGGING_LEVEL, level);
} finally {
Binder.restoreCallingIdentity(token);
@@ -447,7 +321,7 @@ public final class AutofillManagerService extends SystemService {
private void setLogLevelFromSettings() {
final int level = Settings.Global.getInt(
mContext.getContentResolver(),
getContext().getContentResolver(),
Settings.Global.AUTOFILL_LOGGING_LEVEL, AutofillManager.DEFAULT_LOGGING_LEVEL);
boolean debug = false;
boolean verbose = false;
@@ -465,14 +339,13 @@ public final class AutofillManagerService extends SystemService {
+ ", verbose=" + verbose);
}
synchronized (mLock) {
setDebugLocked(debug);
setVerboseLocked(verbose);
setLoggingLevelsLocked(debug, verbose);
}
}
// Called by Shell command.
int getLogLevel() {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
@@ -483,7 +356,7 @@ public final class AutofillManagerService extends SystemService {
// Called by Shell command.
int getMaxPartitions() {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
return sPartitionMaxCount;
@@ -492,12 +365,12 @@ public final class AutofillManagerService extends SystemService {
// Called by Shell command.
void setMaxPartitions(int max) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxPartitions(): " + max);
final long token = Binder.clearCallingIdentity();
try {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.putInt(getContext().getContentResolver(),
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, max);
} finally {
Binder.restoreCallingIdentity(token);
@@ -505,33 +378,33 @@ public final class AutofillManagerService extends SystemService {
}
private void setMaxPartitionsFromSettings() {
final int max = Settings.Global.getInt(mContext.getContentResolver(),
final int max = Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE);
if (sDebug) Slog.d(TAG, "setMaxPartitionsFromSettings(): " + max);
synchronized (mLock) {
synchronized (sLock) {
sPartitionMaxCount = max;
}
}
// Called by Shell command.
int getMaxVisibleDatasets() {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
synchronized (sLock) {
return sVisibleDatasetsMaxCount;
}
}
// Called by Shell command.
void setMaxVisibleDatasets(int max) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
final long token = Binder.clearCallingIdentity();
try {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.putInt(getContext().getContentResolver(),
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, max);
} finally {
Binder.restoreCallingIdentity(token);
@@ -539,11 +412,11 @@ public final class AutofillManagerService extends SystemService {
}
private void setMaxVisibleDatasetsFromSettings() {
final int max = Settings.Global.getInt(mContext.getContentResolver(),
final int max = Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, 0);
if (sDebug) Slog.d(TAG, "setMaxVisibleDatasetsFromSettings(): " + max);
synchronized (mLock) {
synchronized (sLock) {
sVisibleDatasetsMaxCount = max;
}
}
@@ -551,10 +424,10 @@ public final class AutofillManagerService extends SystemService {
// Called by Shell command.
void getScore(@Nullable String algorithmName, @NonNull String value1,
@NonNull String value2, @NonNull RemoteCallback callback) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
final FieldClassificationStrategy strategy =
new FieldClassificationStrategy(mContext, UserHandle.USER_CURRENT);
new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
strategy.getScores(callback, algorithmName, null,
Arrays.asList(AutofillValue.forText(value1)), new String[] { value2 });
@@ -562,19 +435,19 @@ public final class AutofillManagerService extends SystemService {
// Called by Shell command.
Boolean getFullScreenMode() {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
return sFullScreenMode;
}
// Called by Shell command.
void setFullScreenMode(@Nullable Boolean mode) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
sFullScreenMode = mode;
}
// Called by Shell command.
boolean getAllowInstantService() {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
synchronized (mLock) {
return mAllowInstantService;
}
@@ -582,59 +455,21 @@ public final class AutofillManagerService extends SystemService {
// Called by Shell command.
void setAllowInstantService(boolean mode) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setAllowInstantService(): " + mode);
synchronized (mLock) {
mAllowInstantService = mode;
}
}
private void setDebugLocked(boolean debug) {
private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
com.android.server.autofill.Helper.sDebug = debug;
android.view.autofill.Helper.sDebug = debug;
}
this.debug = debug;
private void setVerboseLocked(boolean verbose) {
com.android.server.autofill.Helper.sVerbose = verbose;
android.view.autofill.Helper.sVerbose = verbose;
}
/**
* Removes a cached service for a given user.
*/
@GuardedBy("mLock")
private void removeCachedServiceLocked(int userId) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
mServicesCache.delete(userId);
service.destroyLocked();
mAutofillCompatState.removeCompatibilityModeRequests(userId);
}
}
/**
* Updates a cached service for a given user.
*/
@GuardedBy("mLock")
private void updateCachedServiceLocked(int userId) {
updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
}
/**
* Updates a cached service for a given user.
*/
@GuardedBy("mLock")
private void updateCachedServiceLocked(int userId, boolean disabled) {
AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
service.destroySessionsLocked();
service.updateLocked(disabled);
if (!service.isEnabledLocked()) {
removeCachedServiceLocked(userId);
} else {
addCompatibilityModeRequestsLocked(service, userId);
}
}
this.verbose = verbose;
}
private void addCompatibilityModeRequestsLocked(@NonNull AutofillManagerServiceImpl service
@@ -664,7 +499,7 @@ public final class AutofillManagerService extends SystemService {
private String getWhitelistedCompatModePackagesFromSettings() {
return Settings.Global.getString(
mContext.getContentResolver(),
getContext().getContentResolver(),
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
}
@@ -758,6 +593,24 @@ public final class AutofillManagerService extends SystemService {
return compatPackages;
}
/**
* Gets the maximum number of partitions / fill requests.
*/
public static int getPartitionMaxCount() {
synchronized (sLock) {
return sPartitionMaxCount;
}
}
/**
* Gets the maxium number of datasets visible in the UI.
*/
public static int getVisibleDatasetsMaxCount() {
synchronized (sLock) {
return sVisibleDatasetsMaxCount;
}
}
private final class LocalService extends AutofillManagerInternal {
@Override
public void onBackKeyPressed() {
@@ -889,20 +742,24 @@ public final class AutofillManagerService extends SystemService {
}
private void dump(String prefix, PrintWriter pw) {
if (mUserSpecs == null) {
pw.println("N/A");
return;
}
pw.println();
final String prefix2 = prefix + " ";
for (int i = 0; i < mUserSpecs.size(); i++) {
final int user = mUserSpecs.keyAt(i);
pw.print(prefix); pw.print("User: "); pw.println(user);
final ArrayMap<String, PackageCompatState> perUser = mUserSpecs.valueAt(i);
for (int j = 0; j < perUser.size(); j++) {
final String packageName = perUser.keyAt(j);
final PackageCompatState state = perUser.valueAt(j);
pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
synchronized (mLock) {
if (mUserSpecs == null) {
pw.println("N/A");
return;
}
pw.println();
final String prefix2 = prefix + " ";
for (int i = 0; i < mUserSpecs.size(); i++) {
final int user = mUserSpecs.keyAt(i);
pw.print(prefix);
pw.print("User: ");
pw.println(user);
final ArrayMap<String, PackageCompatState> perUser = mUserSpecs.valueAt(i);
for (int j = 0; j < perUser.size(); j++) {
final String packageName = perUser.keyAt(j);
final PackageCompatState state = perUser.valueAt(j);
pw.print(prefix2); pw.print(packageName); pw.print(": "); pw.println(state);
}
}
}
}
@@ -971,7 +828,7 @@ public final class AutofillManagerService extends SystemService {
Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId");
try {
mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
getContext().getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException(packageName + " is not a valid package", e);
}
@@ -1139,7 +996,7 @@ public final class AutofillManagerService extends SystemService {
boolean restored = false;
synchronized (mLock) {
final AutofillManagerServiceImpl service = mServicesCache.get(userId);
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
restored = service.restoreSession(sessionId, getCallingUid(), activityToken,
appCallback);
@@ -1216,7 +1073,7 @@ public final class AutofillManagerService extends SystemService {
public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) {
boolean supported = false;
synchronized (mLock) {
supported = !mDisabledUsers.get(userId);
supported = !isDisabledLocked(userId);
}
send(receiver, supported);
}
@@ -1253,7 +1110,7 @@ public final class AutofillManagerService extends SystemService {
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
boolean showHistory = true;
boolean uiOnly = false;
@@ -1280,102 +1137,48 @@ public final class AutofillManagerService extends SystemService {
return;
}
boolean oldDebug = sDebug;
final String prefix = " ";
final String prefix2 = " ";
boolean realDebug = sDebug;
boolean realVerbose = sVerbose;
try {
sDebug = sVerbose = true;
synchronized (mLock) {
oldDebug = sDebug;
setDebugLocked(true);
pw.print("Debug mode: "); pw.println(oldDebug);
pw.print("Verbose mode: "); pw.println(sVerbose);
pw.print("Disabled users: "); pw.println(mDisabledUsers);
pw.print("sDebug: "); pw.print(realDebug);
pw.print(" sVerbose: "); pw.println(realVerbose);
// Dump per-user services
dumpLocked("", pw);
pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
pw.print("Max visible datasets: "); pw.println(sVisibleDatasetsMaxCount);
if (sFullScreenMode != null) {
pw.print("Overridden full-screen mode: "); pw.println(sFullScreenMode);
}
pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw);
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("\nService at index "); pw.println(i);
final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
impl.dumpLocked(prefix, pw);
}
}
mUi.dump(pw);
pw.print("Autofill Compat State: ");
mAutofillCompatState.dump(prefix2, pw);
pw.print(prefix2); pw.print("from settings: ");
mAutofillCompatState.dump(prefix, pw);
pw.print("from settings: ");
pw.println(getWhitelistedCompatModePackagesFromSettings());
pw.print("Allow instant service: "); pw.println(mAllowInstantService);
}
if (showHistory) {
pw.println(); pw.println("Requests history:"); pw.println();
mRequestsHistory.reverseDump(fd, pw, args);
pw.println(); pw.println("UI latency history:"); pw.println();
mUiLatencyHistory.reverseDump(fd, pw, args);
pw.println(); pw.println("WTF history:"); pw.println();
mWtfHistory.reverseDump(fd, pw, args);
if (showHistory) {
pw.println(); pw.println("Requests history:"); pw.println();
mRequestsHistory.reverseDump(fd, pw, args);
pw.println(); pw.println("UI latency history:"); pw.println();
mUiLatencyHistory.reverseDump(fd, pw, args);
pw.println(); pw.println("WTF history:"); pw.println();
mWtfHistory.reverseDump(fd, pw, args);
}
}
} finally {
setDebugLocked(oldDebug);
sDebug = realDebug;
sVerbose = realVerbose;
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new AutofillManagerServiceShellCommand(AutofillManagerService.this)).exec(
new AutofillManagerServiceShellCommand(AutofillManagerService.this).exec(
this, in, out, err, args, callback, resultReceiver);
}
}
private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.AUTOFILL_SERVICE), false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_LOGGING_LEVEL), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, this,
UserHandle.USER_ALL);
}
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
if (sVerbose) Slog.v(TAG, "onChange(): uri=" + uri + ", userId=" + userId);
switch (uri.getLastPathSegment()) {
case Settings.Global.AUTOFILL_LOGGING_LEVEL:
setLogLevelFromSettings();
break;
case Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE:
setMaxPartitionsFromSettings();
break;
case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
setMaxVisibleDatasetsFromSettings();
break;
default:
synchronized (mLock) {
updateCachedServiceLocked(userId);
}
}
}
}
}

View File

@@ -25,19 +25,14 @@ import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
import android.os.Binder;
@@ -49,7 +44,6 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
@@ -60,7 +54,6 @@ import android.service.autofill.FillEventHistory.Event;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
@@ -77,6 +70,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.AbstractPerUserSystemService;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.ui.AutoFillUI;
@@ -91,7 +85,8 @@ import java.util.Random;
* app's {@link IAutoFillService} implementation.
*
*/
final class AutofillManagerServiceImpl {
final class AutofillManagerServiceImpl
extends AbstractPerUserSystemService<AutofillManagerServiceImpl> {
private static final String TAG = "AutofillManagerServiceImpl";
private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
@@ -99,9 +94,6 @@ final class AutofillManagerServiceImpl {
/** Minimum interval to prune abandoned sessions */
private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
private final int mUserId;
private final Context mContext;
private final Object mLock;
private final AutoFillUI mUi;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
@@ -131,24 +123,12 @@ final class AutofillManagerServiceImpl {
@GuardedBy("mLock")
private ArrayMap<ComponentName, Long> mDisabledActivities;
/**
* Whether service was disabled for user due to {@link UserManager} restrictions.
*/
@GuardedBy("mLock")
private boolean mDisabled;
/**
* Data used for field classification.
*/
@GuardedBy("mLock")
private UserData mUserData;
/**
* Caches whether the setup completed for the current user.
*/
@GuardedBy("mLock")
private boolean mSetupComplete;
private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
/**
@@ -170,116 +150,27 @@ final class AutofillManagerServiceImpl {
/** When was {@link PruneTask} last executed? */
private long mLastPrune = 0;
AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
AutofillManagerServiceImpl(AutofillManagerService master, Object lock, LocalLog requestsHistory,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState, boolean disabled) {
mContext = context;
mLock = lock;
super(master, lock, userId);
mRequestsHistory = requestsHistory;
mUiLatencyHistory = uiLatencyHistory;
mWtfHistory = wtfHistory;
mUserId = userId;
mUi = ui;
mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId);
mFieldClassificationStrategy = new FieldClassificationStrategy(getContext(), userId);
mAutofillCompatState = autofillCompatState;
updateLocked(disabled);
}
@GuardedBy("mLock")
private int getServiceUidLocked() {
if (mInfo == null) {
Slog.w(TAG, "getServiceUidLocked(): no mInfo");
return -1;
}
return mInfo.getServiceInfo().applicationInfo.uid;
}
@Nullable
String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
}
@Nullable
String getServicePackageName() {
final ComponentName serviceComponent = getServiceComponentName();
if (serviceComponent != null) {
return serviceComponent.getPackageName();
}
return null;
}
@Nullable
ComponentName getServiceComponentName() {
synchronized (mLock) {
if (mInfo == null) {
return null;
}
return mInfo.getServiceInfo().getComponentName();
}
}
int getTargedSdkLocked() {
if (mInfo == null) {
return 0;
}
return mInfo.getServiceInfo().applicationInfo.targetSdkVersion;
}
private boolean isSetupCompletedLocked() {
final String setupComplete = Settings.Secure.getStringForUser(
mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
return "1".equals(setupComplete);
}
private String getComponentNameFromSettings() {
return Settings.Secure.getStringForUser(
mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
}
@GuardedBy("mLock")
void updateLocked(boolean disabled) {
final boolean wasEnabled = isEnabledLocked();
if (sVerbose) {
Slog.v(TAG, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
+ ", mSetupComplete= " + mSetupComplete
+ ", disabled=" + disabled + ", mDisabled=" + mDisabled);
}
mSetupComplete = isSetupCompletedLocked();
mDisabled = disabled;
ComponentName serviceComponent = null;
ServiceInfo serviceInfo = null;
final String componentName = getComponentNameFromSettings();
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
0, mUserId);
if (serviceInfo == null) {
Slog.e(TAG, "Bad AutofillService name: " + componentName);
}
} catch (RuntimeException | RemoteException e) {
Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
serviceInfo = null;
}
}
try {
if (serviceInfo != null) {
mInfo = new AutofillServiceInfo(mContext, serviceComponent, mUserId);
if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
} else {
mInfo = null;
if (sDebug) {
Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
}
}
} catch (Exception e) {
Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
mInfo = null;
}
final boolean isEnabled = isEnabledLocked();
if (wasEnabled != isEnabled) {
if (!isEnabled) {
@Override // from PerUserSystemService
protected boolean updateLocked(boolean disabled) {
destroySessionsLocked();
final boolean enabledChanged = super.updateLocked(disabled);
if (enabledChanged) {
if (!isEnabledLocked()) {
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
final Session session = mSessions.valueAt(i);
@@ -288,6 +179,19 @@ final class AutofillManagerServiceImpl {
}
sendStateToClients(false);
}
return enabledChanged;
}
@Override // from PerUserSystemService
protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
mInfo = new AutofillServiceInfo(getContext(), serviceComponent, mUserId);
return mInfo.getServiceInfo();
}
@Nullable
String[] getUrlBarResourceIdsForCompatMode(@NonNull String packageName) {
return mAutofillCompatState.getUrlBarResourceIds(packageName, mUserId);
}
@GuardedBy("mLock")
@@ -469,7 +373,7 @@ final class AutofillManagerServiceImpl {
if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) {
mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF,
componentName.getPackageName());
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.AUTOFILL_SERVICE, null, mUserId);
destroySessionsLocked();
} else {
@@ -501,7 +405,7 @@ final class AutofillManagerServiceImpl {
assertCallerLocked(componentName, compatMode);
final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock,
final Session newSession = new Session(this, mUi, getContext(), mHandler, mUserId, mLock,
sessionId, taskId, uid, activityToken, appCallbackToken, hasCallback,
mUiLatencyHistory, mWtfHistory, mInfo.getServiceInfo().getComponentName(),
componentName, compatMode, bindInstantServiceAllowed, flags);
@@ -515,7 +419,7 @@ final class AutofillManagerServiceImpl {
*/
private void assertCallerLocked(@NonNull ComponentName componentName, boolean compatMode) {
final String packageName = componentName.getPackageName();
final PackageManager pm = mContext.getPackageManager();
final PackageManager pm = getContext().getPackageManager();
final int callingUid = Binder.getCallingUid();
final int packageUid;
try {
@@ -651,7 +555,8 @@ final class AutofillManagerServiceImpl {
}
@GuardedBy("mLock")
void handlePackageUpdateLocked(String packageName) {
@Override // from PerUserSystemService
protected void handlePackageUpdateLocked(@NonNull String packageName) {
final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
if (serviceInfo != null && serviceInfo.packageName.equals(packageName)) {
resetExtServiceLocked();
@@ -690,29 +595,6 @@ final class AutofillManagerServiceImpl {
}
}
/**
* Gets the user-visibile name of the service this service binds to, or {@code null} if the
* service is disabled.
*/
@Nullable
@GuardedBy("mLock")
public CharSequence getServiceLabelLocked() {
return mInfo == null ? null : mInfo.getServiceInfo().loadSafeLabel(
mContext.getPackageManager(), 0 /* do not ellipsize */,
PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
}
/**
* Gets the icon of the service this service binds to, or {@code null} if the service is
* disabled.
*/
@NonNull
@Nullable
@GuardedBy("mLock")
Drawable getServiceIconLocked() {
return mInfo == null ? null : mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
}
/**
* Initializes the last fill selection after an autofill service returned a new
* {@link FillResponse}.
@@ -941,11 +823,13 @@ final class AutofillManagerServiceImpl {
return true;
}
@Override
@GuardedBy("mLock")
void dumpLocked(String prefix, PrintWriter pw) {
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
final String prefix2 = prefix + " ";
pw.print(prefix); pw.print("User: "); pw.println(mUserId);
pw.print(prefix); pw.print("UID: "); pw.println(getServiceUidLocked());
pw.print(prefix); pw.print("Autofill Service Info: ");
if (mInfo == null) {
@@ -958,9 +842,8 @@ final class AutofillManagerServiceImpl {
}
pw.print(prefix); pw.print("Component from settings: ");
pw.println(getComponentNameFromSettings());
pw.print(prefix); pw.print("Default component: ");
pw.println(mContext.getString(R.string.config_defaultAutofillService));
pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
.getString(R.string.config_defaultAutofillService));
pw.print(prefix); pw.print("Field classification enabled: ");
pw.println(isFieldClassificationEnabledLocked());
pw.print(prefix); pw.print("Compat pkgs: ");
@@ -970,7 +853,6 @@ final class AutofillManagerServiceImpl {
} else {
pw.println(compatPkgs);
}
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
pw.print(prefix); pw.print("Disabled apps: ");
@@ -1158,11 +1040,6 @@ final class AutofillManagerServiceImpl {
return true;
}
@GuardedBy("mLock")
boolean isEnabledLocked() {
return mSetupComplete && mInfo != null && !mDisabled;
}
/**
* Called by {@link Session} when service asked to disable autofill for an app.
*/
@@ -1270,7 +1147,7 @@ final class AutofillManagerServiceImpl {
// Called by internally, no need to check UID.
boolean isFieldClassificationEnabledLocked() {
return Settings.Secure.getIntForUser(
mContext.getContentResolver(),
getContext().getContentResolver(),
Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 1,
mUserId) == 1;
}

View File

@@ -28,20 +28,21 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.WindowManager;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import java.io.PrintWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.LinkedList;
public final class Helper {
private static final String TAG = "AutofillHelper";
// TODO(b/117779333): get rid of sDebug / sVerbose and always use the service variables instead
/**
* Defines a logging flag that can be dynamically changed at runtime using
* {@code cmd autofill set log_level debug} or through
@@ -56,23 +57,6 @@ public final class Helper {
*/
public static boolean sVerbose = false;
/**
* Maximum number of partitions that can be allowed in a session.
*
* <p>Can be modified using {@code cmd autofill set max_partitions} or through
* {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
*/
static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;
/**
* Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
* value from resources.
*
* <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
* {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
*/
public static int sVisibleDatasetsMaxCount = 0;
/**
* When non-null, overrides whether the UI should be shown on full-screen mode.
*
@@ -162,7 +146,7 @@ public final class Helper {
private static ViewNode findViewNode(@NonNull AssistStructure structure,
@NonNull ViewNodeFilter filter) {
final LinkedList<ViewNode> nodesToProcess = new LinkedList<>();
final ArrayDeque<ViewNode> nodesToProcess = new ArrayDeque<>();
final int numWindowNodes = structure.getWindowNodeCount();
for (int i = 0; i < numWindowNodes; i++) {
nodesToProcess.add(structure.getWindowNodeAt(i).getRootViewNode());

View File

@@ -27,7 +27,6 @@ import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.Helper.getNumericValue;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sPartitionMaxCount;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
@@ -65,7 +64,6 @@ import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
import android.service.autofill.FieldClassification;
import android.service.autofill.FieldClassification.Match;
import android.text.TextUtils;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
@@ -75,10 +73,10 @@ import android.service.autofill.SaveInfo;
import android.service.autofill.SaveRequest;
import android.service.autofill.UserData;
import android.service.autofill.ValueFinder;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -2095,9 +2093,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final int numResponses = mResponses.size();
if (numResponses >= sPartitionMaxCount) {
if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
+ " reached maximum of " + sPartitionMaxCount);
+ " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
return false;
}
@@ -2289,7 +2287,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
// Remove the UI if the ViewState has changed.
if (mCurrentViewId != viewState.id) {
if (!Objects.equals(mCurrentViewId, viewState.id)) {
mUi.hideFillUi(this);
mCurrentViewId = viewState.id;
}
@@ -2298,7 +2296,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
viewState.update(value, virtualBounds, flags);
break;
case ACTION_VIEW_EXITED:
if (mCurrentViewId == viewState.id) {
if (Objects.equals(mCurrentViewId, viewState.id)) {
if (sVerbose) Slog.d(TAG, "Exiting view " + id);
mUi.hideFillUi(this);
mCurrentViewId = null;
@@ -3077,7 +3075,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private void wtf(@Nullable Exception e, String fmt, Object...args) {
final String message = String.format(fmt, args);
mWtfHistory.log(message);
synchronized (mLock) {
mWtfHistory.log(message);
}
if (e != null) {
Slog.wtf(TAG, message, e);

View File

@@ -19,25 +19,22 @@ import static com.android.server.autofill.Helper.paramsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.ContextThemeWrapper;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.service.autofill.Dataset;
import android.service.autofill.Dataset.DatasetFieldFilter;
import android.service.autofill.FillResponse;
import android.text.TextUtils;
import android.util.Slog;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -60,6 +57,7 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.server.UiThread;
import com.android.server.autofill.AutofillManagerService;
import com.android.server.autofill.Helper;
import java.io.PrintWriter;
@@ -193,8 +191,8 @@ final class FillUi {
}
});
if (sVisibleDatasetsMaxCount > 0) {
mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
if (AutofillManagerService.getVisibleDatasetsMaxCount() > 0) {
mVisibleDatasetsMaxCount = AutofillManagerService.getVisibleDatasetsMaxCount();
if (sVerbose) {
Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
}

View File

@@ -0,0 +1,507 @@
/*
* Copyright (C) 2018 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;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import java.io.PrintWriter;
import java.util.List;
/**
* Base class for {@link SystemService SystemServices} that support multi user.
*
* <p>Subclasses of this service are just a facade for the service binder calls - the "real" work
* is done by the {@link AbstractPerUserSystemService} subclasses, which are automatically managed
* through an user -> service cache.
*
* <p>It also takes care of other plumbing tasks such as:
*
* <ul>
* <li>Disabling the service when {@link UserManager} restrictions change.
* <li>Refreshing the service when its underlying
* {@link #getServiceSettingsProperty() Settings property} changed.
* <li>Calling the service when other Settings properties changed.
* </ul>
*
* <p>See {@code com.android.server.autofill.AutofillManagerService} for a concrete
* (no pun intended) example of how to use it.
*
* @param <S> "real" service class.
*
* @hide
*/
// TODO(b/117779333): improve javadoc above instead of using Autofill as an example
public abstract class AbstractMasterSystemService<S extends AbstractPerUserSystemService<S>>
extends SystemService {
/**
* Log tag
*/
protected final String mTag = getClass().getSimpleName();
/**
* Lock used to synchronize access to internal state; should be acquired before calling a
* method whose name ends with {@code locked}.
*/
protected final Object mLock = new Object();
/**
* Whether the service should log debug statements.
*/
public boolean verbose = false;
/**
* Whether the service should log verbose statements.
*/
public boolean debug = false;
/**
* Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
* be disabled through {@link UserManager}.
*/
@GuardedBy("mLock")
@Nullable
private final SparseBooleanArray mDisabledUsers;
/**
* Cache of services per user id.
*/
@GuardedBy("mLock")
private final SparseArray<S> mServicesCache = new SparseArray<>();
/**
* Default constructor.
*
* @param context system context.
* @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
* disables the service.
*/
protected AbstractMasterSystemService(@NonNull Context context,
@Nullable String disallowProperty) {
super(context);
if (disallowProperty == null) {
mDisabledUsers = null;
} else {
mDisabledUsers = new SparseBooleanArray();
// Hookup with UserManager to disable service when necessary.
final UserManager um = context.getSystemService(UserManager.class);
final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
final List<UserInfo> users = um.getUsers();
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
final boolean disabled = umi.getUserRestriction(userId, disallowProperty);
if (disabled) {
Slog.i(mTag, "Disabling for user " + userId);
mDisabledUsers.put(userId, disabled);
}
}
umi.addUserRestrictionsListener((userId, newRestrictions, prevRestrictions) -> {
final boolean disabledNow =
newRestrictions.getBoolean(disallowProperty, false);
synchronized (mLock) {
final boolean disabledBefore = mDisabledUsers.get(userId);
if (disabledBefore == disabledNow) {
// Nothing changed, do nothing.
if (debug) {
Slog.d(mTag, "Restriction did not change for user " + userId);
return;
}
}
Slog.i(mTag, "Updating for user " + userId + ": disabled=" + disabledNow);
mDisabledUsers.put(userId, disabledNow);
updateCachedServiceLocked(userId, disabledNow);
}
});
}
startTrackingPackageChanges();
}
@Override // from SystemService
public void onBootPhase(int phase) {
if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
new SettingsObserver(BackgroundThread.getHandler());
}
}
@Override // from SystemService
public void onUnlockUser(int userId) {
synchronized (mLock) {
updateCachedServiceLocked(userId);
}
}
@Override // from SystemService
public void onCleanupUser(int userId) {
synchronized (mLock) {
removeCachedServiceLocked(userId);
}
}
/**
* Creates a new service that will be added to the cache.
*
* @param resolvedUserId the resolved user id for the service.
* @param disabled whether the service is currently disabled (due to {@link UserManager}
* restrictions).
*
* @return a new instance.
*/
protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
/**
* Register the service for extra Settings changes (i.e., other than
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}, which are automatically handled).
*
* <p> Example:
*
* <pre><code>
* resolver.registerContentObserver(Settings.Global.getUriFor(
* Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, observer,
* UserHandle.USER_ALL);
* </code></pre>
*
* <p><b>NOTE: </p>it doesn't need to register for
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}.
*
*/
@SuppressWarnings("unused")
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@NonNull ContentObserver observer) {
}
/**
* Callback for Settings changes that were registered though
* {@link #registerForExtraSettingsChanges(ContentResolver, ContentObserver)}.
*
* @param userId user associated with the change
* @param property Settings property changed.
*/
protected void onSettingsChanged(@UserIdInt int userId, @NonNull String property) {
}
/**
* Gets the service instance for an user, creating an instance if not present in the cache.
*/
@GuardedBy("mLock")
@NonNull
protected S getServiceForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
S service = mServicesCache.get(resolvedUserId);
if (service == null) {
final boolean disabled = isDisabledLocked(userId);
service = newServiceLocked(resolvedUserId, disabled);
if (!disabled) {
onServiceEnabledLocked(service, resolvedUserId);
}
mServicesCache.put(userId, service);
}
return service;
}
/**
* Gets the <b>existing</b> service instance for a user, returning {@code null} if not already
* present in the cache.
*/
@GuardedBy("mLock")
@Nullable
protected S peekServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
return mServicesCache.get(resolvedUserId);
}
/**
* Updates a cached service for a given user.
*/
@GuardedBy("mLock")
protected void updateCachedServiceLocked(int userId) {
updateCachedServiceLocked(userId, isDisabledLocked(userId));
}
/**
* Checks whether the service is disabled (through {@link UserManager} restrictions) for the
* given user.
*/
protected boolean isDisabledLocked(int userId) {
return mDisabledUsers == null ? false : mDisabledUsers.get(userId);
}
/**
* Updates a cached service for a given user.
*
* @param userId user handle.
* @param disabled whether the user is disabled.
* @return service for the user.
*/
@GuardedBy("mLock")
protected S updateCachedServiceLocked(int userId, boolean disabled) {
final S service = getServiceForUserLocked(userId);
if (service != null) {
service.updateLocked(disabled);
if (!service.isEnabledLocked()) {
removeCachedServiceLocked(userId);
} else {
onServiceEnabledLocked(service, userId);
}
}
return service;
}
/**
* Gets the Settings property that defines the name of the component name used to bind this
* service to an external service, or {@code null} when the service is not defined by such
* property (for example, if it's a system service defined by framework resources).
*/
@Nullable
protected String getServiceSettingsProperty() {
return null;
}
/**
* Callback called after a new service was added to the cache, or an existing service that was
* previously disabled gets enabled.
*
* <p>By default doesn't do anything, but can be overridden by subclasses.
*/
@SuppressWarnings("unused")
protected void onServiceEnabledLocked(S service, @UserIdInt int userId) {
}
/**
* Removes a cached service for a given user.
*
* @return the removed service;
*/
@GuardedBy("mLock")
@NonNull
protected S removeCachedServiceLocked(@UserIdInt int userId) {
final S service = peekServiceForUserLocked(userId);
if (service != null) {
mServicesCache.delete(userId);
}
return service;
}
/**
* Visits all services in the cache.
*/
@GuardedBy("mLock")
protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
final int size = mServicesCache.size();
for (int i = 0; i < size; i++) {
visitor.visit(mServicesCache.valueAt(i));
}
}
/**
* Clear the cache by removing all services.
*/
@GuardedBy("mLock")
protected void clearCacheLocked() {
mServicesCache.clear();
}
// TODO(b/117779333): support proto
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug;
boolean realVerbose = verbose;
try {
// Temporarily turn on full logging;
debug = verbose = true;
final int size = mServicesCache.size();
pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
pw.print(" Verbose: "); pw.println(realVerbose);
pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
pw.print(prefix); pw.print("Settings property: "); pw.println(
getServiceSettingsProperty());
pw.print(prefix); pw.print("Cached services: ");
if (size == 0) {
pw.println("none");
} else {
pw.println(size);
final String prefix2 = " ";
for (int i = 0; i < size; i++) {
pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
final S service = mServicesCache.valueAt(i);
service.dumpLocked(prefix2, pw);
pw.println();
}
}
} finally {
debug = realDebug;
verbose = realVerbose;
}
}
private void startTrackingPackageChanges() {
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
synchronized (mLock) {
updateCachedServiceLocked(getChangingUserId());
}
}
@Override
public void onPackageUpdateFinished(String packageName, int uid) {
synchronized (mLock) {
final String activePackageName = getActiveServicePackageName();
if (packageName.equals(activePackageName)) {
removeCachedServiceLocked(getChangingUserId());
} else {
handlePackageUpdateLocked(packageName);
}
}
}
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
final int userId = getChangingUserId();
final S service = peekServiceForUserLocked(userId);
if (service != null) {
final ComponentName componentName = service.getServiceComponentName();
if (componentName != null) {
if (packageName.equals(componentName.getPackageName())) {
handleActiveServiceRemoved(userId);
}
}
}
}
}
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
synchronized (mLock) {
final String activePackageName = getActiveServicePackageName();
for (String pkg : packages) {
if (pkg.equals(activePackageName)) {
if (!doit) {
return true;
}
removeCachedServiceLocked(getChangingUserId());
} else {
handlePackageUpdateLocked(pkg);
}
}
}
return false;
}
private void handleActiveServiceRemoved(@UserIdInt int userId) {
removeCachedServiceLocked(userId);
final String serviceSettingsProperty = getServiceSettingsProperty();
if (serviceSettingsProperty != null) {
Settings.Secure.putStringForUser(getContext().getContentResolver(),
serviceSettingsProperty, null, userId);
}
}
private String getActiveServicePackageName() {
final int userId = getChangingUserId();
final S service = peekServiceForUserLocked(userId);
if (service == null) {
return null;
}
final ComponentName serviceComponent = service.getServiceComponentName();
if (serviceComponent == null) {
return null;
}
return serviceComponent.getPackageName();
}
@GuardedBy("mLock")
private void handlePackageUpdateLocked(String packageName) {
visitServicesLocked((s) -> s.handlePackageUpdateLocked(packageName));
}
};
// package changes
monitor.register(getContext(), null, UserHandle.ALL, true);
}
/**
* Visitor pattern.
*
* @param <S> visited class.
*/
public interface Visitor<S> {
/**
* Visits a service.
*
* @param service the service to be visited.
*/
void visit(@NonNull S service);
}
private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
ContentResolver resolver = getContext().getContentResolver();
final String serviceProperty = getServiceSettingsProperty();
if (serviceProperty != null) {
resolver.registerContentObserver(Settings.Secure.getUriFor(
serviceProperty), false, this, UserHandle.USER_ALL);
}
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL);
registerForExtraSettingsChanges(resolver, this);
}
@Override
public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onChange(): uri=" + uri + ", userId=" + userId);
final String property = uri.getLastPathSegment();
if (property.equals(getServiceSettingsProperty())
|| property.equals(Settings.Secure.USER_SETUP_COMPLETE)) {
synchronized (mLock) {
updateCachedServiceLocked(userId);
}
} else {
onSettingsChanged(userId, property);
}
}
}
}

View File

@@ -0,0 +1,274 @@
/*
* Copyright (C) 2018 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;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
/**
* Companion for {@link AbstractMasterSystemService}, it's the base class for the "real" service
* implementation.
*
* @param <S> itself
*
* @hide
*/
public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S>> {
protected final @UserIdInt int mUserId;
protected final Object mLock;
protected final String mTag = getClass().getSimpleName();
protected final AbstractMasterSystemService<S> mMaster;
/**
* Whether service was disabled for user due to {@link UserManager} restrictions.
*/
@GuardedBy("mLock")
private boolean mDisabled;
/**
* Caches whether the setup completed for the current user.
*/
@GuardedBy("mLock")
private boolean mSetupComplete;
@GuardedBy("mLock")
private ServiceInfo mServiceInfo;
protected AbstractPerUserSystemService(@NonNull AbstractMasterSystemService<S> master,
@NonNull Object lock, @UserIdInt int userId) {
mMaster = master;
mLock = lock;
mUserId = userId;
}
/**
* Creates a new {@link ServiceInfo} for the given service name.
*
* @throws NameNotFoundException if the service does not exist.
* @throws SecurityException if the service does not have the proper permissions to be bound to.
*/
protected abstract ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
throws NameNotFoundException;
/**
* Callback called when an app has been updated.
*
* @param packageName package of the app being updated.
*/
protected void handlePackageUpdateLocked(@NonNull String packageName) {
}
/**
* Gets whether the service is enabled and ready.
*/
@GuardedBy("mLock")
protected boolean isEnabledLocked() {
return mSetupComplete && mServiceInfo != null && !mDisabled;
}
/**
* Updates the state of this service.
*
* <p>Typically called when the service {@link Settings} property or {@link UserManager}
* restriction changed, which includes the initial creation of the service.
*
* <p>Subclasses can extend this method to provide extra initialization.
*
* @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
*
* @return whether the disabled state changed.
*/
@GuardedBy("mLock")
@CallSuper
protected boolean updateLocked(boolean disabled) {
final boolean wasEnabled = isEnabledLocked();
if (mMaster.verbose) {
Slog.v(mTag, "updateLocked(u=" + mUserId + "): wasEnabled=" + wasEnabled
+ ", mSetupComplete=" + mSetupComplete
+ ", disabled=" + disabled + ", mDisabled=" + mDisabled);
}
mSetupComplete = isSetupCompletedLocked();
mDisabled = disabled;
ComponentName serviceComponent = null;
ServiceInfo serviceInfo = null;
final String componentName = getComponentNameFromSettings();
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
0, mUserId);
if (serviceInfo == null) {
Slog.e(mTag, "Bad service name: " + componentName);
}
} catch (RuntimeException | RemoteException e) {
Slog.e(mTag, "Error getting service info for '" + componentName + "': " + e);
serviceInfo = null;
}
}
try {
if (serviceInfo != null) {
mServiceInfo = newServiceInfo(serviceComponent);
if (mMaster.debug) {
Slog.d(mTag, "Set component for user " + mUserId + " as " + mServiceInfo);
}
} else {
mServiceInfo = null;
if (mMaster.debug) {
Slog.d(mTag, "Reset component for user " + mUserId + ":" + componentName);
}
}
} catch (Exception e) {
Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
mServiceInfo = null;
}
return wasEnabled != isEnabledLocked();
}
/**
* Gets this UID of the remote service this service binds to, or {@code -1} if the service is
* disabled.
*/
@GuardedBy("mLock")
protected final int getServiceUidLocked() {
if (mServiceInfo == null) {
Slog.w(mTag, "getServiceUidLocked(): no mServiceInfo");
return Process.INVALID_UID;
}
return mServiceInfo.applicationInfo.uid;
}
/**
* Gets this name of the remote service this service binds to as defined by {@link Settings}.
*/
@Nullable
protected final String getComponentNameFromSettings() {
final String property = mMaster.getServiceSettingsProperty();
return property == null ? null : Settings.Secure
.getStringForUser(getContext().getContentResolver(), property, mUserId);
}
/**
* Gets the {@link ComponentName} of the remote service this service binds to, or {@code null}
* if the service is disabled.
*/
@Nullable
public final ComponentName getServiceComponentName() {
synchronized (mLock) {
return mServiceInfo == null ? null : mServiceInfo.getComponentName();
}
}
/**
* Gets the name of the of the app this service binds to, or {@code null} if the service is
* disabled.
*/
@Nullable
public final String getServicePackageName() {
final ComponentName serviceComponent = getServiceComponentName();
return serviceComponent == null ? null : serviceComponent.getPackageName();
}
/**
* Gets the user-visibile name of the service this service binds to, or {@code null} if the
* service is disabled.
*/
@Nullable
@GuardedBy("mLock")
public final CharSequence getServiceLabelLocked() {
return mServiceInfo == null ? null : mServiceInfo.loadSafeLabel(
getContext().getPackageManager(), 0 /* do not ellipsize */,
PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
}
/**
* Gets the icon the service this service binds to, or {@code null} if the service is disabled.
*/
@Nullable
@GuardedBy("mLock")
public final Drawable getServiceIconLocked() {
return mServiceInfo == null ? null
: mServiceInfo.loadIcon(getContext().getPackageManager());
}
/**
* Whether the service should log debug statements.
*/
public final boolean isDebug() {
return mMaster.debug;
}
/**
* Whether the service should log verbose statements.
*/
public final boolean isVerbose() {
return mMaster.verbose;
}
/**
* Gets the target SDK level of the service this service binds to,
* or {@code 0} if the service is disabled.
*/
public final int getTargedSdkLocked() {
return mServiceInfo == null ? 0 : mServiceInfo.applicationInfo.targetSdkVersion;
}
/**
* Gets whether the device already finished setup.
*/
protected final boolean isSetupCompletedLocked() {
final String setupComplete = Settings.Secure.getStringForUser(
getContext().getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, mUserId);
return "1".equals(setupComplete);
}
/**
* Gets the context associated with this service.
*/
protected final Context getContext() {
return mMaster.getContext();
}
// TODO(b/117779333): support proto
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("User: "); pw.println(mUserId);
pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
pw.print(prefix); pw.print("Service name: "); pw.println(getComponentNameFromSettings());
}
}