summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/am/ContentProviderConnection.java9
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java65
2 files changed, 54 insertions, 20 deletions
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index df8bff285592..be49ce4b02ce 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -265,4 +265,13 @@ public final class ContentProviderConnection extends Binder {
return mUnstableCount;
}
}
+
+ /**
+ * Returns the total number of stable and unstable references.
+ */
+ int totalRefCount() {
+ synchronized (mLock) {
+ return mStableCount + mUnstableCount;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 1155569c6b36..963e8d844c3a 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -62,6 +62,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.server.RescueParty;
@@ -261,7 +262,8 @@ public class ContentProviderHelper {
// doesn't kill our process.
Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
- boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
+ boolean lastRef = decProviderCountLocked(conn, cpr, token, stable,
+ false, false);
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we will be killed during cleaning up, bail.
@@ -697,10 +699,7 @@ public class ContentProviderHelper {
if (conn == null) {
throw new NullPointerException("connection is null");
}
- if (decProviderCountLocked(conn, null, null, stable)) {
- mService.updateOomAdjLocked(conn.provider.proc,
- OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
- }
+ decProviderCountLocked(conn, null, null, stable, true, true);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1275,30 +1274,56 @@ public class ContentProviderHelper {
@GuardedBy("mService")
private boolean decProviderCountLocked(ContentProviderConnection conn,
- ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
+ ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable,
+ boolean enforceDelay, boolean updateOomAdj) {
if (conn == null) {
cpr.removeExternalProcessHandleLocked(externalProcessToken);
return false;
}
- if (conn.decrementCount(stable) != 0) {
+
+ if (conn.totalRefCount() > 1) {
+ conn.decrementCount(stable);
return false;
}
+ if (enforceDelay) {
+ // delay the removal of the provider for 5 seconds - this optimizes for those cases
+ // where providers are released and then quickly re-acquired, causing lots of churn.
+ BackgroundThread.getHandler().postDelayed(() -> {
+ handleProviderRemoval(conn, stable, updateOomAdj);
+ }, 5 * 1000);
+ } else {
+ handleProviderRemoval(conn, stable, updateOomAdj);
+ }
+ return true;
+ }
- cpr = conn.provider;
- conn.stopAssociation();
- cpr.connections.remove(conn);
- conn.client.conProviders.remove(conn);
- if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
- // The client is more important than last activity -- note the time this
- // is happening, so we keep the old provider process around a bit as last
- // activity to avoid thrashing it.
- if (cpr.proc != null) {
- cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ private void handleProviderRemoval(ContentProviderConnection conn, boolean stable,
+ boolean updateOomAdj) {
+ synchronized (mService) {
+ // if the proc was already killed or this is not the last reference, simply exit.
+ if (conn == null || conn.provider == null || conn.decrementCount(stable) != 0) {
+ return;
+ }
+
+ final ContentProviderRecord cpr = conn.provider;
+ conn.stopAssociation();
+ cpr.connections.remove(conn);
+ conn.client.conProviders.remove(conn);
+ if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ // The client is more important than last activity -- note the time this
+ // is happening, so we keep the old provider process around a bit as last
+ // activity to avoid thrashing it.
+ if (cpr.proc != null) {
+ cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ }
+ }
+ mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
+ cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
+ if (updateOomAdj) {
+ mService.updateOomAdjLocked(conn.provider.proc,
+ OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
}
}
- mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
- cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
- return true;
}
/**