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

Commit 3dc45fbc authored by Hui Yu's avatar Hui Yu
Browse files

Update AppOps & UsageStats when app widget tapped.

When the app widget on the launcher is tapped on:
1. Update AppOps. AppOps treats the underlying app as foreground so the app can get while-in-use
permission.
2. Report a USER_INTERACTION event to UsageStats so UsageStats can
update mLastTimeUsed and mLastTimeVisible of this package.

Bug: 149043079
Test: manual test, tapped on a widget.
Change-Id: Ic8c91190881cf5dcf89f0f72cfd410b0c2e86bf6
parent 4846c5e3
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;

import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -580,6 +581,18 @@ public final class UsageStats implements Parcelable {
                    incrementServiceTimeUsed(timeStamp);
                }
                break;
            case USER_INTERACTION:
                if (hasForegroundActivity()) {
                    incrementTimeUsed(timeStamp);
                } else {
                    mLastTimeUsed = timeStamp;
                }
                if (hasVisibleActivity()) {
                    incrementTimeVisible(timeStamp);
                } else {
                    mLastTimeVisible = timeStamp;
                }
                break;
            default:
                break;
        }
+14 −0
Original line number Diff line number Diff line
@@ -1240,4 +1240,18 @@ public class AppWidgetManager {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Note an app widget is tapped on.
     * @param uid App UID.
     * @param packageName App package name.
     * @hide
     */
    public void noteAppWidgetTapped(int uid, @NonNull String packageName) {
        try {
            mService.noteAppWidgetTapped(uid, packageName);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
}
+12 −1
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.app.Application;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.ContextWrapper;
@@ -4130,8 +4131,18 @@ public class RemoteViews implements Parcelable, Filter {
            // The NEW_TASK flags are applied through the activity options and not as a part of
            // the call to startIntentSender() to ensure that they are consistently applied to
            // both mutable and immutable PendingIntents.
            final IntentSender intentSender = pendingIntent.getIntentSender();
            final int uid = intentSender.getCreatorUid();
            final String packageName = intentSender.getCreatorPackage();
            if (uid != -1 && packageName != null) {
                final AppWidgetManager appWidgetManager =
                        context.getSystemService(AppWidgetManager.class);
                if (appWidgetManager != null) {
                    appWidgetManager.noteAppWidgetTapped(uid, packageName);
                }
            }
            context.startIntentSender(
                    pendingIntent.getIntentSender(), options.first,
                    intentSender, options.first,
                    0, 0, 0, options.second.toBundle());
        } catch (IntentSender.SendIntentException e) {
            Log.e(LOG_TAG, "Cannot send pending intent: ", e);
+1 −0
Original line number Diff line number Diff line
@@ -77,5 +77,6 @@ interface IAppWidgetService {
    boolean requestPinAppWidget(String packageName, in ComponentName providerComponent,
            in Bundle extras, in IntentSender resultIntent);
    boolean isRequestPinAppWidgetSupported();
    void noteAppWidgetTapped(int uid, String packageName);
}
+43 −18
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.DevicePolicyManagerInternal.OnCrossProfileWidgetProvidersChangeListener;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetManagerInternal;
import android.appwidget.AppWidgetProviderInfo;
@@ -235,6 +237,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    private PackageManagerInternal mPackageManagerInternal;
    private ActivityManagerInternal mActivityManagerInternal;
    private AppOpsManagerInternal mAppOpsManagerInternal;
    private UsageStatsManagerInternal mUsageStatsManagerInternal;

    private SecurityPolicy mSecurityPolicy;

@@ -278,6 +281,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
    void systemServicesReady() {
        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
        mAppOpsManagerInternal = LocalServices.getService(AppOpsManagerInternal.class);
        mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
    }

    private void computeMaximumWidgetBitmapMemory() {
@@ -879,8 +883,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
                    outUpdates.add(updatesMap.valueAt(j));
                }
            }
            updateAppOpsLocked(host, true);

            // Reset the update counter once all the updates have been calculated
            host.lastWidgetUpdateSequenceNo = updateSequenceNo;
            return new ParceledListSlice<>(outUpdates);
@@ -909,7 +911,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
            if (host != null) {
                host.callbacks = null;
                pruneHostLocked(host);
                updateAppOpsLocked(host, false);
                mAppOpsManagerInternal.updateAppWidgetVisibility(host.getWidgetUids(), false);
            }
        }
    }
@@ -3646,26 +3648,49 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
        return false;
    }

    private void updateAppOpsLocked(Host host, boolean visible) {
        if (visible) {
            final int procState = mActivityManagerInternal.getUidProcessState(host.id.uid);
            if (procState > ActivityManager.PROCESS_STATE_TOP) {
    /**
     * Note an app widget is tapped on. If a app widget is tapped, the underlying app is treated as
     * foreground so the app can get while-in-use permission.
     *
     * @param uid UID of the underlying app.
     * @param packageName Package name of the app.
     */
    @Override
    public void noteAppWidgetTapped(int uid, String packageName) {
        final int callingUid = Binder.getCallingUid();
        final long ident = Binder.clearCallingIdentity();
        try {
            // The launcher must be at TOP.
            final int procState = mActivityManagerInternal.getUidProcessState(callingUid);
            if (procState > ActivityManager.PROCESS_STATE_TOP) {
                return;
            }
        }

        final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
            // Default launcher from package manager.
            final ComponentName defaultLauncher = mPackageManagerInternal
                .getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getUserId(host.id.uid));
        // The launcher must be default launcher.
        if (defaultLauncher == null
                || !defaultLauncher.getPackageName().equals(host.id.packageName)) {
                    .getDefaultHomeActivity(UserHandle.getUserId(callingUid));
            int defaultLauncherUid  = 0;
            try {
                defaultLauncherUid = mPackageManager.getApplicationInfo(
                        defaultLauncher.getPackageName(), 0 ,
                        UserHandle.getUserId(callingUid)).uid;
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to getApplicationInfo for package:"
                        + defaultLauncher.getPackageName(), e);
                return;
            }

        mAppOpsManagerInternal.updateAppWidgetVisibility(host.getWidgetUids(), visible);
            // The callingUid must be default launcher uid.
            if (defaultLauncherUid != callingUid) {
                return;
            }
            final SparseArray<String> uid2PackageName = new SparseArray<String>();
            uid2PackageName.put(uid, packageName);
            mAppOpsManagerInternal.updateAppWidgetVisibility(uid2PackageName, true);
            mUsageStatsManagerInternal.reportEvent(packageName, UserHandle.getUserId(uid),
                    UsageEvents.Event.USER_INTERACTION);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private final class CallbackHandler extends Handler {