Do not bind TextClassifierService if the packge doesn't install.

Currently, TCMS will try to bind TCS even the package doesn't install
for certain user. If there are a lot of API calls come, we may call
bindService() many times, this may cause system stability problem. We
received the system reboot problem before.

We already have preload TextClassifierService in ExtServices on primary
user but it's possible the TextClassifierService doesn't install on the
profile user or users unintall package. It's possible the package may
not be installed on non-primary user.

Users also can disable TextClassifierService from ui interface easily,
it's possible occur on both primary and non-primary users, system should
also not bind TextClassifierService in this case.

In this change, system will avoid trying to call bindService() if the
TCS deosn't install or disabled to minimize binder calls.

Bug: 188825683
Bug: 170014504
Test: atest CtsTextClassifierTestCases
Test: manual. Create work profile, turn on/off it and reboot to check
the status. Disable app from Settings and check the status.

Change-Id: Ifa2bccc0d2435712461067d650b52cb02a903c59
This commit is contained in:
Joanne Chung
2021-05-21 21:42:02 +08:00
parent 35a187904c
commit ce9903f1ed

View File

@@ -25,6 +25,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
@@ -59,6 +61,7 @@ import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
@@ -110,6 +113,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi
try {
publishBinderService(Context.TEXT_CLASSIFICATION_SERVICE, mManagerService);
mManagerService.startListenSettings();
mManagerService.startTrackingPackageChanges();
} catch (Throwable t) {
// Starting this service is not critical to the running of this device and should
// therefore not crash the device. If it fails, log the error and continue.
@@ -119,11 +123,14 @@ public final class TextClassificationManagerService extends ITextClassifierServi
@Override
public void onUserStarting(@NonNull TargetUser user) {
updatePackageStateForUser(user.getUserIdentifier());
processAnyPendingWork(user.getUserIdentifier());
}
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
// refresh if we failed earlier due to locked encrypted user
updatePackageStateForUser(user.getUserIdentifier());
// Rebind if we failed earlier due to locked encrypted user
processAnyPendingWork(user.getUserIdentifier());
}
@@ -134,6 +141,14 @@ public final class TextClassificationManagerService extends ITextClassifierServi
}
}
private void updatePackageStateForUser(int userId) {
synchronized (mManagerService.mLock) {
// Update the cached disable status, the TextClassfier may not be direct boot aware,
// we should update the disable status after user unlock
mManagerService.getUserStateLocked(userId).updatePackageStateLocked();
}
}
@Override
public void onUserStopping(@NonNull TargetUser user) {
int userId = user.getUserIdentifier();
@@ -160,6 +175,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
private final String mDefaultTextClassifierPackage;
@Nullable
private final String mSystemTextClassifierPackage;
// TODO: consider using device config to control it.
private boolean DEBUG = false;
private TextClassificationManagerService(Context context) {
mContext = Objects.requireNonNull(context);
@@ -176,6 +193,46 @@ public final class TextClassificationManagerService extends ITextClassifierServi
mSettingsListener.registerObserver();
}
void startTrackingPackageChanges() {
final PackageMonitor monitor = new PackageMonitor() {
@Override
public void onPackageAdded(String packageName, int uid) {
notifyPackageInstallStatusChange(packageName, /* installed*/ true);
}
@Override
public void onPackageRemoved(String packageName, int uid) {
notifyPackageInstallStatusChange(packageName, /* installed= */ false);
}
@Override
public void onPackageModified(String packageName) {
final int userId = getChangingUserId();
synchronized (mLock) {
final UserState userState = getUserStateLocked(userId);
final ServiceState serviceState = userState.getServiceStateLocked(packageName);
if (serviceState != null) {
serviceState.onPackageModifiedLocked();
}
}
}
private void notifyPackageInstallStatusChange(String packageName, boolean installed) {
final int userId = getChangingUserId();
synchronized (mLock) {
final UserState userState = getUserStateLocked(userId);
final ServiceState serviceState = userState.getServiceStateLocked(packageName);
if (serviceState != null) {
serviceState.onPackageInstallStatusChangeLocked(installed);
}
}
}
};
monitor.register(mContext, null, UserHandle.ALL, true);
}
@Override
public void onConnectedStateChanged(@ConnectionState int connected) {
}
@@ -452,6 +509,14 @@ public final class TextClassificationManagerService extends ITextClassifierServi
if (serviceState == null) {
Slog.d(LOG_TAG, "No configured system TextClassifierService");
callback.onFailure();
} else if (!serviceState.isInstalledLocked() || !serviceState.isEnabledLocked()) {
if (DEBUG) {
Slog.d(LOG_TAG,
serviceState.mPackageName + " is not available in user " + userId
+ ". Installed: " + serviceState.isInstalledLocked()
+ ", enabled:" + serviceState.isEnabledLocked());
}
callback.onFailure();
} else if (attemptToBind && !serviceState.bindLocked()) {
Slog.d(LOG_TAG, "Unable to bind TextClassifierService at " + methodName);
callback.onFailure();
@@ -761,6 +826,24 @@ public final class TextClassificationManagerService extends ITextClassifierServi
return serviceStates;
}
@GuardedBy("mLock")
@Nullable
private ServiceState getServiceStateLocked(String packageName) {
for (ServiceState serviceState : getAllServiceStatesLocked()) {
if (serviceState.mPackageName.equals(packageName)) {
return serviceState;
}
}
return null;
}
@GuardedBy("mLock")
private void updatePackageStateLocked() {
for (ServiceState serviceState : getAllServiceStatesLocked()) {
serviceState.updatePackageStateLocked();
}
}
void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.increaseIndent();
@@ -814,6 +897,10 @@ public final class TextClassificationManagerService extends ITextClassifierServi
ComponentName mBoundComponentName = null;
@GuardedBy("mLock")
int mBoundServiceUid = Process.INVALID_UID;
@GuardedBy("mLock")
boolean mInstalled;
@GuardedBy("mLock")
boolean mEnabled;
private ServiceState(
@UserIdInt int userId, @NonNull String packageName, boolean isTrusted) {
@@ -822,6 +909,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
mConnection = new TextClassifierServiceConnection(mUserId);
mIsTrusted = isTrusted;
mBindServiceFlags = createBindServiceFlags(packageName);
mInstalled = isPackageInstalledForUser();
mEnabled = isServiceEnabledForUser();
}
@Context.BindServiceFlags
@@ -833,6 +922,54 @@ public final class TextClassificationManagerService extends ITextClassifierServi
return flags;
}
private boolean isPackageInstalledForUser() {
try {
PackageManager packageManager = mContext.getPackageManager();
return packageManager.getPackageInfoAsUser(mPackageName, 0, mUserId) != null;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
private boolean isServiceEnabledForUser() {
PackageManager packageManager = mContext.getPackageManager();
Intent intent = new Intent(TextClassifierService.SERVICE_INTERFACE);
intent.setPackage(mPackageName);
ResolveInfo resolveInfo = packageManager.resolveServiceAsUser(intent,
PackageManager.GET_SERVICES, mUserId);
ServiceInfo serviceInfo = resolveInfo == null ? null : resolveInfo.serviceInfo;
return serviceInfo != null;
}
@GuardedBy("mLock")
@NonNull
private void onPackageInstallStatusChangeLocked(boolean installed) {
mInstalled = installed;
}
@GuardedBy("mLock")
@NonNull
private void onPackageModifiedLocked() {
mEnabled = isServiceEnabledForUser();
}
@GuardedBy("mLock")
@NonNull
private void updatePackageStateLocked() {
mInstalled = isPackageInstalledForUser();
mEnabled = isServiceEnabledForUser();
}
@GuardedBy("mLock")
boolean isInstalledLocked() {
return mInstalled;
}
@GuardedBy("mLock")
boolean isEnabledLocked() {
return mEnabled;
}
@GuardedBy("mLock")
boolean isBoundLocked() {
return mService != null;
@@ -923,6 +1060,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi
pw.printPair("userId", mUserId);
synchronized (mLock) {
pw.printPair("packageName", mPackageName);
pw.printPair("installed", mInstalled);
pw.printPair("enabled", mEnabled);
pw.printPair("boundComponentName", mBoundComponentName);
pw.printPair("isTrusted", mIsTrusted);
pw.printPair("bindServiceFlags", mBindServiceFlags);