diff options
5 files changed, 167 insertions, 38 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 099d6b645a6d..29b578ae6e48 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -984,6 +984,16 @@ flag {  }  flag { +    name: "use_notif_inflation_thread_for_row" +    namespace: "systemui" +    description: "use the @NotifInflation thread for ExpandableNotificationRow inflation" +    bug: "375320642" +    metadata { +        purpose: PURPOSE_BUGFIX +    } +} + +flag {      name: "notify_power_manager_user_activity_background"      namespace: "systemui"      description: "Decide whether to notify the user activity to power manager in the background thread." diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AsyncRowInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AsyncRowInflater.kt new file mode 100644 index 000000000000..c3b241154e0e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/AsyncRowInflater.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row + +import android.content.Context +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.annotation.UiThread +import com.android.app.tracing.coroutines.launchTraced +import com.android.app.tracing.coroutines.withContextTraced +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dagger.qualifiers.NotifInflation +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job + +@SysUISingleton +class AsyncRowInflater +@Inject +constructor( +    @Application private val applicationScope: CoroutineScope, +    @Main private val mainCoroutineDispatcher: CoroutineDispatcher, +    @NotifInflation private val inflationCoroutineDispatcher: CoroutineDispatcher, +) { +    /** +     * Inflate the layout on the background thread, and invoke the listener on the main thread when +     * finished. +     * +     * If the inflation fails on the background, it will be retried once on the main thread. +     */ +    @UiThread +    fun inflate( +        context: Context, +        layoutFactory: LayoutInflater.Factory2, +        @LayoutRes resId: Int, +        parent: ViewGroup, +        listener: OnInflateFinishedListener, +    ): Job { +        val inflater = BasicRowInflater(context).apply { factory2 = layoutFactory } +        return applicationScope.launchTraced("AsyncRowInflater-bg", inflationCoroutineDispatcher) { +            val view = +                try { +                    inflater.inflate(resId, parent, false) +                } catch (ex: RuntimeException) { +                    // Probably a Looper failure, retry on the UI thread +                    Log.w( +                        "AsyncRowInflater", +                        "Failed to inflate resource in the background!" + +                            " Retrying on the UI thread", +                        ex, +                    ) +                    null +                } +            withContextTraced("AsyncRowInflater-ui", mainCoroutineDispatcher) { +                // If the inflate failed on the inflation thread, try again on the main thread +                val finalView = view ?: inflater.inflate(resId, parent, false) +                // Inform the listener of the completion +                listener.onInflateFinished(finalView, resId, parent) +            } +        } +    } + +    /** +     * Callback interface (identical to the one from AsyncLayoutInflater) for receiving the inflated +     * view +     */ +    interface OnInflateFinishedListener { +        @UiThread fun onInflateFinished(view: View, @LayoutRes resId: Int, parent: ViewGroup?) +    } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BasicRowInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BasicRowInflater.kt new file mode 100644 index 000000000000..79d50b8398bc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BasicRowInflater.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.row + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View + +/** + * A [LayoutInflater] that is copy of + * [androidx.asynclayoutinflater.view.AsyncLayoutInflater.BasicInflater] + */ +internal class BasicRowInflater(context: Context) : LayoutInflater(context) { +    override fun cloneInContext(newContext: Context): LayoutInflater { +        return BasicRowInflater(newContext) +    } + +    @Throws(ClassNotFoundException::class) +    override fun onCreateView(name: String, attrs: AttributeSet): View { +        for (prefix in sClassPrefixList) { +            try { +                val view = createView(name, prefix, attrs) +                if (view != null) { +                    return view +                } +            } catch (e: ClassNotFoundException) { +                // In this case we want to let the base class take a crack at it. +            } +        } + +        return super.onCreateView(name, attrs) +    } + +    companion object { +        private val sClassPrefixList = arrayOf("android.widget.", "android.webkit.", "android.app.") +    } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java index d60e37423e5c..3971661fa787 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java @@ -30,6 +30,7 @@ import androidx.annotation.VisibleForTesting;  import androidx.asynclayoutinflater.view.AsyncLayoutFactory;  import androidx.asynclayoutinflater.view.AsyncLayoutInflater; +import com.android.systemui.Flags;  import com.android.systemui.res.R;  import com.android.systemui.settings.UserTracker;  import com.android.systemui.statusbar.InflationTask; @@ -44,7 +45,8 @@ import javax.inject.Inject;  /**   * An inflater task that asynchronously inflates a ExpandableNotificationRow   */ -public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInflateFinishedListener { +public class RowInflaterTask implements InflationTask, +        AsyncLayoutInflater.OnInflateFinishedListener, AsyncRowInflater.OnInflateFinishedListener {      private static final String TAG = "RowInflaterTask";      private static final boolean TRACE_ORIGIN = true; @@ -55,15 +57,17 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf      private Throwable mInflateOrigin;      private final SystemClock mSystemClock;      private final RowInflaterTaskLogger mLogger; +    private final AsyncRowInflater mAsyncRowInflater;      private long mInflateStartTimeMs;      private UserTracker mUserTracker;      @Inject      public RowInflaterTask(SystemClock systemClock, RowInflaterTaskLogger logger, -            UserTracker userTracker) { +            UserTracker userTracker, AsyncRowInflater asyncRowInflater) {          mSystemClock = systemClock;          mLogger = logger;          mUserTracker = userTracker; +        mAsyncRowInflater = asyncRowInflater;      }      /** @@ -87,13 +91,19 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf              mInflateOrigin = new Throwable("inflate requested here");          }          mListener = listener; -        AsyncLayoutInflater inflater = new AsyncLayoutInflater(context, makeRowInflater(entry)); +        RowAsyncLayoutInflater asyncLayoutFactory = makeRowInflater(entry);          mEntry = entry;          entry.setInflationTask(this);          mLogger.logInflateStart(entry);          mInflateStartTimeMs = mSystemClock.elapsedRealtime(); -        inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this); +        if (Flags.useNotifInflationThreadForRow()) { +            mAsyncRowInflater.inflate(context, asyncLayoutFactory, +                    R.layout.status_bar_notification_row, parent, this); +        } else { +            AsyncLayoutInflater inflater = new AsyncLayoutInflater(context, asyncLayoutFactory); +            inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this); +        }      }      /** @@ -117,39 +127,6 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf                  entry, mSystemClock, mLogger, mUserTracker.getUserHandle());      } -    /** -     * A {@link LayoutInflater} that is copy of BasicLayoutInflater. -     */ -    private static class BasicRowInflater extends LayoutInflater { -        private static final String[] sClassPrefixList = -                {"android.widget.", "android.webkit.", "android.app."}; -        BasicRowInflater(Context context) { -            super(context); -        } - -        @Override -        public LayoutInflater cloneInContext(Context newContext) { -            return new BasicRowInflater(newContext); -        } - -        @Override -        protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { -            for (String prefix : sClassPrefixList) { -                try { -                    View view = createView(name, prefix, attrs); -                    if (view != null) { -                        return view; -                    } -                } catch (ClassNotFoundException e) { -                    // In this case we want to let the base class take a crack -                    // at it. -                } -            } - -            return super.onCreateView(name, attrs); -        } -    } -      @VisibleForTesting      public static class RowAsyncLayoutInflater implements AsyncLayoutFactory {          private final NotificationEntry mEntry; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index 2543ca95eb3b..8b19491bfdf8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -354,7 +354,8 @@ class ExpandableNotificationRowBuilder(              RowInflaterTask(                  mFakeSystemClock,                  Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY), -                userTracker +                userTracker, +                Mockito.mock(AsyncRowInflater::class.java, STUB_ONLY),              )          val row = rowInflaterTask.inflateSynchronously(context, null, entry)  |