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

Commit 9c7f3d30 authored by Xin Guan's avatar Xin Guan Committed by Android (Google) Code Review
Browse files

Merge "Modify usagestats event processing thread" into main

parents fd5ca1ee 66c56a0c
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);