From 9484603c0fa738b67980c18b4abfd3505778ae74 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 31 Mar 2017 17:55:23 -0700 Subject: [PATCH] Framework support to improve bg check CTS tests. (Finally) introduce a new ServiceConnection callback to tell you when the binding has died. This allows you to robustly have a weak service monitoring, and also is an easy way to find out about breakages due to app updates etc. Also clean up some debug output. Test: moved to own suite and ran them. Change-Id: I526cc00816c384fa9eb1312b92406f38085cbff9 --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + core/java/android/app/IServiceConnection.aidl | 2 +- core/java/android/app/LoadedApk.java | 36 +++++++++++-------- .../android/content/ServiceConnection.java | 17 +++++++-- .../com/android/server/am/ActiveServices.java | 6 ++-- .../server/am/ActivityManagerService.java | 1 - .../com/android/server/am/ProcessList.java | 36 +++++++++---------- .../com/android/server/am/ProcessRecord.java | 4 ++- 10 files changed, 65 insertions(+), 40 deletions(-) diff --git a/api/current.txt b/api/current.txt index ec2a0e6479f0c..f25f564969d64 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9838,6 +9838,7 @@ package android.content { } public abstract interface ServiceConnection { + method public default void onBindingDead(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } diff --git a/api/system-current.txt b/api/system-current.txt index 2890041f13a1e..b05560388e942 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -10368,6 +10368,7 @@ package android.content { } public abstract interface ServiceConnection { + method public default void onBindingDead(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } diff --git a/api/test-current.txt b/api/test-current.txt index 69ea62018e997..f55cd39e4b191 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -9871,6 +9871,7 @@ package android.content { } public abstract interface ServiceConnection { + method public default void onBindingDead(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } diff --git a/core/java/android/app/IServiceConnection.aidl b/core/java/android/app/IServiceConnection.aidl index 6804071f26eb5..97042aa2919f7 100644 --- a/core/java/android/app/IServiceConnection.aidl +++ b/core/java/android/app/IServiceConnection.aidl @@ -21,6 +21,6 @@ import android.content.ComponentName; /** @hide */ oneway interface IServiceConnection { - void connected(in ComponentName name, IBinder service); + void connected(in ComponentName name, IBinder service, boolean dead); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index dbed1beb4cf79..4205db098ef6b 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -88,8 +88,8 @@ final class ServiceConnectionLeaked extends AndroidRuntimeException { * @hide */ public final class LoadedApk { - - private static final String TAG = "LoadedApk"; + static final String TAG = "LoadedApk"; + static final boolean DEBUG = false; private final ActivityThread mActivityThread; final String mPackageName; @@ -641,8 +641,7 @@ public final class LoadedApk { final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) : TextUtils.join(File.pathSeparator, zipPaths); - if (ActivityThread.localLOGV) - Slog.v(ActivityThread.TAG, "Class path: " + zip + + if (DEBUG) Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + librarySearchPath); boolean needToSetupJitProfiles = false; @@ -1371,12 +1370,14 @@ public final class LoadedApk { LoadedApk.ServiceDispatcher sd = null; ArrayMap map = mServices.get(context); if (map != null) { + if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c); sd = map.get(c); } if (sd == null) { sd = new ServiceDispatcher(c, context, handler, flags); + if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c); if (map == null) { - map = new ArrayMap(); + map = new ArrayMap<>(); mServices.put(context, map); } map.put(c, sd); @@ -1396,6 +1397,7 @@ public final class LoadedApk { if (map != null) { sd = map.get(c); if (sd != null) { + if (DEBUG) Slog.d(TAG, "Removing dispatcher " + sd + " for conn " + c); map.remove(c); sd.doForget(); if (map.size() == 0) { @@ -1461,10 +1463,11 @@ public final class LoadedApk { mDispatcher = new WeakReference(sd); } - public void connected(ComponentName name, IBinder service) throws RemoteException { + public void connected(ComponentName name, IBinder service, boolean dead) + throws RemoteException { LoadedApk.ServiceDispatcher sd = mDispatcher.get(); if (sd != null) { - sd.connected(name, service); + sd.connected(name, service, dead); } } } @@ -1533,23 +1536,23 @@ public final class LoadedApk { return mUnbindLocation; } - public void connected(ComponentName name, IBinder service) { + public void connected(ComponentName name, IBinder service, boolean dead) { if (mActivityThread != null) { - mActivityThread.post(new RunConnection(name, service, 0)); + mActivityThread.post(new RunConnection(name, service, 0, dead)); } else { - doConnected(name, service); + doConnected(name, service, dead); } } public void death(ComponentName name, IBinder service) { if (mActivityThread != null) { - mActivityThread.post(new RunConnection(name, service, 1)); + mActivityThread.post(new RunConnection(name, service, 1, false)); } else { doDeath(name, service); } } - public void doConnected(ComponentName name, IBinder service) { + public void doConnected(ComponentName name, IBinder service, boolean dead) { ServiceDispatcher.ConnectionInfo old; ServiceDispatcher.ConnectionInfo info; @@ -1594,6 +1597,9 @@ public final class LoadedApk { if (old != null) { mConnection.onServiceDisconnected(name); } + if (dead) { + mConnection.onBindingDead(name); + } // If there is a new service, it is now connected. if (service != null) { mConnection.onServiceConnected(name, service); @@ -1616,15 +1622,16 @@ public final class LoadedApk { } private final class RunConnection implements Runnable { - RunConnection(ComponentName name, IBinder service, int command) { + RunConnection(ComponentName name, IBinder service, int command, boolean dead) { mName = name; mService = service; mCommand = command; + mDead = dead; } public void run() { if (mCommand == 0) { - doConnected(mName, mService); + doConnected(mName, mService, mDead); } else if (mCommand == 1) { doDeath(mName, mService); } @@ -1633,6 +1640,7 @@ public final class LoadedApk { final ComponentName mName; final IBinder mService; final int mCommand; + final boolean mDead; } private final class DeathMonitor implements IBinder.DeathRecipient diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java index d115ce43e93da..8e428f9e9b3d0 100644 --- a/core/java/android/content/ServiceConnection.java +++ b/core/java/android/content/ServiceConnection.java @@ -37,7 +37,7 @@ public interface ServiceConnection { * @param service The IBinder of the Service's communication channel, * which you can now make calls on. */ - public void onServiceConnected(ComponentName name, IBinder service); + void onServiceConnected(ComponentName name, IBinder service); /** * Called when a connection to the Service has been lost. This typically @@ -49,5 +49,18 @@ public interface ServiceConnection { * @param name The concrete component name of the service whose * connection has been lost. */ - public void onServiceDisconnected(ComponentName name); + void onServiceDisconnected(ComponentName name); + + /** + * Called when the binding to this connection is dead. This means the + * interface will never receive another connection. The application will + * need to unbind and rebind the connection to activate it again. This may + * happen, for example, if the application hosting the service it is bound to + * has been updated. + * + * @param name The concrete component name of the service whose + * connection is dead. + */ + default void onBindingDead(ComponentName name) { + } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 8b0665c6d3121..dc98ef6b22e06 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1140,7 +1140,7 @@ public final class ActiveServices { // Service is already running, so we can immediately // publish the connection. try { - c.conn.connected(s.name, b.intent.binder); + c.conn.connected(s.name, b.intent.binder, false); } catch (Exception e) { Slog.w(TAG, "Failure sending service " + s.shortName + " to connection " + c.conn.asBinder() @@ -1194,7 +1194,7 @@ public final class ActiveServices { } if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c); try { - c.conn.connected(r.name, service); + c.conn.connected(r.name, service, false); } catch (Exception e) { Slog.w(TAG, "Failure sending service " + r.name + " to connection " + c.conn.asBinder() + @@ -2080,7 +2080,7 @@ public final class ActiveServices { // being brought down. Mark it as dead. cr.serviceDead = true; try { - cr.conn.connected(r.name, null); + cr.conn.connected(r.name, null, true); } catch (Exception e) { Slog.w(TAG, "Failure disconnecting service " + r.name + " to connection " + c.get(i).conn.asBinder() + diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b4da152a18ae1..7e13b2d734e55 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17654,7 +17654,6 @@ public class ActivityManagerService extends IActivityManager.Stub */ private final boolean cleanUpApplicationRecordLocked(ProcessRecord app, boolean restarting, boolean allowRestart, int index, boolean replacingPid) { - Slog.d(TAG, "cleanUpApplicationRecord -- " + app.pid); if (index >= 0) { removeLruProcessLocked(app); ProcessList.remove(app.pid); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 40effff77166b..0dc6788133388 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -350,58 +350,58 @@ final class ProcessList { String procState; switch (curProcState) { case ActivityManager.PROCESS_STATE_PERSISTENT: - procState = "P "; + procState = "PER "; break; case ActivityManager.PROCESS_STATE_PERSISTENT_UI: - procState = "PU"; + procState = "PERU"; break; case ActivityManager.PROCESS_STATE_TOP: - procState = "T "; + procState = "TOP"; break; case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE: - procState = "SB"; + procState = "BFGS"; break; case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE: - procState = "SF"; + procState = "FGS "; break; case ActivityManager.PROCESS_STATE_TOP_SLEEPING: - procState = "TS"; + procState = "TPSL"; break; case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: - procState = "IF"; + procState = "IMPF"; break; case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: - procState = "IB"; + procState = "IMPB"; break; case ActivityManager.PROCESS_STATE_BACKUP: - procState = "BU"; + procState = "BKUP"; break; case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: - procState = "HW"; + procState = "HVY "; break; case ActivityManager.PROCESS_STATE_SERVICE: - procState = "S "; + procState = "SVC "; break; case ActivityManager.PROCESS_STATE_RECEIVER: - procState = "R "; + procState = "RCVR"; break; case ActivityManager.PROCESS_STATE_HOME: - procState = "HO"; + procState = "HOME"; break; case ActivityManager.PROCESS_STATE_LAST_ACTIVITY: - procState = "LA"; + procState = "LAST"; break; case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: - procState = "CA"; + procState = "CAC "; break; case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: - procState = "Ca"; + procState = "CACC"; break; case ActivityManager.PROCESS_STATE_CACHED_EMPTY: - procState = "CE"; + procState = "CEM "; break; case ActivityManager.PROCESS_STATE_NONEXISTENT: - procState = "N "; + procState = "NONE"; break; default: procState = "??"; diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 2d2720458fbbe..3c5c5fd0cfe4e 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -287,7 +287,9 @@ final class ProcessRecord { pw.print(" setSchedGroup="); pw.print(setSchedGroup); pw.print(" systemNoUi="); pw.print(systemNoUi); pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); - pw.print(prefix); pw.print("vrThreadTid="); pw.print(vrThreadTid); + if (vrThreadTid != 0) { + pw.print(prefix); pw.print("vrThreadTid="); pw.println(vrThreadTid); + } pw.print(prefix); pw.print("curProcState="); pw.print(curProcState); pw.print(" repProcState="); pw.print(repProcState); pw.print(" pssProcState="); pw.print(pssProcState);