Index: chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java b/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java |
index 688c52f1268a996ef84465233481a02cc5a1d19a..b82a90998561d7e422b5b8532f541162d7d85df6 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gsa/GSAServiceClient.java |
@@ -5,11 +5,14 @@ |
package org.chromium.chrome.browser.gsa; |
import android.annotation.SuppressLint; |
+import android.app.ActivityManager; |
import android.content.ComponentName; |
import android.content.Context; |
import android.content.Intent; |
import android.content.ServiceConnection; |
+import android.os.AsyncTask; |
import android.os.Bundle; |
+import android.os.Debug; |
import android.os.Handler; |
import android.os.IBinder; |
import android.os.Message; |
@@ -18,9 +21,15 @@ import android.os.RemoteException; |
import android.util.Log; |
import org.chromium.base.Callback; |
+import org.chromium.base.ContextUtils; |
+import org.chromium.base.TraceEvent; |
+import org.chromium.base.VisibleForTesting; |
import org.chromium.base.annotations.SuppressFBWarnings; |
+import org.chromium.base.metrics.RecordHistogram; |
import org.chromium.chrome.browser.ChromeApplication; |
+import java.util.List; |
+ |
/** |
* A simple client that connects and talks to the GSAService using Messages. |
*/ |
@@ -31,8 +40,8 @@ public class GSAServiceClient { |
* Constants for gsa communication. These should not change without corresponding changes on the |
* service side in GSA. |
*/ |
- private static final String GSA_SERVICE = |
- "com.google.android.ssb.action.SSB_SERVICE"; |
+ @VisibleForTesting |
+ static final String GSA_SERVICE = "com.google.android.ssb.action.SSB_SERVICE"; |
public static final int REQUEST_REGISTER_CLIENT = 2; |
public static final int RESPONSE_UPDATE_SSB = 3; |
@@ -40,6 +49,10 @@ public class GSAServiceClient { |
public static final String KEY_GSA_CONTEXT = "ssb_service:ssb_context"; |
public static final String KEY_GSA_PACKAGE_NAME = "ssb_service:ssb_package_name"; |
+ @VisibleForTesting |
+ static final int INVALID_PSS = -1; |
+ |
+ private static boolean sHasRecordedPss; |
/** Messenger to handle incoming messages from the service */ |
private final Messenger mMessenger; |
private final IncomingHandler mHandler; |
@@ -50,7 +63,7 @@ public class GSAServiceClient { |
/** Messenger for communicating with service. */ |
private Messenger mService; |
- |
+ private ComponentName mComponentName; |
/** |
* Handler of incoming messages from service. |
@@ -60,17 +73,85 @@ public class GSAServiceClient { |
private class IncomingHandler extends Handler { |
@Override |
public void handleMessage(Message msg) { |
- if (msg.what == RESPONSE_UPDATE_SSB) { |
- if (mService == null) return; |
- Bundle bundle = (Bundle) msg.obj; |
- String account = |
- mGsaHelper.getGSAAccountFromState(bundle.getByteArray(KEY_GSA_STATE)); |
- GSAState.getInstance(mContext.getApplicationContext()).setGsaAccount(account); |
- if (mOnMessageReceived != null) mOnMessageReceived.onResult(bundle); |
- } else { |
+ if (msg.what != RESPONSE_UPDATE_SSB) { |
super.handleMessage(msg); |
+ return; |
+ } |
+ |
+ if (mService == null) return; |
+ final Bundle bundle = (Bundle) msg.obj; |
+ String account = mGsaHelper.getGSAAccountFromState(bundle.getByteArray(KEY_GSA_STATE)); |
+ GSAState.getInstance(mContext.getApplicationContext()).setGsaAccount(account); |
+ if (sHasRecordedPss) { |
+ if (mOnMessageReceived != null) mOnMessageReceived.onResult(bundle); |
+ return; |
+ } |
+ |
+ // Getting the PSS for the GSA service process can be long, don't block the UI thread on |
+ // that. Also, don't process the callback before the PSS is known, since the callback |
+ // can lead to a service disconnect, which can lead to the framework killing the |
+ // process. Hence an AsyncTask (long operation), and processing the callback in |
+ // onPostExecute() (don't disconnect before). |
+ sHasRecordedPss = true; |
+ new AsyncTask<Void, Void, Integer>() { |
+ @Override |
+ protected Integer doInBackground(Void... params) { |
+ TraceEvent.begin("GSAServiceClient.getPssForservice"); |
+ try { |
+ // Looking for the service process is done by component name, which is |
+ // inefficient. We really want the PID, which is only accessible from within |
+ // a Binder transaction. Since the service connection is Messenger-based, |
+ // the calls are not processed from a Binder thread. The alternatives are: |
+ // 1. Override methods in the framework to append the calling PID to the |
+ // Message. |
+ // 2. Usse msg.callingUid to narrow down the search. |
+ // |
+ // (1) is dirty (and brittle), and (2) only works on L+, and still requires |
+ // to get the full list of services from ActivityManager. |
+ return getPssForService(mComponentName); |
+ } finally { |
+ TraceEvent.end("GSAServiceClient.getPssForservice"); |
+ } |
+ } |
+ |
+ @Override |
+ protected void onPostExecute(Integer pssInKB) { |
+ if (pssInKB != INVALID_PSS) { |
+ RecordHistogram.recordMemoryKBHistogram( |
+ "Search.GsaProcessMemoryPss", pssInKB); |
+ } |
+ if (mOnMessageReceived != null) mOnMessageReceived.onResult(bundle); |
+ } |
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
+ } |
+ } |
+ |
+ /** |
+ * Get the PSS used by the process hosting a service. |
+ * |
+ * @param packageName Package name of the service to search for. |
+ * @return the PSS in kB of the process hosting a service, or INVALID_PSS. |
+ */ |
+ @VisibleForTesting |
+ static int getPssForService(ComponentName componentName) { |
+ if (componentName == null) return INVALID_PSS; |
+ Context context = ContextUtils.getApplicationContext(); |
+ ActivityManager activityManager = |
+ (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); |
+ List<ActivityManager.RunningServiceInfo> services = |
+ activityManager.getRunningServices(1000); |
+ if (services == null) return INVALID_PSS; |
+ int pid = -1; |
+ for (ActivityManager.RunningServiceInfo info : services) { |
+ if (componentName.equals(info.service)) { |
+ pid = info.pid; |
+ break; |
} |
} |
+ if (pid == -1) return INVALID_PSS; |
+ Debug.MemoryInfo infos[] = activityManager.getProcessMemoryInfo(new int[] {pid}); |
+ if (infos == null || infos.length == 0) return INVALID_PSS; |
+ return infos[0].getTotalPss(); |
} |
/** |
@@ -137,6 +218,7 @@ public class GSAServiceClient { |
if (mContext == null) return; |
mService = new Messenger(service); |
+ mComponentName = name; |
try { |
Message registerClientMessage = Message.obtain( |
null, REQUEST_REGISTER_CLIENT); |