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

Commit 73dded28 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Add hidden APIs that return user start/unlock time.

Bug: 69456806
Test: Manual test with "watch dumpsys-user"
Change-Id: I68ed4ef53c707ed7c8cb8be4165052f942ea8ccd
parent 229d2cdb
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -98,4 +98,6 @@ interface IUserManager {
    boolean isUserNameSet(int userHandle);
    boolean hasRestrictedProfiles();
    boolean trySetQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userHandle, in IntentSender target);
    long getUserStartRealtime();
    long getUserUnlockRealtime();
}
+28 −0
Original line number Diff line number Diff line
@@ -1391,6 +1391,34 @@ public class UserManager {
        }
    }

    /**
     * Return the time when the calling user started in elapsed milliseconds since boot,
     * or 0 if not started.
     *
     * @hide
     */
    public long getUserStartRealtime() {
        try {
            return mService.getUserStartRealtime();
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Return the time when the calling user was unlocked elapsed milliseconds since boot,
     * or 0 if not unlocked.
     *
     * @hide
     */
    public long getUserUnlockRealtime() {
        try {
            return mService.getUserUnlockRealtime();
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }

    /**
     * Returns the UserInfo object describing a specific user.
     * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+86 −18
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import android.os.SELinux;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -79,6 +80,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;

@@ -241,8 +243,7 @@ public class UserManagerService extends IUserManager.Stub {
    private static final IBinder mUserRestriconToken = new Binder();

    /**
     * User-related information that is used for persisting to flash. Only UserInfo is
     * directly exposed to other system apps.
     * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
     */
    @VisibleForTesting
    static class UserData {
@@ -260,6 +261,12 @@ public class UserManagerService extends IUserManager.Stub {
        // Whether to perist the seed account information to be available after a boot
        boolean persistSeedData;

        /** Elapsed realtime since boot when the user started. */
        long startRealtime;

        /** Elapsed realtime since boot when the user was unlocked. */
        long unlockRealtime;

        void clearSeedAccountData() {
            seedAccountName = null;
            seedAccountType = null;
@@ -453,6 +460,37 @@ public class UserManagerService extends IUserManager.Stub {
                mUms.cleanupPartialUsers();
            }
        }

        @Override
        public void onStartUser(int userHandle) {
            synchronized (mUms.mUsersLock) {
                final UserData user = mUms.getUserDataLU(userHandle);
                if (user != null) {
                    user.startRealtime = SystemClock.elapsedRealtime();
                }
            }
        }

        @Override
        public void onUnlockUser(int userHandle) {
            synchronized (mUms.mUsersLock) {
                final UserData user = mUms.getUserDataLU(userHandle);
                if (user != null) {
                    user.unlockRealtime = SystemClock.elapsedRealtime();
                }
            }
        }

        @Override
        public void onStopUser(int userHandle) {
            synchronized (mUms.mUsersLock) {
                final UserData user = mUms.getUserDataLU(userHandle);
                if (user != null) {
                    user.startRealtime = 0;
                    user.unlockRealtime = 0;
                }
            }
        }
    }

    // TODO b/28848102 Add support for test dependencies injection
@@ -1057,6 +1095,29 @@ public class UserManagerService extends IUserManager.Stub {
        return mLocalService.isUserRunning(userId);
    }

    @Override
    public long getUserStartRealtime() {
        final int userId = UserHandle.getUserId(Binder.getCallingUid());
        synchronized (mUsersLock) {
            final UserData user = getUserDataLU(userId);
            if (user != null) {
                return user.startRealtime;
            }
            return 0;
        }
    }

    @Override
    public long getUserUnlockRealtime() {
        synchronized (mUsersLock) {
            final UserData user = getUserDataLU(UserHandle.getUserId(Binder.getCallingUid()));
            if (user != null) {
                return user.unlockRealtime;
            }
            return 0;
        }
    }

    private void checkManageOrInteractPermIfCallerInOtherProfileGroup(int userId, String name) {
        int callingUserId = UserHandle.getCallingUserId();
        if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
@@ -3484,6 +3545,7 @@ public class UserManagerService extends IUserManager.Stub {
        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;

        long now = System.currentTimeMillis();
        final long nowRealtime = SystemClock.elapsedRealtime();
        StringBuilder sb = new StringBuilder();
        synchronized (mPackagesLock) {
            synchronized (mUsersLock) {
@@ -3511,25 +3573,20 @@ public class UserManagerService extends IUserManager.Stub {
                    }
                    pw.println(UserState.stateToString(state));
                    pw.print("    Created: ");
                    if (userInfo.creationTime == 0) {
                        pw.println("<unknown>");
                    } else {
                        sb.setLength(0);
                        TimeUtils.formatDuration(now - userInfo.creationTime, sb);
                        sb.append(" ago");
                        pw.println(sb);
                    }
                    dumpTimeAgo(pw, sb, now, userInfo.creationTime);

                    pw.print("    Last logged in: ");
                    if (userInfo.lastLoggedInTime == 0) {
                        pw.println("<unknown>");
                    } else {
                        sb.setLength(0);
                        TimeUtils.formatDuration(now - userInfo.lastLoggedInTime, sb);
                        sb.append(" ago");
                        pw.println(sb);
                    }
                    dumpTimeAgo(pw, sb, now, userInfo.lastLoggedInTime);

                    pw.print("    Last logged in fingerprint: ");
                    pw.println(userInfo.lastLoggedInFingerprint);

                    pw.print("    Start time: ");
                    dumpTimeAgo(pw, sb, nowRealtime, userData.startRealtime);

                    pw.print("    Unlock time: ");
                    dumpTimeAgo(pw, sb, nowRealtime, userData.unlockRealtime);

                    pw.print("    Has profile owner: ");
                    pw.println(mIsUserManaged.get(userId));
                    pw.println("    Restrictions:");
@@ -3593,6 +3650,17 @@ public class UserManagerService extends IUserManager.Stub {
        }
    }

    private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
        if (time == 0) {
            pw.println("<unknown>");
        } else {
            sb.setLength(0);
            TimeUtils.formatDuration(nowTime - time, sb);
            sb.append(" ago");
            pw.println(sb);
        }
    }

    final class MainHandler extends Handler {

        @Override