diff options
| -rw-r--r-- | services/core/java/com/android/server/utils/ManagedApplicationService.java | 323 | ||||
| -rw-r--r-- | services/core/java/com/android/server/vr/VrManagerService.java | 182 |
2 files changed, 400 insertions, 105 deletions
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java index d965122ee3f3..c5553881cf6c 100644 --- a/services/core/java/com/android/server/utils/ManagedApplicationService.java +++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java @@ -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. @@ -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; @@ -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; @@ -74,6 +160,9 @@ public class ManagedApplicationService { mSettingsAction = settingsAction; mChecker = binderChecker; mIsImportant = isImportant; + mRetryType = retryType; + mHandler = handler; + mEventCb = eventCallback; } /** @@ -88,7 +177,17 @@ public class ManagedApplicationService { * Implement to call IInterface methods after service is connected. */ public interface PendingEvent { - void runEvent(IInterface service) throws RemoteException; + 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); } /** @@ -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); } @@ -145,13 +249,12 @@ public class ManagedApplicationService { return true; } - - /** - * Send an event to run as soon as the binder interface is available. - * - * @param event a {@link PendingEvent} to send. - */ - public void sendEvent(@NonNull PendingEvent event) { + /** + * Send an event to run as soon as the binder interface is available. + * + * @param event a {@link PendingEvent} to send. + */ + public void sendEvent(@NonNull PendingEvent event) { IInterface iface; synchronized (mLock) { iface = mBoundInterface; @@ -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; } } @@ -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; } @@ -206,40 +307,56 @@ 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)); + }); - 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); - mBoundInterface = null; - } - iface = mBoundInterface; - pendingEvent = mPendingEvent; - mPendingEvent = null; + stopRetriesLocked(); + + mBoundInterface = null; + if (mChecker != null) { + mBoundInterface = mChecker.asInterface(iBinder); + if (!mChecker.checkType(mBoundInterface)) { + // Received an invalid binder, disconnect. + mBoundInterface = null; + Slog.w(TAG, "Invalid binder from " + componentName); + startRetriesLocked(); + return; } - } catch (RemoteException e) { - // DOA - Slog.w(TAG, "Unable to bind service: " + componentName, e); - mBoundInterface = null; + iface = mBoundInterface; + pendingEvent = mPendingEvent; + mPendingEvent = null; } } if (iface != null && pendingEvent != null) { @@ -247,31 +364,44 @@ public class ManagedApplicationService { 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; - mBoundInterface = 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(); } } } @@ -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(); + } + } } diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 5a8de82866f2..f06f76dc9edb 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -66,6 +66,8 @@ import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.utils.ManagedApplicationService.PendingEvent; +import com.android.server.utils.ManagedApplicationService.LogEvent; +import com.android.server.utils.ManagedApplicationService.LogFormattable; import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener; import com.android.server.utils.ManagedApplicationService; import com.android.server.utils.ManagedApplicationService.BinderChecker; @@ -108,7 +110,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC static final boolean DBG = false; private static final int PENDING_STATE_DELAY_MS = 300; - private static final int EVENT_LOG_SIZE = 32; + private static final int EVENT_LOG_SIZE = 64; private static final int INVALID_APPOPS_MODE = -1; /** Null set of sleep sleep flags. */ private static final int FLAG_NONE = 0; @@ -148,7 +150,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC private int mPreviousCoarseLocationMode = INVALID_APPOPS_MODE; private int mPreviousManageOverlayMode = INVALID_APPOPS_MODE; private VrState mPendingState; - private final ArrayDeque<VrState> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE); + private boolean mLogLimitHit; + private final ArrayDeque<LogFormattable> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE); private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager(); private INotificationManager mNotificationManager; /** Tracks the state of the screen and keyguard UI.*/ @@ -161,6 +164,30 @@ public class VrManagerService extends SystemService implements EnabledComponentC private Vr2dDisplay mVr2dDisplay; private boolean mBootsToVr; + // Handles events from the managed services (e.g. VrListenerService and any bound VR compositor + // service). + private final ManagedApplicationService.EventCallback mEventCallback + = new ManagedApplicationService.EventCallback() { + @Override + public void onServiceEvent(LogEvent event) { + logEvent(event); + + ComponentName component = null; + synchronized (mLock) { + component = ((mCurrentVrService == null) ? null : mCurrentVrService.getComponent()); + } + + // If not on an AIO device and we permanently stopped trying to connect to the + // VrListenerService (or don't have one bound), leave persistent VR mode and VR mode. + if (!mBootsToVr && event.event == LogEvent.EVENT_STOPPED_PERMANENTLY && + (component == null || component.equals(event.component))) { + Slog.e(TAG, "VrListenerSevice has died permanently, leaving system VR mode."); + // We're not a native VR device. Leave VR + persistent mode. + setPersistentVrModeEnabled(false); + } + } + }; + private static final int MSG_VR_STATE_CHANGE = 0; private static final int MSG_PENDING_VR_STATE_CHANGE = 1; private static final int MSG_PERSISTENT_VR_MODE_STATE_CHANGE = 2; @@ -277,7 +304,24 @@ public class VrManagerService extends SystemService implements EnabledComponentC } }; - private static class VrState { + // Event used to log when settings are changed for dumpsys logs. + private static class SettingEvent implements LogFormattable { + public final long timestamp; + public final String what; + + SettingEvent(String what) { + this.timestamp = System.currentTimeMillis(); + this.what = what; + } + + @Override + public String toLogString(SimpleDateFormat dateFormat) { + return dateFormat.format(new Date(timestamp)) + " " + what; + } + } + + // Event used to track changes of the primary on-screen VR activity. + private static class VrState implements LogFormattable { final boolean enabled; final boolean running2dInVr; final int userId; @@ -287,7 +331,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC final long timestamp; final boolean defaultPermissionsGranted; - VrState(boolean enabled, boolean running2dInVr, ComponentName targetPackageName, int userId, int processId, ComponentName callingPackage) { this.enabled = enabled; @@ -311,6 +354,39 @@ public class VrManagerService extends SystemService implements EnabledComponentC this.defaultPermissionsGranted = defaultPermissionsGranted; this.timestamp = System.currentTimeMillis(); } + + @Override + public String toLogString(SimpleDateFormat dateFormat) { + String tab = " "; + String newLine = "\n"; + StringBuilder sb = new StringBuilder(dateFormat.format(new Date(timestamp))); + sb.append(tab); + sb.append("State changed to:"); + sb.append(tab); + sb.append((enabled) ? "ENABLED" : "DISABLED"); + sb.append(newLine); + if (enabled) { + sb.append(tab); + sb.append("User="); + sb.append(userId); + sb.append(newLine); + sb.append(tab); + sb.append("Current VR Activity="); + sb.append((callingPackage == null) ? "None" : callingPackage.flattenToString()); + sb.append(newLine); + sb.append(tab); + sb.append("Bound VrListenerService="); + sb.append((targetPackageName == null) ? "None" + : targetPackageName.flattenToString()); + sb.append(newLine); + if (defaultPermissionsGranted) { + sb.append(tab); + sb.append("Default permissions granted to the bound VrListenerService."); + sb.append(newLine); + } + } + return sb.toString(); + } } private static final BinderChecker sBinderChecker = new BinderChecker() { @@ -505,9 +581,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed")); pw.println("Persistent VR mode is currently: " + ((mPersistentVrModeEnabled) ? "enabled" : "disabled")); + pw.println("Currently bound VR listener service: " + + ((mCurrentVrService == null) + ? "None" : mCurrentVrCompositorService.getComponent().flattenToString())); pw.println("Currently bound VR compositor service: " + ((mCurrentVrCompositorService == null) - ? "None" : mCurrentVrCompositorService.getComponent())); + ? "None" : mCurrentVrCompositorService.getComponent().flattenToString())); pw.println("Previous state transitions:\n"); String tab = " "; dumpStateTransitions(pw); @@ -998,7 +1077,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC private void createAndConnectService(@NonNull ComponentName component, int userId) { - mCurrentVrService = VrManagerService.create(mContext, component, userId); + mCurrentVrService = createVrListenerService(component, userId); mCurrentVrService.connect(); Slog.i(TAG, "Connecting " + component + " for user " + userId); } @@ -1033,13 +1112,27 @@ public class VrManagerService extends SystemService implements EnabledComponentC } /** - * Helper function for making ManagedApplicationService instances. + * Helper function for making ManagedApplicationService for VrListenerService instances. */ - private static ManagedApplicationService create(@NonNull Context context, - @NonNull ComponentName component, int userId) { - return ManagedApplicationService.build(context, component, userId, + private ManagedApplicationService createVrListenerService(@NonNull ComponentName component, + int userId) { + int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER + : ManagedApplicationService.RETRY_NEVER; + return ManagedApplicationService.build(mContext, component, userId, R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS, - sBinderChecker, /*isImportant*/true); + sBinderChecker, /*isImportant*/true, retryType, mHandler, mEventCallback); + } + + /** + * Helper function for making ManagedApplicationService for VR Compositor instances. + */ + private ManagedApplicationService createVrCompositorService(@NonNull ComponentName component, + int userId) { + int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER + : ManagedApplicationService.RETRY_BEST_EFFORT; + return ManagedApplicationService.build(mContext, component, userId, /*clientLabel*/0, + /*settingsAction*/null, /*binderChecker*/null, /*isImportant*/true, retryType, + mHandler, /*disconnectCallback*/mEventCallback); } /** @@ -1070,44 +1163,35 @@ public class VrManagerService extends SystemService implements EnabledComponentC private void logStateLocked() { ComponentName currentBoundService = (mCurrentVrService == null) ? null : - mCurrentVrService.getComponent(); - VrState current = new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService, - mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted); - if (mLoggingDeque.size() == EVENT_LOG_SIZE) { - mLoggingDeque.removeFirst(); + mCurrentVrService.getComponent(); + logEvent(new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService, + mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted)); + } + + private void logEvent(LogFormattable event) { + synchronized (mLoggingDeque) { + if (mLoggingDeque.size() == EVENT_LOG_SIZE) { + mLoggingDeque.removeFirst(); + mLogLimitHit = true; + } + mLoggingDeque.add(event); } - mLoggingDeque.add(current); } private void dumpStateTransitions(PrintWriter pw) { SimpleDateFormat d = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); - String tab = " "; - if (mLoggingDeque.size() == 0) { - pw.print(tab); - pw.println("None"); - } - for (VrState state : mLoggingDeque) { - pw.print(d.format(new Date(state.timestamp))); - pw.print(tab); - pw.print("State changed to:"); - pw.print(tab); - pw.println((state.enabled) ? "ENABLED" : "DISABLED"); - if (state.enabled) { - pw.print(tab); - pw.print("User="); - pw.println(state.userId); - pw.print(tab); - pw.print("Current VR Activity="); - pw.println((state.callingPackage == null) ? - "None" : state.callingPackage.flattenToString()); - pw.print(tab); - pw.print("Bound VrListenerService="); - pw.println((state.targetPackageName == null) ? - "None" : state.targetPackageName.flattenToString()); - if (state.defaultPermissionsGranted) { - pw.print(tab); - pw.println("Default permissions granted to the bound VrListenerService."); - } + synchronized (mLoggingDeque) { + if (mLoggingDeque.size() == 0) { + pw.print(" "); + pw.println("None"); + } + + if (mLogLimitHit) { + pw.println("..."); // Indicates log overflow + } + + for (LogFormattable event : mLoggingDeque) { + pw.println(event.toLogString(d)); } } } @@ -1202,6 +1286,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC private void updateCompositorServiceLocked(int userId, ComponentName componentName) { if (mCurrentVrCompositorService != null && mCurrentVrCompositorService.disconnectIfNotMatching(componentName, userId)) { + Slog.i(TAG, "Disconnecting compositor service: " + + mCurrentVrCompositorService.getComponent()); // Check if existing service matches the requested one, if not (or if the requested // component is null) disconnect it. mCurrentVrCompositorService = null; @@ -1210,9 +1296,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC if (componentName != null && mCurrentVrCompositorService == null) { // We don't have an existing service matching the requested component, so attempt to // connect one. - mCurrentVrCompositorService = ManagedApplicationService.build(mContext, - componentName, userId, /*clientLabel*/0, /*settingsAction*/null, - /*binderChecker*/null, /*isImportant*/true); + Slog.i(TAG, "Connecting compositor service: " + componentName); + mCurrentVrCompositorService = createVrCompositorService(componentName, userId); mCurrentVrCompositorService.connect(); } } @@ -1221,6 +1306,9 @@ public class VrManagerService extends SystemService implements EnabledComponentC if (mPersistentVrModeEnabled == enabled) { return; } + String eventName = "Persistent VR mode " + ((enabled) ? "enabled" : "disabled"); + Slog.i(TAG, eventName); + logEvent(new SettingEvent(eventName)); mPersistentVrModeEnabled = enabled; mHandler.sendMessage(mHandler.obtainMessage(MSG_PERSISTENT_VR_MODE_STATE_CHANGE, |