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

Commit 3faa46a1 authored by Jing Ji's avatar Jing Ji Committed by Android (Google) Code Review
Browse files

Merge "Fix potential negative duration in the ServiceState tracker" into sc-dev

parents 7eef5e4c 03a99372
Loading
Loading
Loading
Loading
+5 −7
Original line number Diff line number Diff line
@@ -1841,7 +1841,7 @@ public final class ActiveServices {
                    ServiceState stracker = r.getTracker();
                    if (stracker != null) {
                        stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
                                r.lastActivity);
                                SystemClock.uptimeMillis());
                    }
                }
                if (alreadyStartedOp) {
@@ -1863,7 +1863,7 @@ public final class ActiveServices {
                ServiceState stracker = r.getTracker();
                if (stracker != null) {
                    stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
                            r.lastActivity);
                            SystemClock.uptimeMillis());
                }
                mAm.mAppOpsService.finishOperation(
                        AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3765,6 +3765,7 @@ public final class ActiveServices {
            }
        }

        final long now = SystemClock.uptimeMillis();
        // Check to see if the service had been started as foreground, but being
        // brought down before actually showing a notification.  That is not allowed.
        if (r.fgRequired) {
@@ -3774,8 +3775,7 @@ public final class ActiveServices {
            r.fgWaiting = false;
            ServiceState stracker = r.getTracker();
            if (stracker != null) {
                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
                        r.lastActivity);
                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
            }
            mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
@@ -3834,8 +3834,7 @@ public final class ActiveServices {
            decActiveForegroundAppLocked(smap, r);
            ServiceState stracker = r.getTracker();
            if (stracker != null) {
                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
                        r.lastActivity);
                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
            }
            mAm.mAppOpsService.finishOperation(
                    AppOpsManager.getToken(mAm.mAppOpsService),
@@ -3902,7 +3901,6 @@ public final class ActiveServices {
        }

        int memFactor = mAm.mProcessStats.getMemFactorLocked();
        long now = SystemClock.uptimeMillis();
        if (r.tracker != null) {
            r.tracker.setStarted(false, memFactor, now);
            r.tracker.setBound(false, memFactor, now);
+200 −0
Original line number Diff line number Diff line
@@ -19,23 +19,37 @@ package com.android.server.am;
import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.app.ActivityManager;
import android.app.ActivityManager.OnUidImportanceListener;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiDevice;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
import android.util.Log;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@@ -65,6 +79,12 @@ public class ActivityManagerTest {
    private static final long AWAIT_TIMEOUT = 2000;
    private static final long CHECK_INTERVAL = 100;

    private static final String TEST_FGS_CLASS =
            "com.android.servicestests.apps.simpleservicetestapp.SimpleFgService";
    private static final String ACTION_FGS_STATS_TEST =
            "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
    private static final String EXTRA_MESSENGER = "extra_messenger";

    private IActivityManager mService;
    private IRemoteCallback mCallback;
    private Context mContext;
@@ -204,4 +224,184 @@ public class ActivityManagerTest {
        public void onServiceDisconnected(ComponentName name) {
        }
    }

    /**
     * Note: This test actually only works in eng build. It'll always pass
     * in user and userdebug build, because the expected exception won't be
     * thrown in those builds.
     */
    @LargeTest
    @Test
    public void testFgsProcStatsTracker() throws Exception {
        final PackageManager pm = mContext.getPackageManager();
        final long timeout = 5000;
        int uid = pm.getPackageUid(TEST_APP, 0);
        final MyUidImportanceListener uidListener1 = new MyUidImportanceListener(uid);
        final MyUidImportanceListener uidListener2 = new MyUidImportanceListener(uid);
        final ActivityManager am = mContext.getSystemService(ActivityManager.class);
        final CountDownLatch[] latchHolder = new CountDownLatch[1];
        final H handler = new H(Looper.getMainLooper(), latchHolder);
        final Messenger messenger = new Messenger(handler);
        final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
        final CountDownLatch dboxLatch = new CountDownLatch(1);
        final BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final String tag_wtf = "system_server_wtf";
                if (tag_wtf.equals(intent.getStringExtra(DropBoxManager.EXTRA_TAG))) {
                    final DropBoxManager.Entry e = dbox.getNextEntry(tag_wtf, intent.getLongExtra(
                            DropBoxManager.EXTRA_TIME, 0) - 1);
                    final String text = e.getText(8192);
                    if (TextUtils.isEmpty(text)) {
                        return;
                    }
                    if (text.indexOf("can't store negative values") == -1) {
                        return;
                    }
                    dboxLatch.countDown();
                }
            }
        };
        try {
            mContext.registerReceiver(receiver,
                    new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
            am.addOnUidImportanceListener(uidListener1,
                    RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
            am.addOnUidImportanceListener(uidListener2, RunningAppProcessInfo.IMPORTANCE_GONE);
            runShellCommand("cmd deviceidle whitelist +" + TEST_APP);
            toggleScreenOn(true);

            final Intent intent = new Intent(ACTION_FGS_STATS_TEST);
            final ComponentName cn = ComponentName.unflattenFromString(
                    TEST_APP + "/" + TEST_FGS_CLASS);
            final Bundle bundle = new Bundle();
            intent.setComponent(cn);
            bundle.putBinder(EXTRA_MESSENGER, messenger.getBinder());
            intent.putExtras(bundle);

            latchHolder[0] = new CountDownLatch(1);
            mContext.startForegroundService(intent);
            assertTrue("Timed out to start fg service", uidListener1.waitFor(
                    RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, timeout));
            assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
                    timeout, TimeUnit.MILLISECONDS));

            Thread.sleep(timeout);
            latchHolder[0] = new CountDownLatch(1);
            handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
            assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
                    timeout, TimeUnit.MILLISECONDS));

            Thread.sleep(timeout);
            latchHolder[0] = new CountDownLatch(1);
            handler.sendRemoteMessage(H.MSG_START_FOREGROUND, 0, 0, null);
            assertTrue("Timed out to wait for start fg", latchHolder[0].await(
                    timeout, TimeUnit.MILLISECONDS));

            toggleScreenOn(false);
            latchHolder[0] = new CountDownLatch(1);
            handler.sendRemoteMessage(H.MSG_STOP_FOREGROUND, 0, 0, null);
            assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
                    timeout, TimeUnit.MILLISECONDS));
            assertFalse("There shouldn't be negative values", dboxLatch.await(
                    timeout * 2, TimeUnit.MILLISECONDS));
        } finally {
            toggleScreenOn(true);
            runShellCommand("cmd deviceidle whitelist -" + TEST_APP);
            am.removeOnUidImportanceListener(uidListener1);
            am.removeOnUidImportanceListener(uidListener2);
            am.forceStopPackage(TEST_APP);
            mContext.unregisterReceiver(receiver);
        }
    }

    /**
     * Make sure the screen state.
     */
    private void toggleScreenOn(final boolean screenon) throws Exception {
        if (screenon) {
            runShellCommand("input keyevent KEYCODE_WAKEUP");
            runShellCommand("wm dismiss-keyguard");
        } else {
            runShellCommand("input keyevent KEYCODE_SLEEP");
        }
        // Since the screen on/off intent is ordered, they will not be sent right now.
        Thread.sleep(2_000);
    }

    private class H extends Handler {
        static final int MSG_INIT = 0;
        static final int MSG_DONE = 1;
        static final int MSG_START_FOREGROUND = 2;
        static final int MSG_STOP_FOREGROUND = 3;

        private Messenger mRemoteMessenger;
        private CountDownLatch[] mLatchHolder;

        H(Looper looper, CountDownLatch[] latchHolder) {
            super(looper);
            mLatchHolder = latchHolder;
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_INIT:
                    mRemoteMessenger = (Messenger) msg.obj;
                    mLatchHolder[0].countDown();
                    break;
                case MSG_DONE:
                    mLatchHolder[0].countDown();
                    break;
            }
        }

        void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
            Message msg = Message.obtain();
            msg.what = what;
            msg.arg1 = arg1;
            msg.arg2 = arg2;
            msg.obj = obj;
            try {
                mRemoteMessenger.send(msg);
            } catch (RemoteException e) {
            }
            msg.recycle();
        }
    }

    private static class MyUidImportanceListener implements OnUidImportanceListener {
        final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
        private final int mExpectedUid;
        private int mExpectedImportance;
        private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;

        MyUidImportanceListener(int uid) {
            mExpectedUid = uid;
        }

        @Override
        public void onUidImportance(int uid, int importance) {
            if (uid == mExpectedUid) {
                synchronized (this) {
                    if (importance == mExpectedImportance && mLatchHolder[0] != null) {
                        mLatchHolder[0].countDown();
                    }
                    mCurrentImportance = importance;
                }
                Log.i(TAG, "uid " + uid + " importance: " + importance);
            }
        }

        boolean waitFor(int expectedImportance, long timeout) throws Exception {
            synchronized (this) {
                mExpectedImportance = expectedImportance;
                if (mCurrentImportance == expectedImportance) {
                    return true;
                }
                mLatchHolder[0] = new CountDownLatch(1);
            }
            return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
        }
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -17,9 +17,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.servicestests.apps.simpleservicetestapp">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application>
        <service android:name=".SimpleService"
                 android:exported="true" />
        <service android:name=".SimpleFgService"
                 android:exported="true" />
    </application>

</manifest>
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.servicestests.apps.simpleservicetestapp;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.R;

public class SimpleFgService extends Service {
    private static final String TAG = SimpleFgService.class.getSimpleName();
    private static final String NOTIFICATION_CHANNEL_ID = TAG;
    private static final int NOTIFICATION_ID = 1;

    private static final int MSG_INIT = 0;
    private static final int MSG_DONE = 1;
    private static final int MSG_START_FOREGROUND = 2;
    private static final int MSG_STOP_FOREGROUND = 3;

    private static final String ACTION_FGS_STATS_TEST =
            "com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
    private static final String EXTRA_MESSENGER = "extra_messenger";

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_START_FOREGROUND: {
                    Log.i(TAG, "startForeground");
                    startForeground(NOTIFICATION_ID, mNotification);
                    sendRemoteMessage(MSG_DONE, 0, 0, null);
                } break;
                case MSG_STOP_FOREGROUND: {
                    Log.i(TAG, "stopForeground");
                    stopForeground(true);
                    sendRemoteMessage(MSG_DONE, 0, 0, null);
                } break;
            }
        }
    };
    private final Messenger mMessenger = new Messenger(mHandler);

    private Notification mNotification;
    private Messenger mRemoteMessenger;

    @Override
    public void onCreate() {
        Log.i(TAG, "onCreate");
        final NotificationManager nm = getSystemService(NotificationManager.class);
        nm.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
                NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW));
        mNotification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setContentTitle(TAG)
                .setSmallIcon(R.drawable.ic_info)
                .build();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand");
        startForeground(NOTIFICATION_ID, mNotification);
        if (ACTION_FGS_STATS_TEST.equals(intent.getAction())) {
            mRemoteMessenger = new Messenger(intent.getExtras().getBinder(EXTRA_MESSENGER));
            sendRemoteMessage(MSG_INIT, 0, 0, mMessenger);
        }
        return START_NOT_STICKY;
    }

    private void sendRemoteMessage(int what, int arg1, int arg2, Object obj) {
        final Message msg = Message.obtain();
        msg.what = what;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        msg.obj = obj;
        try {
            mRemoteMessenger.send(msg);
        } catch (RemoteException e) {
        }
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy");
        mNotification = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}