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

Commit 41a95d30 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Include getMainUser() calls on dumpsys user." into main

parents f270d592 743580bc
Loading
Loading
Loading
Loading
+131 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2025 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.pm;

import android.annotation.Nullable;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.util.SparseIntArray;

import com.android.server.LocalServices;

import java.io.PrintWriter;

// TODO(b/414326600): rename (and add unit tests) once it's used to log blocked HSU actions
/**
 * Class used to report deprecated calls.
 */
final class MultiuserDeprecationReporter {

    private final Handler mHandler;

    // Key is "absolute" uid  / app id (i.e., stripping out the user id part), value is count.
    @Nullable // Only set when logging is enabled
    private final SparseIntArray mGetMainUserCalls;

    // Set on demand, Should not be used directly (but through getPackageManagerInternal() instead).
    @Nullable
    private PackageManagerInternal mPmInternal;

    MultiuserDeprecationReporter(Handler handler) {
        mHandler = handler;
        if (Build.isDebuggable()) {
            mGetMainUserCalls = new SparseIntArray();
        } else
            mGetMainUserCalls = null;
    }

    // TODO(b/414326600): add unit tests (once the proper formats are determined).
    void logGetMainUserCall() {
        if (mGetMainUserCalls == null) {
            return;
        }

        // Must set before posting to the handler (otherwise it would always return the system UID)
        int uid = Binder.getCallingUid();

        mHandler.post(() -> {
            int canonicalUid = UserHandle.getAppId(uid);
            int newCount = mGetMainUserCalls.get(canonicalUid, 0) + 1;
            mGetMainUserCalls.put(canonicalUid, newCount);
        });
    }

    // NOTE: output format might changed, so it should not be used for automated testing purposes
    // (a proto version will be provided when it's ready)
    void dump(PrintWriter pw) {
        // TODO(b/414326600): add unit tests (once the proper formats are determined).
        if (mGetMainUserCalls == null) {
            pw.println("Not logging getMainUser() calls");
            return;
        }

        // TODO(b/414326600): should dump in the mHandler thread (as its state is written in that
        // thread) , but it would require blocking the caller until it's done


        // TODO(b/414326600): should also dump on proto, but we need to wait until the format is
        // properly defined (for example, we might want to log a generic "user violation" that would
        // include other metrics such as stuff that shouldn't be called when the current user is the
        // headless system user)
        int size = mGetMainUserCalls.size();
        if (size == 0) {
            pw.println("Good News, Everyone!: no app called getMainUser()!");
            return;
        }
        pw.printf("%d apps called getMainUser():\n", size);
        var pm = getPackageManagerInternal();
        for (int i = 0; i < size; i++) {
            int canonicalUid = mGetMainUserCalls.keyAt(i);
            int count = mGetMainUserCalls.valueAt(i);
            String pkgName = getPackageNameForLoggingPurposes(pm, canonicalUid);
            // uid is the canonical UID, but including "canonical" would add extra churn / bytes
            pw.printf("  %s (uid %d): %d calls\n", pkgName, canonicalUid, count);
        }
    }

    /** Retrieves the internal package manager interface. */
    private PackageManagerInternal getPackageManagerInternal() {
        // Don't need to synchronize; worst-case scenario LocalServices will be called twice.
        if (mPmInternal == null) {
            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
        }
        return mPmInternal;
    }

