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

Commit 66c56a0c authored by Xin Guan's avatar Xin Guan
Browse files

Modify usagestats event processing thread

Use a dedicated thread with default priority to handle
the usage stats event processing.

Also offload IOs to the dedicated IoThread and post processing
the launch time estimation.

Bug: 302675504
Test: atest CtsUsageStatsTestCases
Test: atest FrameworksCoreTests:android.app.usage.*
Change-Id: Ib0bb490279c1dcbd71cabfc3a1f7d97c638eb9b6
parent a1bfd75d
Loading
Loading
Loading
Loading
+80 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.server.usage;

import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.os.Trace;

import java.util.concurrent.Executor;

/**
 * Shared singleton default priority thread for usage stats message handling.
 *
 * @see com.android.internal.os.BackgroundThread
 */
public final class UsageStatsHandlerThread extends HandlerThread {
    private static final long SLOW_DISPATCH_THRESHOLD_MS = 10_000;
    private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
    private static UsageStatsHandlerThread sInstance;
    private static Handler sHandler;
    private static Executor sHandlerExecutor;

    private UsageStatsHandlerThread() {
        super("usagestats.default", Process.THREAD_PRIORITY_DEFAULT);
    }

    private static void ensureThreadLocked() {
        if (sInstance == null) {
            sInstance = new UsageStatsHandlerThread();
            sInstance.start();
            final Looper looper = sInstance.getLooper();
            looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
            looper.setSlowLogThresholdMs(
                    SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
            sHandler = new Handler(sInstance.getLooper());
            sHandlerExecutor = new HandlerExecutor(sHandler);
        }
    }

    /** Returns the UsageStatsHandlerThread singleton */
    public static UsageStatsHandlerThread get() {
        synchronized (UsageStatsHandlerThread.class) {
            ensureThreadLocked();
            return sInstance;
        }
    }

    /** Returns the singleton handler for UsageStatsHandlerThread */
    public static Handler getHandler() {
        synchronized (UsageStatsHandlerThread.class) {
            ensureThreadLocked();
            return sHandler;
        }
    }

    /** Returns the singleton handler executor for UsageStatsHandlerThread */
    public static Executor getExecutor() {
        synchronized (UsageStatsHandlerThread.class) {
            ensureThreadLocked();
            return sHandlerExecutor;
        }
    }
}
+158 −138
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
@@ -201,7 +202,8 @@ public class UsageStatsService extends SystemService implements
    static final int MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED = 9;

    private final Object mLock = new Object();
    Handler mHandler;
    private Handler mHandler;
    private Handler mIoHandler;
    AppOpsManager mAppOps;
    UserManager mUserManager;
    PackageManager mPackageManager;
@@ -233,7 +235,7 @@ public class UsageStatsService extends SystemService implements
    private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>();
    final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
    final SparseArray<ActivityData> mVisibleActivities = new SparseArray();
    @GuardedBy("mLock")
    @GuardedBy("mLaunchTimeAlarmQueues") // Don't hold the main lock
    private final SparseArray<LaunchTimeAlarmQueue> mLaunchTimeAlarmQueues = new SparseArray<>();
    @GuardedBy("mUsageEventListeners") // Don't hold the main lock when calling out
    private final ArraySet<UsageStatsManagerInternal.UsageEventListener> mUsageEventListeners =
@@ -279,6 +281,38 @@ public class UsageStatsService extends SystemService implements
        }
    }

    private final Handler.Callback mIoHandlerCallback = (msg) -> {
        switch (msg.what) {
            case MSG_UID_STATE_CHANGED: {
                final int uid = msg.arg1;
                final int procState = msg.arg2;

                final int newCounter = (procState <= ActivityManager.PROCESS_STATE_TOP) ? 0 : 1;
                synchronized (mUidToKernelCounter) {
                    final int oldCounter = mUidToKernelCounter.get(uid, 0);
                    if (newCounter != oldCounter) {
                        mUidToKernelCounter.put(uid, newCounter);
                        try {
                            FileUtils.stringToFile(KERNEL_COUNTER_FILE, uid + " " + newCounter);
                        } catch (IOException e) {
                            Slog.w(TAG, "Failed to update counter set: " + e);
                        }
                    }
                }
                return true;
            }
            case MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK: {
                final int userId = msg.arg1;
                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
                        "usageStatsHandleEstimatedLaunchTimesOnUser(" + userId + ")");
                handleEstimatedLaunchTimesOnUserUnlock(userId);
                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
                return true;
            }
        }
        return false;
    };

    private final Injector mInjector;

    public UsageStatsService(Context context) {
@@ -298,7 +332,9 @@ public class UsageStatsService extends SystemService implements
        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
        mPackageManager = getContext().getPackageManager();
        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        mHandler = new H(BackgroundThread.get().getLooper());

        mHandler = new H(UsageStatsHandlerThread.get().getLooper());
        mIoHandler = new Handler(IoThread.get().getLooper(), mIoHandlerCallback);

        mAppStandby = mInjector.getAppStandbyController(getContext());
        mResponseStatsTracker = new BroadcastResponseStatsTracker(mAppStandby, getContext());
@@ -424,6 +460,9 @@ public class UsageStatsService extends SystemService implements
            }
            mUserUnlockedStates.remove(userId);
            mUserState.put(userId, null); // release the service (mainly for GC)
        }

        synchronized (mLaunchTimeAlarmQueues) {
            LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
            if (alarmQueue != null) {
                alarmQueue.removeAllAlarms();
@@ -476,11 +515,13 @@ public class UsageStatsService extends SystemService implements
            }
            reportEvent(unlockEvent, userId);

            mHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK, userId, 0).sendToTarget();
            mIoHandler.obtainMessage(MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK,
                    userId, 0).sendToTarget();

            // Remove all the stats stored in memory and in system DE.
            mReportedEvents.remove(userId);
            deleteRecursively(new File(Environment.getDataSystemDeDirectory(userId), "usagestats"));

            // Force a flush to disk for the current user to ensure important events are persisted.
            // Note: there is a very very small chance that the system crashes between deleting
            // the stats above from DE and persisting them to CE here in which case we will lose
