Merge "Refactored AutofillManager service classes into common code."
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user