    // TODO(b/414326600): this method is taking a simplest aproach to get the uid, but it's not
    // handling corner cases like an app not available on user 0 or multiple apps with the same uid.
    // This is fine for now, but the final solution need to take those scenarios in account.
    private static String getPackageNameForLoggingPurposes(PackageManagerInternal pm, int uid) {
        if (uid == Process.SYSTEM_UID) {
            // Many apps might be running as system (because they declare sharedUserId in the
            // manifest), so we wouldn't know for sure which one calls it here
            return "system";
        }
        var pkg = pm.getPackage(uid);
        // TODO(b/414326600): if it's from system, it might be useful to log the method that's
        // calling it, but that's expensive (so we should guard using a system property) and we'd
        // need to change the type of mGetMainUserCalls as well - for now, the solution is to look
        // at logcat (which logs the full stacktrace the tag is VERBOSE).
        // TODO(b/414326600): figure out proper way to handle null (for example, it'is also null
        // for root UID).
        return pkg == null ? "system" :  pkg.getPackageName();
    }
}
+28 −17
Original line number Original line Diff line number Diff line
@@ -398,6 +398,7 @@ public class UserManagerService extends IUserManager.Stub {
    private final Object mAppRestrictionsLock = new Object();
    private final Object mAppRestrictionsLock = new Object();


    private final Handler mHandler;
    private final Handler mHandler;
    private final MultiuserDeprecationReporter mDeprecationReporter;


    private final ThreadPoolExecutor mInternalExecutor;
    private final ThreadPoolExecutor mInternalExecutor;


@@ -1111,6 +1112,7 @@ public class UserManagerService extends IUserManager.Stub {
        mPackagesLock = packagesLock;
        mPackagesLock = packagesLock;
        mUsers = users != null ? users : new SparseArray<>();
        mUsers = users != null ? users : new SparseArray<>();
        mHandler = new MainHandler();
        mHandler = new MainHandler();
        mDeprecationReporter = new MultiuserDeprecationReporter(mHandler);
        mInternalExecutor = new ThreadPoolExecutor(/* corePoolSize */ 0, /* maximumPoolSize */ 1,
        mInternalExecutor = new ThreadPoolExecutor(/* corePoolSize */ 0, /* maximumPoolSize */ 1,
                /* keepAliveTime */ 24, TimeUnit.HOURS, new LinkedBlockingQueue<>());
                /* keepAliveTime */ 24, TimeUnit.HOURS, new LinkedBlockingQueue<>());
        mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
        mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
@@ -1369,6 +1371,7 @@ public class UserManagerService extends IUserManager.Stub {
    @Override
    @Override
    public @CanBeNULL @UserIdInt int getMainUserId() {
    public @CanBeNULL @UserIdInt int getMainUserId() {
        checkQueryOrCreateUsersPermission("get main user id");
        checkQueryOrCreateUsersPermission("get main user id");
        mDeprecationReporter.logGetMainUserCall();
        return getMainUserIdUnchecked();
        return getMainUserIdUnchecked();
    }
    }


@@ -1391,7 +1394,6 @@ public class UserManagerService extends IUserManager.Stub {
        return null;
        return null;
    }
    }



    private @CanBeNULL @UserIdInt int getPrivateProfileUserId() {
    private @CanBeNULL @UserIdInt int getPrivateProfileUserId() {
        synchronized (mUsersLock) {
        synchronized (mUsersLock) {
            for (int userId : getUserIds()) {
            for (int userId : getUserIds()) {
@@ -4775,10 +4777,13 @@ public class UserManagerService extends IUserManager.Stub {
    /**
    /**
     * Checks whether the default state of the device is headless system user mode, i.e. what the
     * Checks whether the default state of the device is headless system user mode, i.e. what the
     * mode would be if we did a fresh factory reset.
     * mode would be if we did a fresh factory reset.
     * If the mode is  being emulated (via SYSTEM_USER_MODE_EMULATION_PROPERTY) then that will be
     *
     * returned instead.
     * <p>If the mode is being emulated (through the
     * Note that, even in the absence of emulation, a device might deviate from the current default
     * {@link UserManager#SYSTEM_USER_MODE_EMULATION_PROPERTY} system property) then the value
     * due to an OTA changing the default (which won't change the already-decided mode).
     * represented by that system property will be returned instead.
     *
     * <p>Note that, even in the absence of emulation, a device might deviate from the current
     * default due to an OTA changing the default (which won't change the already-decided mode).
     */
     */
    private boolean isDefaultHeadlessSystemUserMode() {
    private boolean isDefaultHeadlessSystemUserMode() {
        if (!Build.isDebuggable()) {
        if (!Build.isDebuggable()) {
@@ -8041,6 +8046,9 @@ public class UserManagerService extends IUserManager.Stub {
                case "--visibility-mediator":
                case "--visibility-mediator":
                    mUserVisibilityMediator.dump(pw, args);
                    mUserVisibilityMediator.dump(pw, args);
                    return;
                    return;
                case "--deprecated-calls":
                    mDeprecationReporter.dump(pw);
                    return;
            }
            }
        }
        }


@@ -8179,17 +8187,23 @@ public class UserManagerService extends IUserManager.Stub {
            mUserTypes.valueAt(i).dump(pw, "        ");
            mUserTypes.valueAt(i).dump(pw, "        ");
        }
        }


        // TODO: create IndentingPrintWriter at the beginning of dump() and use the proper
        pw.println();
        // indentation methods instead of explicit printing "  "
        mDeprecationReporter.dump(pw);
        try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw)) {

        // NOTE: add new stuff here, as pw is closed after the try-with-resources block below


        // TODO(b/163423525): create IndentingPrintWriter at the beginning of dump() and use the
        // proper indentation methods instead of explicit printing "  "; that would also solve the
        // pw closure as well.
        try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw)) {
            // Dump SystemPackageInstaller info
            // Dump SystemPackageInstaller info
            ipw.println();
            ipw.println();
            mSystemPackageInstaller.dump(ipw);
            mSystemPackageInstaller.dump(ipw);
        }


        // NOTE: pw's not available after this point as it's auto-closed by ipw, so new dump
        // NOTE: pw's not available after this point as it's auto-closed by ipw, so new dump
        // statements should use ipw below
        // statements should use ipw below
        }

    }
    }


    private void dumpUser(PrintWriter pw, @CanBeCURRENT @UserIdInt int userId, StringBuilder sb,
    private void dumpUser(PrintWriter pw, @CanBeCURRENT @UserIdInt int userId, StringBuilder sb,
@@ -8348,8 +8362,8 @@ public class UserManagerService extends IUserManager.Stub {
                        if (userData != null) {
                        if (userData != null) {
                            writeUserLP(userData);
                            writeUserLP(userData);
                        } else {
                        } else {
                            Slog.i(LOG_TAG, "handle(WRITE_USER_MSG): no data for user " + userId
                            Slogf.i(LOG_TAG, "handle(WRITE_USER_MSG): no data for user %d, it was "
                                    + ", it was probably removed before handler could handle it");
                                    + "probably removed before handler could handle it", userId);
                        }
                        }
                    }
                    }
                    break;
                    break;
@@ -8858,8 +8872,6 @@ public class UserManagerService extends IUserManager.Stub {
        }
        }
    } // class LocalService
    } // class LocalService




    /**
    /**
     * Check if user has restrictions
     * Check if user has restrictions
     * @param restriction restrictions to check
     * @param restriction restrictions to check
@@ -8978,7 +8990,7 @@ public class UserManagerService extends IUserManager.Stub {


    /** Retrieves the internal package manager interface. */
    /** Retrieves the internal package manager interface. */
    private PackageManagerInternal getPackageManagerInternal() {
    private PackageManagerInternal getPackageManagerInternal() {
        // Don't need to synchonize; worst-case scenario LocalServices will be called twice.
        // Don't need to synchronize; worst-case scenario LocalServices will be called twice.
        if (mPmInternal == null) {
        if (mPmInternal == null) {
            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
        }
        }
@@ -9156,5 +9168,4 @@ public class UserManagerService extends IUserManager.Stub {
    public UserJourneyLogger getUserJourneyLogger() {
    public UserJourneyLogger getUserJourneyLogger() {
        return mUserJourneyLogger;
        return mUserJourneyLogger;
    }
    }

}
}