Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2ec961da authored by Christopher Tate's avatar Christopher Tate
Browse files

Send widget registration broadcasts before BOOT_COMPLETE

Widget presence in the home app is important for usability, but it
depends on a broadcast-based handshake at boot time.  This handshake
occurring after the BOOT_COMPLETED broadcast was initiated means
that in practice widgets may not become available for literal
minutes following unlock, as it can take this long for the
boot-complete broadcast to clear and let the systen proceed with
dispatch of the widget handshakes.

We address this by hoisting the widget setup broadcast to occur
just *before* the boot-completed broadcast, rather than as part
of general listener reaction to the global "this user has been
unlocked" notification.

Bug: 76154638
Test: manual (note broadcast ordering following boot)
Change-Id: I7c1a9f7a84fee71f71d2dcd52362a29c2436b01d
Merged-In: Ibd97268f995ec673f21d8f5df257041cf61a058d
parent 4deb5938
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -16,12 +16,9 @@

package android.appwidget;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArraySet;

import java.util.Set;

/**
 * App widget manager local system service interface.
 *
@@ -36,4 +33,13 @@ public abstract class AppWidgetManagerInternal {
     * @return Whether the UID hosts widgets from the package.
     */
    public abstract @Nullable ArraySet<String> getHostedWidgetPackages(int uid);

    /**
     * Execute the widget-related work of unlocking a user.  This is intentionally
     * invoked just <em>before</em> the boot-completed broadcast is issued, after
     * the data-related work of unlock has completed.
     *
     * @param userId The user that is being unlocked.
     */
    public abstract void unlockUser(int userId);
}
+0 −5
Original line number Diff line number Diff line
@@ -47,11 +47,6 @@ public class AppWidgetService extends SystemService {
        }
    }

    @Override
    public void onUnlockUser(int userHandle) {
        FgThread.getHandler().post(() -> mImpl.onUserUnlocked(userHandle));
    }

    @Override
    public void onStopUser(int userHandle) {
        mImpl.onUserStopped(userHandle);
+13 −3
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.server.appwidget;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;

import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;

import android.annotation.UserIdInt;
@@ -2697,7 +2696,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        }
    }

    void onUserUnlocked(int userId) {
    /**
     * This does not use the usual onUserUnlocked() listener mechanism because it is
     * invoked at a choreographed point in the middle of the user unlock sequence,
     * before the boot-completed broadcast is issued and the listeners notified.
     */
    void handleUserUnlocked(int userId) {
        if (isProfileWithLockedParent(userId)) {
            return;
        }
@@ -2734,7 +2738,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                }
            }
        }
        Slog.i(TAG, "Async processing of onUserUnlocked u" + userId + " took "
        Slog.i(TAG, "Processing of handleUserUnlocked u" + userId + " took "
                + (SystemClock.elapsedRealtime() - time) + " ms");
    }

@@ -4801,5 +4805,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                return widgetPackages;
            }
        }

        @Override
        public void unlockUser(int userId) {
            handleUserUnlocked(userId);
        }

    }
}
+12 −2
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;

import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -48,6 +47,7 @@ import android.app.IStopUserCallback;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
import android.app.usage.UsageEvents;
import android.appwidget.AppWidgetManagerInternal;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -87,8 +87,8 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimingsTraceLog;
import android.util.proto.ProtoOutputStream;

import android.view.Window;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -533,6 +533,9 @@ class UserController implements Handler.Callback {
            }
        }

        // Spin up app widgets prior to boot-complete, so they can be ready promptly
        mInjector.startUserWidgets(userId);

        Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId);
        // Do not report secondary users, runtime restarts or first boot/upgrade
        if (userId == UserHandle.USER_SYSTEM
@@ -2173,6 +2176,13 @@ class UserController implements Handler.Callback {
            }
        }

        void startUserWidgets(int userId) {
            AppWidgetManagerInternal awm = LocalServices.getService(AppWidgetManagerInternal.class);
            if (awm != null) {
                awm.unlockUser(userId);
            }
        }

        void updateUserConfiguration() {
            synchronized (mService) {
                mService.updateUserConfigurationLocked();