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

This commit is contained in:
Jing Ji
2021-03-13 23:35:06 +00:00
committed by Android (Google) Code Review
4 changed files with 322 additions and 7 deletions

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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>

View File

@@ -0,0 +1,113 @@
/*
* 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;
}
}