Merge "Fix potential deadlock in getAutofillOptions()." into rvc-dev

This commit is contained in:
Joanne Chung
2020-03-25 02:21:47 +00:00
committed by Android (Google) Code Review
2 changed files with 248 additions and 138 deletions

View File

@@ -50,6 +50,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import android.os.ShellCallback; import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
@@ -63,6 +64,7 @@ import android.util.LocalLog;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.view.autofill.AutofillId; import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.SmartSuggestionMode; import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -151,6 +153,7 @@ public final class AutofillManagerService
private final LocalLog mWtfHistory = new LocalLog(50); private final LocalLog mWtfHistory = new LocalLog(50);
private final AutofillCompatState mAutofillCompatState = new AutofillCompatState(); private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
private final DisabledInfoCache mDisabledInfoCache = new DisabledInfoCache();
private final LocalService mLocalService = new LocalService(); private final LocalService mLocalService = new LocalService();
private final ActivityManagerInternal mAm; private final ActivityManagerInternal mAm;
@@ -302,14 +305,15 @@ public final class AutofillManagerService
@Override // from AbstractMasterSystemService @Override // from AbstractMasterSystemService
protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId, protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
boolean disabled) { boolean disabled) {
return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, mWtfHistory,
mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled); resolvedUserId, mUi, mAutofillCompatState, disabled, mDisabledInfoCache);
} }
@Override // AbstractMasterSystemService @Override // AbstractMasterSystemService
protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service, protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
@UserIdInt int userId) { @UserIdInt int userId) {
service.destroyLocked(); service.destroyLocked();
mDisabledInfoCache.remove(userId);
mAutofillCompatState.removeCompatibilityModeRequests(userId); mAutofillCompatState.removeCompatibilityModeRequests(userId);
} }
@@ -835,15 +839,10 @@ public final class AutofillManagerService
private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId, private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
String packageName) { String packageName) {
synchronized (mLock) { options.appDisabledExpiration =
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); mDisabledInfoCache.getAppDisabledExpiration(userId, packageName);
if (service != null) { options.disabledActivities =
options.appDisabledExpiration = service.getAppDisabledExpirationLocked( mDisabledInfoCache.getAppDisabledActivities(userId, packageName);
packageName);
options.disabledActivities = service.getAppDisabledActivitiesLocked(
packageName);
}
}
} }
} }
@@ -866,6 +865,234 @@ public final class AutofillManagerService
} }
} }
/**
* Stores autofill disable information, i.e. {@link AutofillDisabledInfo}, keyed by user id.
* The information is cleaned up when the service is removed.
*/
static final class DisabledInfoCache {
private final Object mLock = new Object();
@GuardedBy("mLock")
private final SparseArray<AutofillDisabledInfo> mCache = new SparseArray<>();
void remove(@UserIdInt int userId) {
synchronized (mLock) {
mCache.remove(userId);
}
}
void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
long expiration) {
Preconditions.checkNotNull(packageName);
synchronized (mLock) {
AutofillDisabledInfo info =
getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
info.putDisableAppsLocked(packageName, expiration);
}
}
void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
long expiration) {
Preconditions.checkNotNull(componentName);
synchronized (mLock) {
AutofillDisabledInfo info =
getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
info.putDisableActivityLocked(componentName, expiration);
}
}
boolean isAutofillDisabledLocked(@UserIdInt int userId,
@NonNull ComponentName componentName) {
Preconditions.checkNotNull(componentName);
final boolean disabled;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
disabled = info != null ? info.isAutofillDisabledLocked(componentName) : false;
}
return disabled;
}
long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
Preconditions.checkNotNull(packageName);
final Long expiration;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
expiration = info != null ? info.getAppDisabledExpirationLocked(packageName) : 0;
}
return expiration;
}
@Nullable
ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
@NonNull String packageName) {
Preconditions.checkNotNull(packageName);
final ArrayMap<String, Long> disabledList;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
disabledList =
info != null ? info.getAppDisabledActivitiesLocked(packageName) : null;
}
return disabledList;
}
void dump(@UserIdInt int userId, String prefix, PrintWriter pw) {
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
if (info != null) {
info.dumpLocked(prefix, pw);
}
}
}
@NonNull
private AutofillDisabledInfo getOrCreateAutofillDisabledInfoByUserIdLocked(
@UserIdInt int userId) {
AutofillDisabledInfo info = mCache.get(userId);
if (info == null) {
info = new AutofillDisabledInfo();
mCache.put(userId, info);
}
return info;
}
}
/**
* The autofill disable information.
* <p>
* This contains disable information set by the AutofillService, e.g. disabled application
* expiration, disable activity expiration.
*/
private static final class AutofillDisabledInfo {
/**
* Apps disabled by the service; key is package name, value is when they will be enabled
* again.
*/
private ArrayMap<String, Long> mDisabledApps;
/**
* Activities disabled by the service; key is component name, value is when they will be
* enabled again.
*/
private ArrayMap<ComponentName, Long> mDisabledActivities;
void putDisableAppsLocked(@NonNull String packageName, long expiration) {
if (mDisabledApps == null) {
mDisabledApps = new ArrayMap<>(1);
}
mDisabledApps.put(packageName, expiration);
}
void putDisableActivityLocked(@NonNull ComponentName componentName, long expiration) {
if (mDisabledActivities == null) {
mDisabledActivities = new ArrayMap<>(1);
}
mDisabledActivities.put(componentName, expiration);
}
long getAppDisabledExpirationLocked(@NonNull String packageName) {
if (mDisabledApps == null) {
return 0;
}
final Long expiration = mDisabledApps.get(packageName);
return expiration != null ? expiration : 0;
}
ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
if (mDisabledActivities != null) {
final int size = mDisabledActivities.size();
ArrayMap<String, Long> disabledList = null;
for (int i = 0; i < size; i++) {
final ComponentName component = mDisabledActivities.keyAt(i);
if (packageName.equals(component.getPackageName())) {
if (disabledList == null) {
disabledList = new ArrayMap<>();
}
final long expiration = mDisabledActivities.valueAt(i);
disabledList.put(component.flattenToShortString(), expiration);
}
}
return disabledList;
}
return null;
}
boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
// Check activities first.
long elapsedTime = 0;
if (mDisabledActivities != null) {
elapsedTime = SystemClock.elapsedRealtime();
final Long expiration = mDisabledActivities.get(componentName);
if (expiration != null) {
if (expiration >= elapsedTime) return true;
// Restriction expired - clean it up.
if (sVerbose) {
Slog.v(TAG, "Removing " + componentName.toShortString()
+ " from disabled list");
}
mDisabledActivities.remove(componentName);
}
}
// Then check apps.
final String packageName = componentName.getPackageName();
if (mDisabledApps == null) return false;
final Long expiration = mDisabledApps.get(packageName);
if (expiration == null) return false;
if (elapsedTime == 0) {
elapsedTime = SystemClock.elapsedRealtime();
}
if (expiration >= elapsedTime) return true;
// Restriction expired - clean it up.
if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list");
mDisabledApps.remove(packageName);
return false;
}
void dumpLocked(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("Disabled apps: ");
if (mDisabledApps == null) {
pw.println("N/A");
} else {
final int size = mDisabledApps.size();
pw.println(size);
final StringBuilder builder = new StringBuilder();
final long now = SystemClock.elapsedRealtime();
for (int i = 0; i < size; i++) {
final String packageName = mDisabledApps.keyAt(i);
final long expiration = mDisabledApps.valueAt(i);
builder.append(prefix).append(prefix)
.append(i).append(". ").append(packageName).append(": ");
TimeUtils.formatDuration((expiration - now), builder);
builder.append('\n');
}
pw.println(builder);
}
pw.print(prefix); pw.print("Disabled activities: ");
if (mDisabledActivities == null) {
pw.println("N/A");
} else {
final int size = mDisabledActivities.size();
pw.println(size);
final StringBuilder builder = new StringBuilder();
final long now = SystemClock.elapsedRealtime();
for (int i = 0; i < size; i++) {
final ComponentName component = mDisabledActivities.keyAt(i);
final long expiration = mDisabledActivities.valueAt(i);
builder.append(prefix).append(prefix)
.append(i).append(". ").append(component).append(": ");
TimeUtils.formatDuration((expiration - now), builder);
builder.append('\n');
}
pw.println(builder);
}
}
}
/** /**
* Compatibility mode metadata associated with all services. * Compatibility mode metadata associated with all services.
* *

View File

@@ -67,7 +67,6 @@ import android.util.LocalLog;
import android.util.Pair; import android.util.Pair;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.autofill.AutofillId; import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager; import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.SmartSuggestionMode; import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -80,6 +79,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState; import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks; import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
import com.android.server.autofill.ui.AutoFillUI; import com.android.server.autofill.ui.AutoFillUI;
import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -90,7 +90,6 @@ import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
/** /**
* Bridge between the {@code system_server}'s {@link AutofillManagerService} and the * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
* app's {@link IAutoFillService} implementation. * app's {@link IAutoFillService} implementation.
@@ -124,19 +123,6 @@ final class AutofillManagerServiceImpl
@Nullable @Nullable
private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService; private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
/**
* Apps disabled by the service; key is package name, value is when they will be enabled again.
*/
@GuardedBy("mLock")
private ArrayMap<String, Long> mDisabledApps;
/**
* Activities disabled by the service; key is component name, value is when they will be enabled
* again.
*/
@GuardedBy("mLock")
private ArrayMap<ComponentName, Long> mDisabledActivities;
/** /**
* Data used for field classification. * Data used for field classification.
*/ */
@@ -186,10 +172,12 @@ final class AutofillManagerServiceImpl
private final ContentCaptureManagerInternal mContentCaptureManagerInternal; private final ContentCaptureManagerInternal mContentCaptureManagerInternal;
private final DisabledInfoCache mDisabledInfoCache;
AutofillManagerServiceImpl(AutofillManagerService master, Object lock, AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui, LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
AutofillCompatState autofillCompatState, AutofillCompatState autofillCompatState,
boolean disabled) { boolean disabled, DisabledInfoCache disableCache) {
super(master, lock, userId); super(master, lock, userId);
mUiLatencyHistory = uiLatencyHistory; mUiLatencyHistory = uiLatencyHistory;
@@ -200,7 +188,7 @@ final class AutofillManagerServiceImpl
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
mContentCaptureManagerInternal = LocalServices.getService( mContentCaptureManagerInternal = LocalServices.getService(
ContentCaptureManagerInternal.class); ContentCaptureManagerInternal.class);
mDisabledInfoCache = disableCache;
updateLocked(disabled); updateLocked(disabled);
} }
@@ -1045,45 +1033,7 @@ final class AutofillManagerServiceImpl
pw.println(isInlineSuggestionsEnabled()); pw.println(isInlineSuggestionsEnabled());
pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
pw.print(prefix); pw.print("Disabled apps: "); mDisabledInfoCache.dump(mUserId, prefix, pw);
if (mDisabledApps == null) {
pw.println("N/A");
} else {
final int size = mDisabledApps.size();
pw.println(size);
final StringBuilder builder = new StringBuilder();
final long now = SystemClock.elapsedRealtime();
for (int i = 0; i < size; i++) {
final String packageName = mDisabledApps.keyAt(i);
final long expiration = mDisabledApps.valueAt(i);
builder.append(prefix).append(prefix)
.append(i).append(". ").append(packageName).append(": ");
TimeUtils.formatDuration((expiration - now), builder);
builder.append('\n');
}
pw.println(builder);
}
pw.print(prefix); pw.print("Disabled activities: ");
if (mDisabledActivities == null) {
pw.println("N/A");
} else {
final int size = mDisabledActivities.size();
pw.println(size);
final StringBuilder builder = new StringBuilder();
final long now = SystemClock.elapsedRealtime();
for (int i = 0; i < size; i++) {
final ComponentName component = mDisabledActivities.keyAt(i);
final long expiration = mDisabledActivities.valueAt(i);
builder.append(prefix).append(prefix)
.append(i).append(". ").append(component).append(": ");
TimeUtils.formatDuration((expiration - now), builder);
builder.append('\n');
}
pw.println(builder);
}
final int size = mSessions.size(); final int size = mSessions.size();
if (size == 0) { if (size == 0) {
@@ -1480,15 +1430,13 @@ final class AutofillManagerServiceImpl
void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId, void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId,
boolean compatMode) { boolean compatMode) {
synchronized (mLock) { synchronized (mLock) {
if (mDisabledApps == null) {
mDisabledApps = new ArrayMap<>(1);
}
long expiration = SystemClock.elapsedRealtime() + duration; long expiration = SystemClock.elapsedRealtime() + duration;
// Protect it against overflow // Protect it against overflow
if (expiration < 0) { if (expiration < 0) {
expiration = Long.MAX_VALUE; expiration = Long.MAX_VALUE;
} }
mDisabledApps.put(packageName, expiration); mDisabledInfoCache.addDisabledAppLocked(mUserId, packageName, expiration);
int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration; int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP, mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
packageName, getServicePackageName(), sessionId, compatMode) packageName, getServicePackageName(), sessionId, compatMode)
@@ -1502,15 +1450,12 @@ final class AutofillManagerServiceImpl
void disableAutofillForActivity(@NonNull ComponentName componentName, long duration, void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
int sessionId, boolean compatMode) { int sessionId, boolean compatMode) {
synchronized (mLock) { synchronized (mLock) {
if (mDisabledActivities == null) {
mDisabledActivities = new ArrayMap<>(1);
}
long expiration = SystemClock.elapsedRealtime() + duration; long expiration = SystemClock.elapsedRealtime() + duration;
// Protect it against overflow // Protect it against overflow
if (expiration < 0) { if (expiration < 0) {
expiration = Long.MAX_VALUE; expiration = Long.MAX_VALUE;
} }
mDisabledActivities.put(componentName, expiration); mDisabledInfoCache.addDisabledActivityLocked(mUserId, componentName, expiration);
final int intDuration = duration > Integer.MAX_VALUE final int intDuration = duration > Integer.MAX_VALUE
? Integer.MAX_VALUE ? Integer.MAX_VALUE
: (int) duration; : (int) duration;
@@ -1528,74 +1473,12 @@ final class AutofillManagerServiceImpl
} }
} }
// Called by AutofillManagerService
long getAppDisabledExpirationLocked(@NonNull String packageName) {
if (mDisabledApps == null) {
return 0;
}
final Long expiration = mDisabledApps.get(packageName);
return expiration != null ? expiration : 0;
}
// Called by AutofillManagerService
@Nullable
ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
if (mDisabledActivities != null) {
final int size = mDisabledActivities.size();
ArrayMap<String, Long> disabledList = null;
for (int i = 0; i < size; i++) {
final ComponentName component = mDisabledActivities.keyAt(i);
if (packageName.equals(component.getPackageName())) {
if (disabledList == null) {
disabledList = new ArrayMap<>();
}
final long expiration = mDisabledActivities.valueAt(i);
disabledList.put(component.flattenToShortString(), expiration);
}
}
return disabledList;
}
return null;
}
/** /**
* Checks if autofill is disabled by service to the given activity. * Checks if autofill is disabled by service to the given activity.
*/ */
@GuardedBy("mLock") @GuardedBy("mLock")
private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) { private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
// Check activities first. return mDisabledInfoCache.isAutofillDisabledLocked(mUserId, componentName);
long elapsedTime = 0;
if (mDisabledActivities != null) {
elapsedTime = SystemClock.elapsedRealtime();
final Long expiration = mDisabledActivities.get(componentName);
if (expiration != null) {
if (expiration >= elapsedTime) return true;
// Restriction expired - clean it up.
if (sVerbose) {
Slog.v(TAG, "Removing " + componentName.toShortString()
+ " from disabled list");
}
mDisabledActivities.remove(componentName);
}
}
// Then check apps.
final String packageName = componentName.getPackageName();
if (mDisabledApps == null) return false;
final Long expiration = mDisabledApps.get(packageName);
if (expiration == null) return false;
if (elapsedTime == 0) {
elapsedTime = SystemClock.elapsedRealtime();
}
if (expiration >= elapsedTime) return true;
// Restriction expired - clean it up.
if (sVerbose) Slog.v(TAG, "Removing " + packageName + " from disabled list");
mDisabledApps.remove(packageName);
return false;
} }
// Called by AutofillManager, checks UID. // Called by AutofillManager, checks UID.