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

Commit f0226c3a authored by Michael Wachenschwanz's avatar Michael Wachenschwanz
Browse files

Wrap all state changes that can affect OomAdjuster computation

The new ProcessStateController will be responsible for handling all
state changes that can affect OomAdjuster computation. This CL is the
first step of several and just does a benign replacements of all state
changes with a call into ProcessStateController.

Flag: EXEMPTED refactor
Bug: 346822474
Test: atest MockingOomAdjusterTests
Change-Id: Iefc05dd5996c86eab362e73054821702aa87e927
parent b6924395
Loading
Loading
Loading
Loading
+51 −44
Original line number Diff line number Diff line
@@ -1189,8 +1189,8 @@ public final class ActiveServices {
            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
        }
        final boolean wasStartRequested = r.startRequested;
        r.lastActivity = SystemClock.uptimeMillis();
        r.startRequested = true;
        mAm.mProcessStateController.setServiceLastActivityTime(r, SystemClock.uptimeMillis());
        mAm.mProcessStateController.setStartRequested(r, true);
        r.delayedStop = false;
        r.fgRequired = fgRequired;
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
@@ -1623,7 +1623,7 @@ public final class ActiveServices {
            FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
                    serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
            mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
            service.startRequested = false;
            mAm.mProcessStateController.setStartRequested(service, false);
            if (service.tracker != null) {
                synchronized (mAm.mProcessStats.mLock) {
                    service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -1812,7 +1812,7 @@ public final class ActiveServices {
            FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
                    serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
            mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
            r.startRequested = false;
            mAm.mProcessStateController.setStartRequested(r, false);
            if (r.tracker != null) {
                synchronized (mAm.mProcessStats.mLock) {
                    r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -2618,7 +2618,7 @@ public final class ActiveServices {
                    }
                    notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
                    r.foregroundNoti = notification;
                    r.foregroundServiceType = foregroundServiceType;
                    mAm.mProcessStateController.setForegroundServiceType(r, foregroundServiceType);
                    if (!r.isForeground) {
                        final ServiceMap smap = getServiceMapLocked(r.userId);
                        if (smap != null) {
@@ -2643,7 +2643,7 @@ public final class ActiveServices {
                            }
                            active.mNumActive++;
                        }
                        r.isForeground = true;
                        mAm.mProcessStateController.setIsForegroundService(r, true);

                        // The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
                        // be deferred, make a copy of mAllowStartForeground and
@@ -2772,7 +2772,7 @@ public final class ActiveServices {
                    }
                }

                r.isForeground = false;
                mAm.mProcessStateController.setIsForegroundService(r, false);
                r.mFgsExitTime = SystemClock.uptimeMillis();
                synchronized (mAm.mProcessStats.mLock) {
                    final ServiceState stracker = r.getTracker();
@@ -3565,7 +3565,7 @@ public final class ActiveServices {
    private void maybeUpdateShortFgsTrackingLocked(ServiceRecord sr,
            boolean extendTimeout) {
        if (!sr.isShortFgs()) {
            sr.clearShortFgsInfo(); // Just in case we have it.
            mAm.mProcessStateController.clearShortFgsInfo(sr); // Just in case we have it.
            unscheduleShortFgsTimeoutLocked(sr);
            return;
        }
@@ -3581,7 +3581,7 @@ public final class ActiveServices {
                }
            }
            traceInstant("short FGS start/extend: ", sr);
            sr.setShortFgsInfo(SystemClock.uptimeMillis());
            mAm.mProcessStateController.setShortFgsInfo(sr, SystemClock.uptimeMillis());

            // We'll restart the timeout.
            unscheduleShortFgsTimeoutLocked(sr);
@@ -3605,7 +3605,7 @@ public final class ActiveServices {
     * Stop the timeout for a ServiceRecord, if it's of a short-FGS.
     */
    private void maybeStopShortFgsTimeoutLocked(ServiceRecord sr) {
        sr.clearShortFgsInfo(); // Always clear, just in case.
        mAm.mProcessStateController.clearShortFgsInfo(sr); // Always clear, just in case.
        if (!sr.isShortFgs()) {
            return;
        }
@@ -3993,7 +3993,7 @@ public final class ActiveServices {
    private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
        maybeStopShortFgsTimeoutLocked(service);
        final ProcessServiceRecord psr = service.app.mServices;
        psr.stopService(service);
        mAm.mProcessStateController.stopService(psr, service);
        psr.updateBoundClientUids();
        if (service.allowlistManager) {
            updateAllowlistManagerLocked(psr);
@@ -4047,7 +4047,7 @@ public final class ActiveServices {
            }
        }
        if (anyClientActivities != psr.hasClientActivities()) {
            psr.setHasClientActivities(anyClientActivities);
            mAm.mProcessStateController.setHasClientActivities(psr, anyClientActivities);
            if (updateLru) {
                mAm.updateLruProcessLocked(psr.mApp, anyClientActivities, null);
            }
@@ -4216,7 +4216,8 @@ public final class ActiveServices {
            }

            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                mAm.mProcessStateController.setServiceLastActivityTime(s,
                        SystemClock.uptimeMillis());
                if (!s.hasAutoCreateConnections()) {
                    // This is the first binding, let the tracker know.
                    synchronized (mAm.mProcessStats.mLock) {
@@ -4253,12 +4254,12 @@ public final class ActiveServices {
            if (activity != null) {
                activity.addConnection(c);
            }
            clientPsr.addConnection(c);
            mAm.mProcessStateController.addConnection(clientPsr, c);
            c.startAssociationIfNeeded();
            // Don't set hasAboveClient if binding to self to prevent modifyRawOomAdj() from
            // dropping the process' adjustment level.
            if (b.client != s.app && c.hasFlag(Context.BIND_ABOVE_CLIENT)) {
                clientPsr.setHasAboveClient(true);
                mAm.mProcessStateController.setHasAboveClient(clientPsr, true);
            }
            if (c.hasFlag(BIND_ALLOW_WHITELIST_MANAGEMENT)) {
                s.allowlistManager = true;
@@ -4274,7 +4275,8 @@ public final class ActiveServices {
            if (s.app != null && s.app.mState != null
                    && s.app.mState.getCurProcState() <= PROCESS_STATE_TOP
                    && c.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)) {
                s.lastTopAlmostPerceptibleBindRequestUptimeMs = SystemClock.uptimeMillis();
                mAm.mProcessStateController.setLastTopAlmostPerceptibleBindRequest(s,
                        SystemClock.uptimeMillis());
            }

            if (s.app != null) {
@@ -4312,7 +4314,8 @@ public final class ActiveServices {

            boolean needOomAdj = false;
            if (c.hasFlag(Context.BIND_AUTO_CREATE)) {
                s.lastActivity = SystemClock.uptimeMillis();
                mAm.mProcessStateController.setServiceLastActivityTime(s,
                        SystemClock.uptimeMillis());
                needOomAdj = (serviceBindingOomAdjPolicy
                        & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) == 0;
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
@@ -4328,7 +4331,7 @@ public final class ActiveServices {
            if (s.app != null) {
                ProcessServiceRecord servicePsr = s.app.mServices;
                if (c.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
                    servicePsr.setTreatLikeActivity(true);
                    mAm.mProcessStateController.setTreatLikeActivity(servicePsr, true);
                }
                if (s.allowlistManager) {
                    servicePsr.mAllowlistManager = true;
@@ -4575,7 +4578,9 @@ public final class ActiveServices {
                    }
                    // This could have made the service less important.
                    if (r.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) {
                        psr.setTreatLikeActivity(true);
                        // TODO(b/367545398): the following line is a bug. A service unbind
                        //  should potentially lower a process's importance, not elevate it.
                        mAm.mProcessStateController.setTreatLikeActivity(psr, true);
                        mAm.updateLruProcessLocked(app, true, null);
                    }
                    // If the bindee is more important than the binder, we may skip the OomAdjuster.
@@ -5162,8 +5167,9 @@ public final class ActiveServices {
            }
            if (r.app != null) {
                psr = r.app.mServices;
                psr.startExecutingService(r);
                psr.setExecServicesFg(psr.shouldExecServicesFg() || fg);
                mAm.mProcessStateController.startExecutingService(psr, r);
                mAm.mProcessStateController.setExecServicesFg(psr,
                        psr.shouldExecServicesFg() || fg);
                if (timeoutNeeded && psr.numberOfExecutingServices() == 1) {
                    if (!shouldSkipTimeout) {
                        scheduleServiceTimeoutLocked(r.app);
@@ -5175,7 +5181,7 @@ public final class ActiveServices {
        } else if (r.app != null && fg) {
            psr = r.app.mServices;
            if (!psr.shouldExecServicesFg()) {
                psr.setExecServicesFg(true);
                mAm.mProcessStateController.setExecServicesFg(psr, true);
                if (timeoutNeeded) {
                    if (!shouldSkipTimeout) {
                        scheduleServiceTimeoutLocked(r.app);
@@ -6020,11 +6026,13 @@ public final class ActiveServices {
            Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
                    + ", ProcessRecord.uid = " + app.uid);
        r.setProcess(app, thread, pid, uidRecord);
        r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
        final long now = SystemClock.uptimeMillis();
        r.restartTime = now;
        mAm.mProcessStateController.setServiceLastActivityTime(r, now);
        final boolean skipOomAdj = (serviceBindingOomAdjPolicy
                & SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) != 0;
        final ProcessServiceRecord psr = app.mServices;
        final boolean newService = psr.startService(r);
        final boolean newService = mAm.mProcessStateController.startService(psr, r);
        bumpServiceExecutingLocked(r, execInFg, "create",
                OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */,
                skipOomAdj /* skipTimeoutIfPossible */);
@@ -6083,7 +6091,7 @@ public final class ActiveServices {

                // Cleanup.
                if (newService) {
                    psr.stopService(r);
                    mAm.mProcessStateController.stopService(psr, r);
                    r.setProcess(null, null, 0, null);
                }

@@ -6428,7 +6436,7 @@ public final class ActiveServices {
            mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
        }

        r.isForeground = false;
        mAm.mProcessStateController.setIsForegroundService(r, false);
        r.mFgsNotificationWasDeferred = false;
        dropFgsNotificationStateLocked(r);
        r.foregroundId = 0;
@@ -6579,9 +6587,9 @@ public final class ActiveServices {
        }
        if (b.client != skipApp) {
            final ProcessServiceRecord psr = b.client.mServices;
            psr.removeConnection(c);
            mAm.mProcessStateController.removeConnection(psr, c);
            if (c.hasFlag(Context.BIND_ABOVE_CLIENT)) {
                psr.updateHasAboveClientLocked();
                mAm.mProcessStateController.updateHasAboveClientLocked(psr);
            }
            // If this connection requested allowlist management, see if we should
            // now clear that state.
@@ -6597,7 +6605,7 @@ public final class ActiveServices {
            }
            // And for almost perceptible exceptions.
            if (c.hasFlag(Context.BIND_ALMOST_PERCEPTIBLE)) {
                psr.updateHasTopStartedAlmostPerceptibleServices();
                mAm.mProcessStateController.updateHasTopStartedAlmostPerceptibleServices(psr);
            }
            if (s.app != null) {
                updateServiceClientActivitiesLocked(s.app.mServices, c, true);
@@ -6796,8 +6804,8 @@ public final class ActiveServices {
                final ProcessServiceRecord psr = r.app.mServices;
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                        "Nesting at 0 of " + r.shortInstanceName);
                psr.setExecServicesFg(false);
                psr.stopExecutingService(r);
                mAm.mProcessStateController.setExecServicesFg(psr, false);
                mAm.mProcessStateController.stopExecutingService(psr, r);
                if (psr.numberOfExecutingServices() == 0) {
                    if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
                            "No more executingServices of " + r.shortInstanceName);
@@ -6806,7 +6814,7 @@ public final class ActiveServices {
                    // Need to re-evaluate whether the app still needs to be in the foreground.
                    for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
                        if (psr.getExecutingServiceAt(i).executeFg) {
                            psr.setExecServicesFg(true);
                            mAm.mProcessStateController.setExecServicesFg(psr, true);
                            break;
                        }
                    }
@@ -6819,9 +6827,9 @@ public final class ActiveServices {
                }
                if (oomAdjReason != OOM_ADJ_REASON_NONE) {
                    if (enqueueOomAdj) {
                        mAm.enqueueOomAdjTargetLocked(r.app);
                        mAm.mProcessStateController.enqueueUpdateTarget(r.app);
                    } else {
                        mAm.updateOomAdjLocked(r.app, oomAdjReason);
                        mAm.mProcessStateController.runUpdate(r.app, oomAdjReason);
                    }
                } else {
                    // Skip oom adj if it wasn't bumped during the bumpServiceExecutingLocked()
@@ -7206,8 +7214,7 @@ public final class ActiveServices {
            removeConnectionLocked(r, app, null, true);
        }
        updateServiceConnectionActivitiesLocked(psr);
        psr.removeAllConnections();
        psr.removeAllSdkSandboxConnections();
        mAm.mProcessStateController.removeAllConnections(psr);

        psr.mAllowlistManager = false;

@@ -7217,7 +7224,7 @@ public final class ActiveServices {
            mAm.mBatteryStatsService.noteServiceStopLaunch(sr.appInfo.uid, sr.name.getPackageName(),
                    sr.name.getClassName());
            if (sr.app != app && sr.app != null && !sr.app.isPersistent()) {
                sr.app.mServices.stopService(sr);
                mAm.mProcessStateController.stopService(psr, sr);
                sr.app.mServices.updateBoundClientUids();
            }
            sr.setProcess(null, null, 0, null);
@@ -7287,7 +7294,7 @@ public final class ActiveServices {
            // Unless the process is persistent, this process record is going away,
            // so make sure the service is cleaned out of it.
            if (!app.isPersistent()) {
                psr.stopService(sr);
                mAm.mProcessStateController.stopService(psr, sr);
                psr.updateBoundClientUids();
            }

@@ -7328,7 +7335,7 @@ public final class ActiveServices {
                    // Update to stopped state because the explicit start is gone. The service is
                    // scheduled to restart for other reason (e.g. connections) so we don't bring
                    // down it.
                    sr.startRequested = false;
                    mAm.mProcessStateController.setStartRequested(sr, false);
                    if (sr.tracker != null) {
                        synchronized (mAm.mProcessStats.mLock) {
                            sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -7342,7 +7349,7 @@ public final class ActiveServices {
        mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_STOP_SERVICE);

        if (!allowRestart) {
            psr.stopAllServices();
            mAm.mProcessStateController.stopAllServices(psr);
            psr.clearBoundClientUids();

            // Make sure there are no more restarting services for this process.
@@ -7384,7 +7391,7 @@ public final class ActiveServices {
            }
        }

        psr.stopAllExecutingServices();
        mAm.mProcessStateController.stopAllExecutingServices(psr);
        psr.noteScheduleServiceTimeoutPending(false);
    }

@@ -9210,14 +9217,14 @@ public final class ActiveServices {
                new ForegroundServiceDelegation(options, connection);
        r.mFgsDelegation = delegation;
        mFgsDelegations.put(delegation, r);
        r.isForeground = true;
        mAm.mProcessStateController.setIsForegroundService(r, true);
        r.mFgsEnterTime = SystemClock.uptimeMillis();
        r.foregroundServiceType = options.mForegroundServiceTypes;
        mAm.mProcessStateController.setForegroundServiceType(r, options.mForegroundServiceTypes);
        r.updateOomAdjSeq();
        setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId,
                BackgroundStartPrivileges.NONE,  false /* isBindService */);
        final ProcessServiceRecord psr = callerApp.mServices;
        final boolean newService = psr.startService(r);
        final boolean newService = mAm.mProcessStateController.startService(psr, r);
        // updateOomAdj.
        updateServiceForegroundLocked(psr, /* oomAdj= */ true);

+44 −39

File changed.

Preview size limit exceeded, changes collapsed.

+2 −0
Original line number Diff line number Diff line
@@ -1356,6 +1356,7 @@ public class AppProfiler {
    @GuardedBy("mService")
    void setMemFactorOverrideLocked(@MemFactor int factor) {
        mMemFactorOverride = factor;
        mService.mProcessStateController.setIsLastMemoryLevelNormal(isLastMemoryLevelNormal());
    }

    @GuardedBy({"mService", "mProcLock"})
@@ -1423,6 +1424,7 @@ public class AppProfiler {
        }

        mLastMemoryLevel = memFactor;
        mService.mProcessStateController.setIsLastMemoryLevelNormal(isLastMemoryLevelNormal());
        mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();

        // Dispatch UI_HIDDEN to processes that need it
+15 −11
Original line number Diff line number Diff line
@@ -325,7 +325,8 @@ public class ContentProviderHelper {
                    final int verifiedAdj = cpr.proc.mState.getVerifiedAdj();
                    boolean success = !serviceBindingOomAdjPolicy()
                            || mService.mOomAdjuster.evaluateProviderConnectionAdd(r, cpr.proc)
                            ? mService.updateOomAdjLocked(cpr.proc, OOM_ADJ_REASON_GET_PROVIDER)
                            ? mService.mProcessStateController.runUpdate(cpr.proc,
                            OOM_ADJ_REASON_GET_PROVIDER)
                            : true;
                    // XXX things have changed so updateOomAdjLocked doesn't actually tell us
                    // if the process has been successfully adjusted.  So to reduce races with
@@ -534,10 +535,9 @@ public class ContentProviderHelper {
                            if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
                                Slog.d(TAG, "Installing in existing process " + proc);
                            }
                            final ProcessProviderRecord pr = proc.mProviders;
                            if (!pr.hasProvider(cpi.name)) {
                            if (mService.mProcessStateController.addPublishedProvider(proc,
                                    cpi.name, cpr)) {
                                checkTime(startTime, "getContentProviderImpl: scheduling install");
                                pr.installProvider(cpi.name, cpr);
                                mService.mOomAdjuster.unfreezeTemporarily(proc,
                                        CachedAppOptimizer.UNFREEZE_REASON_GET_PROVIDER);
                                try {
@@ -881,7 +881,8 @@ public class ContentProviderHelper {
            ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
            ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
            if (localCpr.hasExternalProcessHandles()) {
                if (localCpr.removeExternalProcessHandleLocked(token)) {
                if (mService.mProcessStateController.removeExternalProviderClient(localCpr,
                        token)) {
                    mService.updateOomAdjLocked(localCpr.proc, OOM_ADJ_REASON_REMOVE_PROVIDER);
                } else {
                    Slog.e(TAG, "Attempt to remove content provider " + localCpr
@@ -1447,7 +1448,8 @@ public class ContentProviderHelper {
            String callingPackage, String callingTag, boolean stable, boolean updateLru,
            long startTime, ProcessList processList, @UserIdInt int expectedUserId) {
        if (r == null) {
            cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
            mService.mProcessStateController.addExternalProviderClient(cpr, externalProcessToken,
                    callingUid, callingTag);
            return null;
        }

@@ -1470,7 +1472,7 @@ public class ContentProviderHelper {
        if (cpr.proc != null) {
            cpr.proc.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_PROVIDER);
        }
        pr.addProviderConnection(conn);
        mService.mProcessStateController.addProviderConnection(r, conn);
        mService.startAssociationLocked(r.uid, r.processName, r.mState.getCurProcState(),
                cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
        if (updateLru && cpr.proc != null
@@ -1493,7 +1495,8 @@ public class ContentProviderHelper {
            ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable,
            boolean enforceDelay, boolean updateOomAdj) {
        if (conn == null) {
            cpr.removeExternalProcessHandleLocked(externalProcessToken);
            mService.mProcessStateController.removeExternalProviderClient(cpr,
                    externalProcessToken);
            return false;
        }

@@ -1537,14 +1540,15 @@ public class ContentProviderHelper {
            if (cpr.proc != null && !hasProviderConnectionLocked(cpr.proc)) {
                cpr.proc.mProfile.clearHostingComponentType(HOSTING_COMPONENT_TYPE_PROVIDER);
            }
            conn.client.mProviders.removeProviderConnection(conn);
            mService.mProcessStateController.removeProviderConnection(conn.client, conn);
            if (conn.client.mState.getSetProcState()
                    < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
                // The client is more important than last activity -- note the time this
                // is happening, so we keep the old provider process around a bit as last
                // activity to avoid thrashing it.
                if (cpr.proc != null) {
                    cpr.proc.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
                    mService.mProcessStateController.setLastProviderTime(cpr.proc,
                            SystemClock.uptimeMillis());
                }
            }
            mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
@@ -1821,7 +1825,7 @@ public class ContentProviderHelper {
                }
            }
            if (removed && cpr.proc != null) {
                cpr.proc.mProviders.removeProvider(cpr.info.name);
                mService.mProcessStateController.removePublishedProvider(cpr.proc, cpr.info.name);
            }
        }

+2 −1
Original line number Diff line number Diff line
@@ -54,8 +54,9 @@ per-file Broadcast* = file:/BROADCASTS_OWNERS
per-file *Permission* = patb@google.com
per-file *Package* = patb@google.com

# OOM Adjuster
# OOM Adjuster & ProcessStateController
per-file *Oom* = file:/OOM_ADJUSTER_OWNERS
per-file ProcessStateController.java = file:/OOM_ADJUSTER_OWNERS

# Miscellaneous
per-file SettingsToPropertiesMapper.java = omakoto@google.com, yamasani@google.com, dzshen@google.com, zhidou@google.com, tedbauer@google.com
Loading