Merge cherrypicks of ['googleplex-android-review.googlesource.com/31154470', 'googleplex-android-review.googlesource.com/31195717', 'googleplex-android-review.googlesource.com/31014284', 'googleplex-android-review.googlesource.com/31395613', 'googleplex-android-review.googlesource.com/31569659'] into 25Q1-release.
Change-Id: I446aae829c6c9c29b5d7808787dba8f1b68c361b
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 5eadd06..221beaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2951,9 +2951,11 @@
final int transitType = info.getType();
TransitionInfo.Change pipChange = null;
int closingSplitTaskId = -1;
- // This array tracks if we are sending stages TO_BACK in this transition.
- // TODO (b/349828130): Update for n apps
- boolean[] stagesSentToBack = new boolean[2];
+ // This array tracks where we are sending stages (TO_BACK/TO_FRONT) in this transition.
+ // TODO (b/349828130): Update for n apps (needs to handle different indices than 0/1).
+ // Also make sure having multiple changes per stage (2+ tasks in one stage) is being
+ // handled properly.
+ int[] stageChanges = new int[2];
for (int iC = 0; iC < info.getChanges().size(); ++iC) {
final TransitionInfo.Change change = info.getChanges().get(iC);
@@ -3016,18 +3018,25 @@
+ " with " + taskId + " before startAnimation().");
}
}
- if (isClosingType(change.getMode()) &&
- getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED) {
- // Record which stages are getting sent to back
- if (change.getMode() == TRANSIT_TO_BACK) {
- stagesSentToBack[getStageOfTask(taskId)] = true;
- }
-
+ final int stageOfTaskId = getStageOfTask(taskId);
+ if (stageOfTaskId == STAGE_TYPE_UNDEFINED) {
+ continue;
+ }
+ if (isClosingType(change.getMode())) {
// (For PiP transitions) If either one of the 2 stages is closing we're assuming
// we'll break split
closingSplitTaskId = taskId;
}
+ if (transitType == WindowManager.TRANSIT_WAKE) {
+ // Record which stages are receiving which changes
+ if ((change.getMode() == TRANSIT_TO_BACK
+ || change.getMode() == TRANSIT_TO_FRONT)
+ && (stageOfTaskId == STAGE_TYPE_MAIN
+ || stageOfTaskId == STAGE_TYPE_SIDE)) {
+ stageChanges[stageOfTaskId] = change.getMode();
+ }
+ }
}
if (pipChange != null) {
@@ -3052,19 +3061,11 @@
return true;
}
- // If keyguard is active, check to see if we have our TO_BACK transitions in order.
- // This array should either be all false (no split stages sent to back) or all true
- // (all stages sent to back). In any other case (which can happen with SHOW_ABOVE_LOCKED
- // apps) we should break split.
- if (mKeyguardActive) {
- boolean isFirstStageSentToBack = stagesSentToBack[0];
- for (boolean b : stagesSentToBack) {
- // Compare each boolean to the first one. If any are different, break split.
- if (b != isFirstStageSentToBack) {
- dismissSplitKeepingLastActiveStage(EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
- break;
- }
- }
+ // If keyguard is active, check to see if we have all our stages showing. If one stage
+ // was moved but not the other (which can happen with SHOW_ABOVE_LOCKED apps), we should
+ // break split.
+ if (mKeyguardActive && stageChanges[STAGE_TYPE_MAIN] != stageChanges[STAGE_TYPE_SIDE]) {
+ dismissSplitKeepingLastActiveStage(EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP);
}
final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 83ca496..2b71c87 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -19,10 +19,11 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
+import android.os.IRemoteCallback;
import android.view.MotionEvent;
import com.android.systemui.shared.recents.ISystemUiProxy;
-// Next ID: 34
+// Next ID: 36
oneway interface IOverviewProxy {
void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
@@ -137,4 +138,10 @@
* Sent when {@link TaskbarDelegate#appTransitionPending} is called.
*/
void appTransitionPending(boolean pending) = 34;
+
+ /**
+ * Sent right after OverviewProxy calls unbindService() on the TouchInteractionService.
+ * TouchInteractionService is expected to send the reply once it has finished cleaning up.
+ */
+ void onUnbind(IRemoteCallback reply) = 35;
}
diff --git a/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt b/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
index 2978595..9596a54 100644
--- a/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
@@ -20,10 +20,12 @@
import android.hardware.input.InputManager
import android.hardware.input.KeyGestureEvent
import androidx.datastore.core.DataStore
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.preferences.core.MutablePreferences
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.preferencesDataStoreFile
@@ -68,7 +70,7 @@
suspend fun updateGestureEduModel(
gestureType: GestureType,
- transform: (GestureEduModel) -> GestureEduModel
+ transform: (GestureEduModel) -> GestureEduModel,
)
suspend fun updateEduDeviceConnectionTime(
@@ -149,6 +151,8 @@
String.format(DATASTORE_DIR, userId)
)
},
+ corruptionHandler =
+ ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
scope = newDsScope,
)
dataStoreScope = newDsScope
@@ -159,7 +163,7 @@
private fun getGestureEduModel(
gestureType: GestureType,
- preferences: Preferences
+ preferences: Preferences,
): GestureEduModel {
return GestureEduModel(
signalCount = preferences[getSignalCountKey(gestureType)] ?: 0,
@@ -183,7 +187,7 @@
override suspend fun updateGestureEduModel(
gestureType: GestureType,
- transform: (GestureEduModel) -> GestureEduModel
+ transform: (GestureEduModel) -> GestureEduModel,
) {
datastore.filterNotNull().first().edit { preferences ->
val currentModel = getGestureEduModel(gestureType, preferences)
@@ -193,17 +197,17 @@
setInstant(
preferences,
updatedModel.lastShortcutTriggeredTime,
- getLastShortcutTriggeredTimeKey(gestureType)
+ getLastShortcutTriggeredTimeKey(gestureType),
)
setInstant(
preferences,
updatedModel.usageSessionStartTime,
- getUsageSessionStartTimeKey(gestureType)
+ getUsageSessionStartTimeKey(gestureType),
)
setInstant(
preferences,
updatedModel.lastEducationTime,
- getLastEducationTimeKey(gestureType)
+ getLastEducationTimeKey(gestureType),
)
}
}
@@ -220,12 +224,12 @@
setInstant(
preferences,
updatedModel.keyboardFirstConnectionTime,
- getKeyboardFirstConnectionTimeKey()
+ getKeyboardFirstConnectionTimeKey(),
)
setInstant(
preferences,
updatedModel.touchpadFirstConnectionTime,
- getTouchpadFirstConnectionTimeKey()
+ getTouchpadFirstConnectionTimeKey(),
)
}
}
@@ -235,7 +239,7 @@
keyboardFirstConnectionTime =
preferences[getKeyboardFirstConnectionTimeKey()]?.let { Instant.ofEpochSecond(it) },
touchpadFirstConnectionTime =
- preferences[getTouchpadFirstConnectionTimeKey()]?.let { Instant.ofEpochSecond(it) }
+ preferences[getTouchpadFirstConnectionTimeKey()]?.let { Instant.ofEpochSecond(it) },
)
}
@@ -263,7 +267,7 @@
private fun setInstant(
preferences: MutablePreferences,
instant: Instant?,
- key: Preferences.Key<Long>
+ key: Preferences.Key<Long>,
) {
if (instant != null) {
// Use epochSecond because an instant is defined as a signed long (64bit number) of
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
index a89ec70..315d3b1 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
@@ -18,8 +18,10 @@
import android.content.Context
import androidx.datastore.core.DataStore
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.android.systemui.dagger.SysUISingleton
@@ -45,7 +47,12 @@
) : this(applicationContext, backgroundScope, dataStoreName = DATASTORE_NAME)
private val Context.dataStore: DataStore<Preferences> by
- preferencesDataStore(name = dataStoreName, scope = backgroundScope)
+ preferencesDataStore(
+ name = dataStoreName,
+ corruptionHandler =
+ ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
+ scope = backgroundScope,
+ )
suspend fun isLaunched(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isLaunched
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index a595d81..29bda76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -86,15 +86,56 @@
transition.values[SMARTSPACE_BOUNDS] = targetSSView.getRect()
}
- open fun mutateBounds(
- view: View,
- fromIsVis: Boolean,
- toIsVis: Boolean,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?,
- ) {}
+ open fun initTargets(from: Target, to: Target) {}
+
+ open fun mutateTargets(from: Target, to: Target) {}
+
+ data class Target(
+ var view: View,
+ var visibility: Int,
+ var isVisible: Boolean,
+ var alpha: Float,
+ var bounds: Rect,
+ var ssBounds: Rect?,
+ ) {
+ companion object {
+ fun fromStart(startValues: TransitionValues): Target {
+ var fromVis = startValues.values[PROP_VISIBILITY] as Int
+ var fromIsVis = fromVis == View.VISIBLE
+ var fromAlpha = startValues.values[PROP_ALPHA] as Float
+
+ // Align starting visibility and alpha
+ if (!fromIsVis) fromAlpha = 0f
+ else if (fromAlpha <= 0f) {
+ fromIsVis = false
+ fromVis = View.INVISIBLE
+ }
+
+ return Target(
+ view = startValues.view,
+ visibility = fromVis,
+ isVisible = fromIsVis,
+ alpha = fromAlpha,
+ bounds = startValues.values[PROP_BOUNDS] as Rect,
+ ssBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?,
+ )
+ }
+
+ fun fromEnd(endValues: TransitionValues): Target {
+ val toVis = endValues.values[PROP_VISIBILITY] as Int
+ val toIsVis = toVis == View.VISIBLE
+
+ return Target(
+ view = endValues.view,
+ visibility = toVis,
+ isVisible = toIsVis,
+ alpha = if (toIsVis) 1f else 0f,
+ bounds = endValues.values[PROP_BOUNDS] as Rect,
+ ssBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?,
+ )
+ }
+ }
+ }
override fun createAnimator(
sceenRoot: ViewGroup,
@@ -109,72 +150,58 @@
return null
}
- var fromVis = startValues.values[PROP_VISIBILITY] as Int
- var fromIsVis = fromVis == View.VISIBLE
- var fromAlpha = startValues.values[PROP_ALPHA] as Float
- val fromBounds = startValues.values[PROP_BOUNDS] as Rect
- val fromSSBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?
+ val from = Target.fromStart(startValues)
+ val to = Target.fromEnd(endValues)
+ initTargets(from, to)
+ mutateTargets(from, to)
- val toView = endValues.view
- val toVis = endValues.values[PROP_VISIBILITY] as Int
- val toBounds = endValues.values[PROP_BOUNDS] as Rect
- val toSSBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?
- val toIsVis = toVis == View.VISIBLE
- val toAlpha = if (toIsVis) 1f else 0f
-
- // Align starting visibility and alpha
- if (!fromIsVis) fromAlpha = 0f
- else if (fromAlpha <= 0f) {
- fromIsVis = false
- fromVis = View.INVISIBLE
- }
-
- mutateBounds(toView, fromIsVis, toIsVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
- if (fromIsVis == toIsVis && fromBounds.equals(toBounds)) {
+ if (from.isVisible == to.isVisible && from.bounds.equals(to.bounds)) {
if (DEBUG) {
Log.w(
TAG,
- "Skipping no-op transition: $toView; " +
- "vis: $fromVis -> $toVis; " +
- "alpha: $fromAlpha -> $toAlpha; " +
- "bounds: $fromBounds -> $toBounds; ",
+ "Skipping no-op transition: ${to.view}; " +
+ "vis: ${from.visibility} -> ${to.visibility}; " +
+ "alpha: ${from.alpha} -> ${to.alpha}; " +
+ "bounds: ${from.bounds} -> ${to.bounds}; ",
)
}
return null
}
- val sendToBack = fromIsVis && !toIsVis
+ val sendToBack = from.isVisible && !to.isVisible
fun lerp(start: Int, end: Int, fract: Float): Int =
MathUtils.lerp(start.toFloat(), end.toFloat(), fract).toInt()
fun computeBounds(fract: Float): Rect =
Rect(
- lerp(fromBounds.left, toBounds.left, fract),
- lerp(fromBounds.top, toBounds.top, fract),
- lerp(fromBounds.right, toBounds.right, fract),
- lerp(fromBounds.bottom, toBounds.bottom, fract),
+ lerp(from.bounds.left, to.bounds.left, fract),
+ lerp(from.bounds.top, to.bounds.top, fract),
+ lerp(from.bounds.right, to.bounds.right, fract),
+ lerp(from.bounds.bottom, to.bounds.bottom, fract),
)
fun assignAnimValues(src: String, fract: Float, vis: Int? = null) {
+ mutateTargets(from, to)
val bounds = computeBounds(fract)
- val alpha = MathUtils.lerp(fromAlpha, toAlpha, fract)
+ val alpha = MathUtils.lerp(from.alpha, to.alpha, fract)
if (DEBUG) {
Log.i(
TAG,
- "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;",
+ "$src: ${to.view}; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;",
)
}
- toView.setVisibility(vis ?: View.VISIBLE)
- toView.setAlpha(alpha)
- toView.setRect(bounds)
+
+ to.view.setVisibility(vis ?: View.VISIBLE)
+ to.view.setAlpha(alpha)
+ to.view.setRect(bounds)
}
if (DEBUG) {
Log.i(
TAG,
- "transitioning: $toView; " +
- "vis: $fromVis -> $toVis; " +
- "alpha: $fromAlpha -> $toAlpha; " +
- "bounds: $fromBounds -> $toBounds; ",
+ "transitioning: ${to.view}; " +
+ "vis: ${from.visibility} -> ${to.visibility}; " +
+ "alpha: ${from.alpha} -> ${to.alpha}; " +
+ "bounds: ${from.bounds} -> ${to.bounds}; ",
)
}
@@ -190,11 +217,11 @@
this@VisibilityBoundsTransition.addListener(
object : TransitionListenerAdapter() {
override fun onTransitionStart(t: Transition) {
- toView.viewTreeObserver.addOnPreDrawListener(predrawCallback)
+ to.view.viewTreeObserver.addOnPreDrawListener(predrawCallback)
}
override fun onTransitionEnd(t: Transition) {
- toView.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
+ to.view.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
}
}
)
@@ -202,17 +229,17 @@
val listener =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(anim: Animator) {
- assignAnimValues("start", 0f, fromVis)
+ assignAnimValues("start", 0f, from.visibility)
}
override fun onAnimationEnd(anim: Animator) {
- assignAnimValues("end", 1f, toVis)
- if (sendToBack) toView.translationZ = 0f
+ assignAnimValues("end", 1f, to.visibility)
+ if (sendToBack) to.view.translationZ = 0f
}
}
anim.addListener(listener)
- assignAnimValues("init", 0f, fromVis)
+ assignAnimValues("init", 0f, from.visibility)
}
}
@@ -251,31 +278,23 @@
}
}
- override fun mutateBounds(
- view: View,
- fromIsVis: Boolean,
- toIsVis: Boolean,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?,
- ) {
+ override fun initTargets(from: Target, to: Target) {
// Move normally if clock is not changing visibility
- if (fromIsVis == toIsVis) return
+ if (from.isVisible == to.isVisible) return
- fromBounds.set(toBounds)
+ from.bounds.set(to.bounds)
if (isLargeClock) {
// Large clock shouldn't move; fromBounds already set
- } else if (toSSBounds != null && fromSSBounds != null) {
+ } else if (to.ssBounds != null && from.ssBounds != null) {
// Instead of moving the small clock the full distance, we compute the distance
// smartspace will move. We then scale this to match the duration of this animation
// so that the small clock moves at the same speed as smartspace.
val ssTranslation =
- abs((toSSBounds.top - fromSSBounds.top) * smallClockMoveScale).toInt()
- fromBounds.top = toBounds.top - ssTranslation
- fromBounds.bottom = toBounds.bottom - ssTranslation
+ abs((to.ssBounds!!.top - from.ssBounds!!.top) * smallClockMoveScale).toInt()
+ from.bounds.top = to.bounds.top - ssTranslation
+ from.bounds.bottom = to.bounds.bottom - ssTranslation
} else {
- Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
+ Log.e(TAG, "initTargets: smallClock received no smartspace bounds")
}
}
}
@@ -320,10 +339,9 @@
}
}
- // TODO: Might need a mechanism to update this one while in-progress
class SmartspaceMoveTransition(
val config: IntraBlueprintTransition.Config,
- viewModel: KeyguardClockViewModel,
+ val viewModel: KeyguardClockViewModel,
) : VisibilityBoundsTransition() {
private val isLargeClock = viewModel.isLargeClockVisible.value
override val captureSmartspace = false
@@ -340,23 +358,23 @@
addTarget(R.id.status_view_media_container)
}
- override fun mutateBounds(
- view: View,
- fromIsVis: Boolean,
- toIsVis: Boolean,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?,
- ) {
+ override fun initTargets(from: Target, to: Target) {
// If view is changing visibility, hold it in place
- if (fromIsVis == toIsVis) return
- if (DEBUG) Log.i(TAG, "Holding position of ${view.id}")
+ if (from.isVisible == to.isVisible) return
+ if (DEBUG) Log.i(TAG, "Holding position of ${to.view.id}")
- if (fromIsVis) {
- toBounds.set(fromBounds)
+ if (from.isVisible) {
+ to.bounds.set(from.bounds)
} else {
- fromBounds.set(toBounds)
+ from.bounds.set(to.bounds)
+ }
+ }
+
+ override fun mutateTargets(from: Target, to: Target) {
+ if (to.view.id == sharedR.id.date_smartspace_view) {
+ to.isVisible = !viewModel.hasCustomWeatherDataDisplay.value
+ to.visibility = if (to.isVisible) View.VISIBLE else View.GONE
+ to.alpha = if (to.isVisible) 1f else 0f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index e3cf411..adf9eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -58,6 +58,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.PatternMatcher;
import android.os.Process;
@@ -146,7 +147,6 @@
public static final String TAG_OPS = "OverviewProxyService";
private static final long BACKOFF_MILLIS = 1000;
private static final long DEFERRED_CALLBACK_MILLIS = 5000;
-
// Max backoff caps at 5 mins
private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
@@ -183,6 +183,10 @@
private int mConnectionBackoffAttempts;
private boolean mBound;
private boolean mIsEnabled;
+ // This is set to false when the overview service is requested to be bound until it is notified
+ // that the previous service has been cleaned up in IOverviewProxy#onUnbind(). It is also set to
+ // true after a 1000ms timeout by mDeferredBindAfterTimedOutCleanup.
+ private boolean mIsPrevServiceCleanedUp = true;
private boolean mIsSystemOrVisibleBgUser;
private int mCurrentBoundedUserId = -1;
@@ -489,6 +493,12 @@
retryConnectionWithBackoff();
};
+ private final Runnable mDeferredBindAfterTimedOutCleanup = () -> {
+ Log.w(TAG_OPS, "Timed out waiting for previous service to clean up, binding to new one");
+ mIsPrevServiceCleanedUp = true;
+ maybeBindService();
+ };
+
private final BroadcastReceiver mUserEventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -859,6 +869,7 @@
mShadeViewControllerLazy.get().cancelInputFocusTransfer();
});
}
+ mIsPrevServiceCleanedUp = true;
startConnectionToCurrentUser();
}
@@ -889,6 +900,19 @@
}
mHandler.removeCallbacks(mConnectionRunnable);
+ maybeBindService();
+ }
+
+ private void maybeBindService() {
+ if (!mIsPrevServiceCleanedUp) {
+ Log.w(TAG_OPS, "Skipping connection to TouchInteractionService until previous"
+ + " instance is cleaned up.");
+ if (!mHandler.hasCallbacks(mDeferredConnectionCallback)) {
+ mHandler.postDelayed(mDeferredBindAfterTimedOutCleanup, BACKOFF_MILLIS);
+ }
+ return;
+ }
+
// Avoid creating TouchInteractionService because the System user in HSUM mode does not
// interact with UI elements
UserHandle currentUser = UserHandle.of(mUserTracker.getUserId());
@@ -907,6 +931,7 @@
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
if (mBound) {
+ mIsPrevServiceCleanedUp = false;
// Ensure that connection has been established even if it thinks it is bound
mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
} else {
@@ -960,6 +985,24 @@
// Always unbind the service (ie. if called through onNullBinding or onBindingDied)
mContext.unbindService(mOverviewServiceConnection);
mBound = false;
+ if (mOverviewProxy != null) {
+ try {
+ mOverviewProxy.onUnbind(new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ // Received Launcher reply, try to bind anew.
+ mIsPrevServiceCleanedUp = true;
+ if (mHandler.hasCallbacks(mDeferredBindAfterTimedOutCleanup)) {
+ mHandler.removeCallbacks(mDeferredBindAfterTimedOutCleanup);
+ maybeBindService();
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.w(TAG_OPS, "disconnectFromLauncherService failed to notify Launcher");
+ mIsPrevServiceCleanedUp = true;
+ }
+ }
}
if (mOverviewProxy != null) {
@@ -1189,6 +1232,7 @@
pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis);
pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
pw.print(" mNavBarMode="); pw.println(mNavBarMode);
+ pw.print(" mIsPrevServiceCleanedUp="); pw.println(mIsPrevServiceCleanedUp);
mSysUiState.dump(pw, args);
}