Loading services/core/java/com/android/server/utils/ManagedApplicationService.java +265 −58 Original line number Diff line number Diff line Loading @@ -22,14 +22,18 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.IInterface; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.util.Slog; import java.text.SimpleDateFormat; import java.util.Objects; import java.util.Date; /** * Manages the lifecycle of an application-provided service bound from system server. Loading @@ -39,6 +43,40 @@ import java.util.Objects; public class ManagedApplicationService { private final String TAG = getClass().getSimpleName(); /** * Attempt to reconnect service forever if an onBindingDied or onServiceDisconnected event * is received. */ public static final int RETRY_FOREVER = 1; /** * Never attempt to reconnect the service - a single onBindingDied or onServiceDisconnected * event will cause this to fully unbind the service and never attempt to reconnect. */ public static final int RETRY_NEVER = 2; /** * Attempt to reconnect the service until the maximum number of retries is reached, then stop. * * The first retry will occur MIN_RETRY_DURATION_MS after the disconnection, and each * subsequent retry will occur after 2x the duration used for the previous retry up to the * MAX_RETRY_DURATION_MS duration. * * In this case, retries mean a full unbindService/bindService pair to handle cases when the * usual service re-connection logic in ActiveServices has very high backoff times or when the * serviceconnection has fully died due to a package update or similar. */ public static final int RETRY_BEST_EFFORT = 3; // Maximum number of retries before giving up (for RETRY_BEST_EFFORT). private static final int MAX_RETRY_COUNT = 4; // Max time between retry attempts. private static final long MAX_RETRY_DURATION_MS = 16000; // Min time between retry attempts. private static final long MIN_RETRY_DURATION_MS = 2000; // Time since the last retry attempt after which to clear the retry attempt counter. private static final long RETRY_RESET_TIME_MS = MAX_RETRY_DURATION_MS * 4; private final Context mContext; private final int mUserId; private final ComponentName mComponent; Loading @@ -46,27 +84,75 @@ public class ManagedApplicationService { private final String mSettingsAction; private final BinderChecker mChecker; private final boolean mIsImportant; private final DeathRecipient mDeathRecipient = new DeathRecipient() { @Override public void binderDied() { synchronized (mLock) { mBoundInterface = null; } } }; private final int mRetryType; private final Handler mHandler; private final Runnable mRetryRunnable = this::doRetry; private final EventCallback mEventCb; private final Object mLock = new Object(); // State protected by mLock private ServiceConnection mPendingConnection; private ServiceConnection mConnection; private IInterface mBoundInterface; private PendingEvent mPendingEvent; private int mRetryCount; private long mLastRetryTimeMs; private long mNextRetryDurationMs = MIN_RETRY_DURATION_MS; private boolean mRetrying; public static interface LogFormattable { String toLogString(SimpleDateFormat dateFormat); } /** * Lifecycle event of this managed service. */ public static class LogEvent implements LogFormattable { public static final int EVENT_CONNECTED = 1; public static final int EVENT_DISCONNECTED = 2; public static final int EVENT_BINDING_DIED = 3; public static final int EVENT_STOPPED_PERMANENTLY = 4; // Time of the events in "current time ms" timebase. public final long timestamp; // Name of the component for this system service. public final ComponentName component; // ID of the event that occurred. public final int event; public LogEvent(long timestamp, ComponentName component, int event) { this.timestamp = timestamp; this.component = component; this.event = event; } @Override public String toLogString(SimpleDateFormat dateFormat) { return dateFormat.format(new Date(timestamp)) + " " + eventToString(event) + " Managed Service: " + ((component == null) ? "None" : component.flattenToString()); } public static String eventToString(int event) { switch (event) { case EVENT_CONNECTED: return "Connected"; case EVENT_DISCONNECTED: return "Disconnected"; case EVENT_BINDING_DIED: return "Binding Died For"; case EVENT_STOPPED_PERMANENTLY: return "Permanently Stopped"; default: return "Unknown Event Occurred"; } } } private ManagedApplicationService(final Context context, final ComponentName component, final int userId, int clientLabel, String settingsAction, BinderChecker binderChecker, boolean isImportant) { BinderChecker binderChecker, boolean isImportant, int retryType, Handler handler, EventCallback eventCallback) { mContext = context; mComponent = component; mUserId = userId; Loading @@ -74,6 +160,9 @@ public class ManagedApplicationService { mSettingsAction = settingsAction; mChecker = binderChecker; mIsImportant = isImportant; mRetryType = retryType; mHandler = handler; mEventCb = eventCallback; } /** Loading @@ -91,6 +180,16 @@ public class ManagedApplicationService { void runEvent(IInterface service) throws RemoteException; } /** * Implement to be notified about any problems with remote service. */ public interface EventCallback { /** * Called when an sevice lifecycle event occurs. */ void onServiceEvent(LogEvent event); } /** * Create a new ManagedApplicationService object but do not yet bind to the user service. * Loading @@ -104,14 +203,19 @@ public class ManagedApplicationService { * @param binderChecker an interface used to validate the returned binder object, or null if * this interface is unchecked. * @param isImportant bind the user service with BIND_IMPORTANT. * @param retryType reconnect behavior to have when bound service is disconnected. * @param handler the Handler to use for retries and delivering EventCallbacks. * @param eventCallback a callback used to deliver disconnection events, or null if you * don't care. * @return a ManagedApplicationService instance. */ public static ManagedApplicationService build(@NonNull final Context context, @NonNull final ComponentName component, final int userId, int clientLabel, @Nullable String settingsAction, @Nullable BinderChecker binderChecker, boolean isImportant) { boolean isImportant, int retryType, @NonNull Handler handler, @Nullable EventCallback eventCallback) { return new ManagedApplicationService(context, component, userId, clientLabel, settingsAction, binderChecker, isImportant); settingsAction, binderChecker, isImportant, retryType, handler, eventCallback); } Loading Loading @@ -145,7 +249,6 @@ public class ManagedApplicationService { return true; } /** * Send an event to run as soon as the binder interface is available. * Loading Loading @@ -174,15 +277,13 @@ public class ManagedApplicationService { */ public void disconnect() { synchronized (mLock) { // Wipe out pending connections mPendingConnection = null; // Unbind existing connection, if it exists if (mConnection != null) { mContext.unbindService(mConnection); mConnection = null; if (mConnection == null) { return; } mContext.unbindService(mConnection); mConnection = null; mBoundInterface = null; } } Loading @@ -192,7 +293,7 @@ public class ManagedApplicationService { */ public void connect() { synchronized (mLock) { if (mConnection != null || mPendingConnection != null) { if (mConnection != null) { // We're already connected or are trying to connect return; } Loading @@ -206,72 +307,101 @@ public class ManagedApplicationService { PendingIntent.getActivity(mContext, 0, new Intent(mSettingsAction), 0)); } final ServiceConnection serviceConnection = new ServiceConnection() { mConnection = new ServiceConnection() { @Override public void onBindingDied(ComponentName componentName) { final long timestamp = System.currentTimeMillis(); Slog.w(TAG, "Service binding died: " + componentName); synchronized (mLock) { if (mConnection != this) { return; } mHandler.post(() -> { mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, LogEvent.EVENT_BINDING_DIED)); }); mBoundInterface = null; startRetriesLocked(); } } @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { final long timestamp = System.currentTimeMillis(); Slog.i(TAG, "Service connected: " + componentName); IInterface iface = null; PendingEvent pendingEvent = null; synchronized (mLock) { if (mPendingConnection == this) { // No longer pending, remove from pending connection mPendingConnection = null; mConnection = this; } else { // Service connection wasn't pending, must have been disconnected mContext.unbindService(this); if (mConnection != this) { // Must've been unbound. return; } mHandler.post(() -> { mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, LogEvent.EVENT_CONNECTED)); }); stopRetriesLocked(); try { iBinder.linkToDeath(mDeathRecipient, 0); mBoundInterface = null; if (mChecker != null) { mBoundInterface = mChecker.asInterface(iBinder); if (!mChecker.checkType(mBoundInterface)) { // Received an invalid binder, disconnect mContext.unbindService(this); // Received an invalid binder, disconnect. mBoundInterface = null; Slog.w(TAG, "Invalid binder from " + componentName); startRetriesLocked(); return; } iface = mBoundInterface; pendingEvent = mPendingEvent; mPendingEvent = null; } } catch (RemoteException e) { // DOA Slog.w(TAG, "Unable to bind service: " + componentName, e); mBoundInterface = null; } } if (iface != null && pendingEvent != null) { try { pendingEvent.runEvent(iface); } catch (RuntimeException | RemoteException ex) { Slog.e(TAG, "Received exception from user service: ", ex); startRetriesLocked(); } } } @Override public void onServiceDisconnected(ComponentName componentName) { final long timestamp = System.currentTimeMillis(); Slog.w(TAG, "Service disconnected: " + componentName); mConnection = null; synchronized (mLock) { if (mConnection != this) { return; } mHandler.post(() -> { mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, LogEvent.EVENT_DISCONNECTED)); }); mBoundInterface = null; startRetriesLocked(); } } }; mPendingConnection = serviceConnection; int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; if (mIsImportant) { flags |= Context.BIND_IMPORTANT; } try { if (!mContext.bindServiceAsUser(intent, serviceConnection, flags, if (!mContext.bindServiceAsUser(intent, mConnection, flags, new UserHandle(mUserId))) { Slog.w(TAG, "Unable to bind service: " + intent); startRetriesLocked(); } } catch (SecurityException e) { Slog.w(TAG, "Unable to bind service: " + intent, e); startRetriesLocked(); } } } Loading @@ -279,4 +409,81 @@ public class ManagedApplicationService { private boolean matches(final ComponentName component, final int userId) { return Objects.equals(mComponent, component) && mUserId == userId; } private void startRetriesLocked() { if (checkAndDeliverServiceDiedCbLocked()) { // If we delivered the service callback, disconnect and stop retrying. disconnect(); return; } if (mRetrying) { // Retry already queued, don't queue a new one. return; } mRetrying = true; queueRetryLocked(); } private void stopRetriesLocked() { mRetrying = false; mHandler.removeCallbacks(mRetryRunnable); } private void queueRetryLocked() { long now = SystemClock.uptimeMillis(); if ((now - mLastRetryTimeMs) > RETRY_RESET_TIME_MS) { // It's been longer than the reset time since we last had to retry. Re-initialize. mNextRetryDurationMs = MIN_RETRY_DURATION_MS; mRetryCount = 0; } mLastRetryTimeMs = now; mHandler.postDelayed(mRetryRunnable, mNextRetryDurationMs); mNextRetryDurationMs = Math.min(2 * mNextRetryDurationMs, MAX_RETRY_DURATION_MS); mRetryCount++; } private boolean checkAndDeliverServiceDiedCbLocked() { if (mRetryType == RETRY_NEVER || (mRetryType == RETRY_BEST_EFFORT && mRetryCount >= MAX_RETRY_COUNT)) { // If we never retry, or we've exhausted our retries, post the onServiceDied callback. Slog.e(TAG, "Service " + mComponent + " has died too much, not retrying."); if (mEventCb != null) { final long timestamp = System.currentTimeMillis(); mHandler.post(() -> { mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, LogEvent.EVENT_STOPPED_PERMANENTLY)); }); } return true; } return false; } private void doRetry() { synchronized (mLock) { if (mConnection == null) { // We disconnected for good. Don't attempt to retry. return; } if (!mRetrying) { // We successfully connected. Don't attempt to retry. return; } Slog.i(TAG, "Attempting to reconnect " + mComponent + "..."); // While frameworks may restart the remote Service if we stay bound, we have little // control of the backoff timing for reconnecting the service. In the event of a // process crash, the backoff time can be very large (1-30 min), which is not // acceptable for the types of services this is used for. Instead force an unbind/bind // sequence to cause a more immediate retry. disconnect(); if (checkAndDeliverServiceDiedCbLocked()) { // No more retries. return; } queueRetryLocked(); connect(); } } } services/core/java/com/android/server/vr/VrManagerService.java +135 −47 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/utils/ManagedApplicationService.java +265 −58 Original line number Diff line number Diff line Loading @@ -22,14 +22,18 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.IInterface; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.util.Slog; import java.text.SimpleDateFormat; import java.util.Objects; import java.util.Date; /** * Manages the lifecycle of an application-provided service bound from system server. Loading @@ -39,6 +43,40 @@ import java.util.Objects; public class ManagedApplicationService { private final String TAG = getClass().getSimpleName(); /** * Attempt to reconnect service forever if an onBindingDied or onServiceDisconnected event * is received. */ public static final int RETRY_FOREVER = 1; /** * Never attempt to reconnect the service - a single onBindingDied or onServiceDisconnected * event will cause this to fully unbind the service and never attempt to reconnect. */ public static final int RETRY_NEVER = 2; /** * Attempt to reconnect the service until the maximum number of retries is reached, then stop. * * The first retry will occur MIN_RETRY_DURATION_MS after the disconnection, and each * subsequent retry will occur after 2x the duration used for the previous retry up to the * MAX_RETRY_DURATION_MS duration. * * In this case, retries mean a full unbindService/bindService pair to handle cases when the * usual service re-connection logic in ActiveServices has very high backoff times or when the * serviceconnection has fully died due to a package update or similar. */ public static final int RETRY_BEST_EFFORT = 3; // Maximum number of retries before giving up (for RETRY_BEST_EFFORT). private static final int MAX_RETRY_COUNT = 4; // Max time between retry attempts. private static final long MAX_RETRY_DURATION_MS = 16000; // Min time between retry attempts. private static final long MIN_RETRY_DURATION_MS = 2000; // Time since the last retry attempt after which to clear the retry attempt counter. private static final long RETRY_RESET_TIME_MS = MAX_RETRY_DURATION_MS * 4; private final Context mContext; private final int mUserId; private final ComponentName mComponent; Loading @@ -46,27 +84,75 @@ public class ManagedApplicationService { private final String mSettingsAction; private final BinderChecker mChecker; private final boolean mIsImportant; private final DeathRecipient mDeathRecipient = new DeathRecipient() { @Override public void binderDied() { synchronized (mLock) { mBoundInterface = null; } } }; private final int mRetryType; private final Handler mHandler; private final Runnable mRetryRunnable = this::doRetry; private final EventCallback mEventCb; private final Object mLock = new Object(); // State protected by mLock private ServiceConnection mPendingConnection; private ServiceConnection mConnection; private IInterface mBoundInterface; private PendingEvent mPendingEvent; private int mRetryCount; private long mLastRetryTimeMs; private long mNextRetryDurationMs = MIN_RETRY_DURATION_MS; private boolean mRetrying; public static interface LogFormattable { String toLogString(SimpleDateFormat dateFormat); } /** * Lifecycle event of this managed service. */ public static class LogEvent implements LogFormattable { public static final int EVENT_CONNECTED = 1; public static final int EVENT_DISCONNECTED = 2; public static final int EVENT_BINDING_DIED = 3; public static final int EVENT_STOPPED_PERMANENTLY = 4; // Time of the events in "current time ms" timebase. public final long timestamp; // Name of the component for this system service. public final ComponentName component; // ID of the event that occurred. public final int event; public LogEvent(long timestamp, ComponentName component, int event) { this.timestamp = timestamp; this.component = component; this.event = event; } @Override public String toLogString(SimpleDateFormat dateFormat) { return dateFormat.format(new Date(timestamp)) + " " + eventToString(event) + " Managed Service: " + ((component == null) ? "None" : component.flattenToString()); } public static String eventToString(int event) { switch (event) { case EVENT_CONNECTED: return "Connected"; case EVENT_DISCONNECTED: return "Disconnected"; case EVENT_BINDING_DIED: return "Binding Died For"; case EVENT_STOPPED_PERMANENTLY: return "Permanently Stopped"; default: return "Unknown Event Occurred"; } } } private ManagedApplicationService(final Context context, final ComponentName component, final int userId, int clientLabel, String settingsAction, BinderChecker binderChecker, boolean isImportant) { BinderChecker binderChecker, boolean isImportant, int retryType, Handler handler, EventCallback eventCallback) { mContext = context; mComponent = component; mUserId = userId; Loading @@ -74,6 +160,9 @@ public class ManagedApplicationService { mSettingsAction = settingsAction; mChecker = binderChecker; mIsImportant = isImportant; mRetryType = retryType; mHandler = handler; mEventCb = eventCallback; } /** Loading @@ -91,6 +180,16 @@ public class ManagedApplicationService { void runEvent(IInterface service) throws RemoteException; } /** * Implement to be notified about any problems with remote service. */ public interface EventCallback { /** * Called when an sevice lifecycle event occurs. */ void onServiceEvent(LogEvent event); } /** * Create a new ManagedApplicationService object but do not yet bind to the user service. * Loading @@ -104,14 +203,19 @@ public class ManagedApplicationService { * @param binderChecker an interface used to validate the returned binder object, or null if * this interface is unchecked. * @param isImportant bind the user service with BIND_IMPORTANT. * @param retryType reconnect behavior to have when bound service is disconnected. * @param handler the Handler to use for retries and delivering EventCallbacks. * @param eventCallback a callback used to deliver disconnection events, or null if you * don't care. * @return a ManagedApplicationService instance. */ public static ManagedApplicationService build(@NonNull final Context context, @NonNull final ComponentName component, final int userId, int clientLabel, @Nullable String settingsAction, @Nullable BinderChecker binderChecker, boolean isImportant) { boolean isImportant, int retryType, @NonNull Handler handler, @Nullable EventCallback eventCallback) { return new ManagedApplicationService(context, component, userId, clientLabel, settingsAction, binderChecker, isImportant); settingsAction, binderChecker, isImportant, retryType, handler, eventCallback); } Loading Loading @@ -145,7 +249,6 @@ public class ManagedApplicationService { return true; } /** * Send an event to run as soon as the binder interface is available. * Loading Loading @@ -174,15 +277,13 @@ public class ManagedApplicationService { */ public void disconnect() { synchronized (mLock) { // Wipe out pending connections mPendingConnection = null; // Unbind existing connection, if it exists if (mConnection != null) { mContext.unbindService(mConnection); mConnection = null; if (mConnection == null) { return; } mContext.unbindService(mConnection); mConnection = null; mBoundInterface = null; } } Loading @@ -192,7 +293,7 @@ public class ManagedApplicationService { */ public void connect() { synchronized (mLock) { if (mConnection != null || mPendingConnection != null) { if (mConnection != null) { // We're already connected or are trying to connect return; } Loading @@ -206,72 +307,101 @@ public class ManagedApplicationService { PendingIntent.getActivity(mContext, 0, new Intent(mSettingsAction), 0)); } final ServiceConnection serviceConnection = new ServiceConnection() { mConnection = new ServiceConnection() { @Override public void onBindingDied(ComponentName componentName) { final long timestamp = System.currentTimeMillis(); Slog.w(TAG, "Service binding died: " + componentName); synchronized (mLock) { if (mConnection != this) { return; } mHandler.post(() -> { mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, LogEvent.EVENT_BINDING_DIED)); }); mBoundInterface = null; startRetriesLocked(); } } @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { final long timestamp = System.currentTimeMillis(); Slog.i(TAG, "Service connected: " + componentName); IInterface iface = null; PendingEvent pendingEvent = null; synchronized (mLock) { if (mPendingConnection == this) { // No longer pending, remove from pending connection mPendingConnection = null; mConnection = this; } else { // Service connection wasn't pending, must have been disconnected mContext.unbindService(this); if (mConnection != this) { // Must've been unbound. return; } mHandler.post(() -> { mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, LogEvent.EVENT_CONNECTED)); }); stopRetriesLocked(); try { iBinder.linkToDeath(mDeathRecipient, 0); mBoundInterface = null; if (mChecker != null) { mBoundInterface = mChecker.asInterface(iBinder); if (!mChecker.checkType(mBoundInterface)) { // Received an invalid binder, disconnect mContext.unbindService(this); // Received an invalid binder, disconnect. mBoundInterface = null; Slog.w(TAG, "Invalid binder from " + componentName); startRetriesLocked(); return; } iface = mBoundInterface; pendingEvent = mPendingEvent; mPendingEvent = null; } } catch (RemoteException e) { // DOA Slog.w(TAG, "Unable to bind service: " + componentName, e); mBoundInterface = null; } } if (iface != null && pendingEvent != null) { try { pendingEvent.runEvent(iface); } catch (RuntimeException | RemoteException ex) { Slog.e(TAG, "Received exception from user service: ", ex); startRetriesLocked(); } } } @Override public void onServiceDisconnected(ComponentName componentName) { final long timestamp = System.currentTimeMillis(); Slog.w(TAG, "Service disconnected: " + componentName); mConnection = null; synchronized (mLock) { if (mConnection != this) { return; } mHandler.post(() -> { mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, LogEvent.EVENT_DISCONNECTED)); }); mBoundInterface = null; startRetriesLocked(); } } }; mPendingConnection = serviceConnection; int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; if (mIsImportant) { flags |= Context.BIND_IMPORTANT; } try { if (!mContext.bindServiceAsUser(intent, serviceConnection, flags, if (!mContext.bindServiceAsUser(intent, mConnection, flags, new UserHandle(mUserId))) { Slog.w(TAG, "Unable to bind service: " + intent); startRetriesLocked(); } } catch (SecurityException e) { Slog.w(TAG, "Unable to bind service: " + intent, e); startRetriesLocked(); } } } Loading @@ -279,4 +409,81 @@ public class ManagedApplicationService { private boolean matches(final ComponentName component, final int userId) { return Objects.equals(mComponent, component) && mUserId == userId; } private void startRetriesLocked() { if (checkAndDeliverServiceDiedCbLocked()) { // If we delivered the service callback, disconnect and stop retrying. disconnect(); return; } if (mRetrying) { // Retry already queued, don't queue a new one. return; } mRetrying = true; queueRetryLocked(); } private void stopRetriesLocked() { mRetrying = false; mHandler.removeCallbacks(mRetryRunnable); } private void queueRetryLocked() { long now = SystemClock.uptimeMillis(); if ((now - mLastRetryTimeMs) > RETRY_RESET_TIME_MS) { // It's been longer than the reset time since we last had to retry. Re-initialize. mNextRetryDurationMs = MIN_RETRY_DURATION_MS; mRetryCount = 0; } mLastRetryTimeMs = now; mHandler.postDelayed(mRetryRunnable, mNextRetryDurationMs); mNextRetryDurationMs = Math.min(2 * mNextRetryDurationMs, MAX_RETRY_DURATION_MS); mRetryCount++; } private boolean checkAndDeliverServiceDiedCbLocked() { if (mRetryType == RETRY_NEVER || (mRetryType == RETRY_BEST_EFFORT && mRetryCount >= MAX_RETRY_COUNT)) { // If we never retry, or we've exhausted our retries, post the onServiceDied callback. Slog.e(TAG, "Service " + mComponent + " has died too much, not retrying."); if (mEventCb != null) { final long timestamp = System.currentTimeMillis(); mHandler.post(() -> { mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, LogEvent.EVENT_STOPPED_PERMANENTLY)); }); } return true; } return false; } private void doRetry() { synchronized (mLock) { if (mConnection == null) { // We disconnected for good. Don't attempt to retry. return; } if (!mRetrying) { // We successfully connected. Don't attempt to retry. return; } Slog.i(TAG, "Attempting to reconnect " + mComponent + "..."); // While frameworks may restart the remote Service if we stay bound, we have little // control of the backoff timing for reconnecting the service. In the event of a // process crash, the backoff time can be very large (1-30 min), which is not // acceptable for the types of services this is used for. Instead force an unbind/bind // sequence to cause a more immediate retry. disconnect(); if (checkAndDeliverServiceDiedCbLocked()) { // No more retries. return; } queueRetryLocked(); connect(); } } }
services/core/java/com/android/server/vr/VrManagerService.java +135 −47 File changed.Preview size limit exceeded, changes collapsed. Show changes