@@ -599,7 +640,7 @@ public class UsageStatsService extends SystemService implements
    private final IUidObserver mUidObserver = new UidObserver() {
        @Override
        public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
            mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
            mIoHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
        }

        @Override
@@ -671,16 +712,18 @@ public class UsageStatsService extends SystemService implements
                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED);
    }

    private static void deleteRecursively(File f) {
        File[] files = f.listFiles();
    private static void deleteRecursively(final File path) {
        if (path.isDirectory()) {
            final File[] files = path.listFiles();
            if (files != null) {
                for (File subFile : files) {
                    deleteRecursively(subFile);
                }
            }
        }

        if (f.exists() && !f.delete()) {
            Slog.e(TAG, "Failed to delete " + f);
        if (path.exists() && !path.delete()) {
            Slog.e(TAG, "Failed to delete " + path);
        }
    }

@@ -1241,6 +1284,9 @@ public class UsageStatsService extends SystemService implements
            Slog.i(TAG, "Removing user " + userId + " and all data.");
            mUserState.remove(userId);
            mAppTimeLimit.onUserRemoved(userId);
        }

        synchronized (mLaunchTimeAlarmQueues) {
            final LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
            if (alarmQueue != null) {
                alarmQueue.removeAllAlarms();
@@ -1271,6 +1317,13 @@ public class UsageStatsService extends SystemService implements
            }
        }

        synchronized (mLaunchTimeAlarmQueues) {
            final LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
            if (alarmQueue != null) {
                alarmQueue.removeAlarmForKey(packageName);
            }
        }

        final int tokenRemoved;
        synchronized (mLock) {
            final long timeRemoved = System.currentTimeMillis();
@@ -1279,10 +1332,7 @@ public class UsageStatsService extends SystemService implements
                // when the user service is initialized and package manager is queried.
                return;
            }
            final LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
            if (alarmQueue != null) {
                alarmQueue.removeAlarmForKey(packageName);
            }

            final UserUsageStatsService userService = mUserState.get(userId);
            if (userService == null) {
                return;
@@ -1492,23 +1542,27 @@ public class UsageStatsService extends SystemService implements
            estimatedLaunchTime = calculateEstimatedPackageLaunchTime(userId, packageName);
            mAppStandby.setEstimatedLaunchTime(packageName, userId, estimatedLaunchTime);

            synchronized (mLock) {
            getOrCreateLaunchTimeAlarmQueue(userId).addAlarm(packageName,
                    SystemClock.elapsedRealtime() + (estimatedLaunchTime - now));
        }
        return estimatedLaunchTime;
    }

    private LaunchTimeAlarmQueue getOrCreateLaunchTimeAlarmQueue(int userId) {
        synchronized (mLaunchTimeAlarmQueues) {
            LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
            if (alarmQueue == null) {
                alarmQueue = new LaunchTimeAlarmQueue(
                    userId, getContext(), BackgroundThread.get().getLooper());
                mLaunchTimeAlarmQueues.put(userId, alarmQueue);
            }
                alarmQueue.addAlarm(packageName,
                        SystemClock.elapsedRealtime() + (estimatedLaunchTime - now));
            }

            return alarmQueue;
        }
        return estimatedLaunchTime;
    }

    @CurrentTimeMillisLong
    private long calculateEstimatedPackageLaunchTime(int userId, String packageName) {
        synchronized (mLock) {
        final long endTime = System.currentTimeMillis();
        final long beginTime = endTime - ONE_WEEK;
        final long unknownTime = endTime + UNKNOWN_LAUNCH_TIME_DELAY_MS;
@@ -1546,7 +1600,6 @@ public class UsageStatsService extends SystemService implements
        } while (events.getNextEvent(event));
        return unknownTime;
    }
    }

    @CurrentTimeMillisLong
    private static long calculateNextLaunchTime(
@@ -1566,7 +1619,6 @@ public class UsageStatsService extends SystemService implements
    }

    private void handleEstimatedLaunchTimesOnUserUnlock(int userId) {
        synchronized (mLock) {
        final long nowElapsed = SystemClock.elapsedRealtime();
        final long now = System.currentTimeMillis();
        final long beginTime = now - ONE_WEEK;
@@ -1577,13 +1629,8 @@ public class UsageStatsService extends SystemService implements
        }
        final ArrayMap<String, Boolean> hasMoreThan24HoursOfHistory = new ArrayMap<>();
        final UsageEvents.Event event = new UsageEvents.Event();
            LaunchTimeAlarmQueue alarmQueue = mLaunchTimeAlarmQueues.get(userId);
            if (alarmQueue == null) {
                alarmQueue = new LaunchTimeAlarmQueue(
                        userId, getContext(), BackgroundThread.get().getLooper());
                mLaunchTimeAlarmQueues.put(userId, alarmQueue);
            }
        boolean changedTimes = false;
        final LaunchTimeAlarmQueue alarmQueue = getOrCreateLaunchTimeAlarmQueue(userId);
        for (boolean unprocessedEvent = events.getNextEvent(event); unprocessedEvent;
                unprocessedEvent = events.getNextEvent(event)) {
            final String packageName = event.getPackageName();
@@ -1622,7 +1669,6 @@ public class UsageStatsService extends SystemService implements
            mHandler.sendEmptyMessage(MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED);
        }
    }
    }

    private void setEstimatedLaunchTime(int userId, String packageName,
            @CurrentTimeMillisLong long estimatedLaunchTime) {
@@ -1989,37 +2035,11 @@ public class UsageStatsService extends SystemService implements
                case MSG_PACKAGE_REMOVED:
                    onPackageRemoved(msg.arg1, (String) msg.obj);
                    break;
                case MSG_UID_STATE_CHANGED: {
                    final int uid = msg.arg1;
                    final int procState = msg.arg2;

                    final int newCounter = (procState <= ActivityManager.PROCESS_STATE_TOP) ? 0 : 1;
                    synchronized (mUidToKernelCounter) {
                        final int oldCounter = mUidToKernelCounter.get(uid, 0);
                        if (newCounter != oldCounter) {
                            mUidToKernelCounter.put(uid, newCounter);
                            try {
                                FileUtils.stringToFile(KERNEL_COUNTER_FILE, uid + " " + newCounter);
                            } catch (IOException e) {
                                Slog.w(TAG, "Failed to update counter set: " + e);
                            }
                        }
                    }
                    break;
                }
                case MSG_ON_START:
                    synchronized (mLock) {
                        loadGlobalComponentUsageLocked();
                    }
                    break;
                case MSG_HANDLE_LAUNCH_TIME_ON_USER_UNLOCK: {
                    final int userId = msg.arg1;
                    Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
                            "usageStatsHandleEstimatedLaunchTimesOnUser(" + userId + ")");
                    handleEstimatedLaunchTimesOnUserUnlock(userId);
                    Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
                }
                break;
                case MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED: {
                    removeMessages(MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED);