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

Commit 28b7793e authored by Chris Tate's avatar Chris Tate Committed by Android (Google) Code Review
Browse files

Merge "Treat widget lifecycle broadcasts as interactive"

parents dc441fd5 5cc867ee
Loading
Loading
Loading
Loading
+37 −10
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.KeyguardManager;
@@ -257,6 +258,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    private boolean mIsProviderInfoPersisted;
    private boolean mIsCombinedBroadcastEnabled;

    // Mark widget lifecycle broadcasts as 'interactive'
    private Bundle mInteractiveBroadcast;

    AppWidgetServiceImpl(Context context) {
        mContext = context;
    }
@@ -286,6 +290,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            Slog.d(TAG, "App widget provider info will not be persisted on this device");
        }

        BroadcastOptions opts = BroadcastOptions.makeBasic();
        opts.setBackgroundActivityStartsAllowed(false);
        opts.setInteractive(true);
        mInteractiveBroadcast = opts.toBundle();

        computeMaximumWidgetBitmapMemory();
        registerBroadcastReceiver();
        registerOnCrossProfileProvidersChangedListener();
@@ -2379,33 +2388,40 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLE_AND_UPDATE);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
        intent.setComponent(p.id.componentName);
        sendBroadcastAsUser(intent, p.id.getProfile());
        // Placing a widget is something users expect to be UX-responsive, so mark this
        // broadcast as interactive
        sendBroadcastAsUser(intent, p.id.getProfile(), true);
    }

    private void sendEnableIntentLocked(Provider p) {
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
        intent.setComponent(p.id.componentName);
        sendBroadcastAsUser(intent, p.id.getProfile());
        // Enabling the widget is something users expect to be UX-responsive, so mark this
        // broadcast as interactive
        sendBroadcastAsUser(intent, p.id.getProfile(), true);
    }

    private void sendUpdateIntentLocked(Provider provider, int[] appWidgetIds) {
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
        intent.setComponent(provider.id.componentName);
        sendBroadcastAsUser(intent, provider.id.getProfile());
        // Periodic background widget update heartbeats are not an interactive use case
        sendBroadcastAsUser(intent, provider.id.getProfile(), false);
    }

    private void sendDeletedIntentLocked(Widget widget) {
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
        intent.setComponent(widget.provider.id.componentName);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId);
        sendBroadcastAsUser(intent, widget.provider.id.getProfile());
        // Cleanup after deletion isn't an interactive UX case
        sendBroadcastAsUser(intent, widget.provider.id.getProfile(), false);
    }

    private void sendDisabledIntentLocked(Provider provider) {
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
        intent.setComponent(provider.id.componentName);
        sendBroadcastAsUser(intent, provider.id.getProfile());
        // Cleanup after disable isn't an interactive UX case
        sendBroadcastAsUser(intent, provider.id.getProfile(), false);
    }

    public void sendOptionsChangedIntentLocked(Widget widget) {
@@ -2413,7 +2429,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        intent.setComponent(widget.provider.id.componentName);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widget.appWidgetId);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, widget.options);
        sendBroadcastAsUser(intent, widget.provider.id.getProfile());
        // The user's changed the options, so seeing them take effect promptly is
        // an interactive UX expectation
        sendBroadcastAsUser(intent, widget.provider.id.getProfile(), true);
    }

    @GuardedBy("mLock")
@@ -3666,10 +3684,17 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        return null;
    }

    private void sendBroadcastAsUser(Intent intent, UserHandle userHandle) {
    /**
     * Sends a widget lifecycle broadcast within the specified user.  If {@code isInteractive}
     * is specified as {@code true}, the broadcast dispatch mechanism will be told that it
     * is related to a UX flow with user-visible expectations about timely dispatch.  This
     * should only be used for broadcast flows that do have such expectations.
     */
    private void sendBroadcastAsUser(Intent intent, UserHandle userHandle, boolean isInteractive) {
        final long identity = Binder.clearCallingIdentity();
        try {
            mContext.sendBroadcastAsUser(intent, userHandle);
            mContext.sendBroadcastAsUser(intent, userHandle, null,
                    isInteractive ? mInteractiveBroadcast : null);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
@@ -5008,18 +5033,20 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku

        private void sendWidgetRestoreBroadcastLocked(String action, Provider provider,
                Host host, int[] oldIds, int[] newIds, UserHandle userHandle) {
            // Users expect restore to emplace widgets properly ASAP, so flag these as
            // being interactive broadcast dispatches
            Intent intent = new Intent(action);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds);
            if (provider != null) {
                intent.setComponent(provider.id.componentName);
                sendBroadcastAsUser(intent, userHandle);
                sendBroadcastAsUser(intent, userHandle, true);
            }
            if (host != null) {
                intent.setComponent(null);
                intent.setPackage(host.id.packageName);
                intent.putExtra(AppWidgetManager.EXTRA_HOST_ID, host.id.hostId);
                sendBroadcastAsUser(intent, userHandle);
                sendBroadcastAsUser(intent, userHandle, true);
            }
        }