diff options
author | Christopher Tate <ctate@google.com> | 2019-01-17 16:58:31 -0800 |
---|---|---|
committer | Christopher Tate <ctate@google.com> | 2019-01-28 12:32:41 -0800 |
commit | 2f558d2659cf1c13c1672f93e7dd420cb887e8d3 (patch) | |
tree | 114b85c0e66da27465cd4d4e013a2348d38f5e41 /tests/ActivityTests/src | |
parent | 97bd994bb02a52de40725c09c14f62f6ed1bfaeb (diff) |
Defer broadcasts to slow-handling apps
When an app takes a long time to handle broadcasts, we start deferring
further broadcasts to it to make sure that other broadcast traffic in
the system can continue to make progress. Global delivery order is
technically rearranged, but delivery order from the point of view of any
given app remains consistent with issuance order.
When alarm broadcasts are issued, we prioritize delivery of deferred
alarms to the alarm recipients (i.e. we suspend the deferral policy and
catch up as promptly as possible) in order to minimize wake time spent
waiting for the alarm broadcast to be delivered. Once an app with a
deferred broadcast backlog is no longer the target of an in-flight
alarm, we re-impose deferral policy on it.
This policy intentionally trades off increased broadcast delivery
latency to apps that take a "long" time to handle broadcasts, in
exchange for lowering delivery latency to all other apps in the system
that would previously have had to wait behind the slow app.
In addition, broadcast dispatch policy parameters can now be overlaid
via the usual global Settings mechanism. In particular, configuring the
"bcast_slow_time" parameter to a value in milliseconds higher than the
queue's broadcast timeout period will disable the new slow-receiver
policies.
Bug: 111404343
Test: device boots & runs
Test: tests/ActivityTests
Change-Id: I76ac79bdf41ca3cfcc48515bca779ea0f5744c0b
Diffstat (limited to 'tests/ActivityTests/src')
-rw-r--r-- | tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java | 100 | ||||
-rw-r--r-- | tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java | 47 |
2 files changed, 133 insertions, 14 deletions
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index 0f4960887a33..2581e083cc64 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -16,21 +16,21 @@ package com.google.android.test.activity; -import java.util.ArrayList; -import java.util.List; - import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.AlarmManager; import android.app.AlertDialog; import android.app.PendingIntent; -import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentProviderClient; +import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.UserInfo; +import android.content.res.Configuration; +import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; @@ -42,21 +42,18 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.graphics.Bitmap; import android.provider.Settings; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.ScrollView; -import android.widget.Toast; +import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.content.Context; -import android.content.pm.UserInfo; -import android.content.res.Configuration; -import android.util.Log; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; +import java.util.ArrayList; +import java.util.List; public class ActivityTestMain extends Activity { static final String TAG = "ActivityTest"; @@ -73,8 +70,13 @@ public class ActivityTestMain extends Activity { ServiceConnection mIsolatedConnection; + static final String SLOW_RECEIVER_ACTION = "com.google.android.test.activity.SLOW_ACTION"; + static final String SLOW_RECEIVER_EXTRA = "slow_ordinal"; + static final int MSG_SPAM = 1; static final int MSG_SPAM_ALARM = 2; + static final int MSG_SLOW_RECEIVER = 3; + static final int MSG_SLOW_ALARM_RECEIVER = 4; final Handler mHandler = new Handler() { @Override @@ -100,11 +102,58 @@ public class ActivityTestMain extends Activity { mAlarm.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, when+(30*1000), pi); scheduleSpamAlarm(30*1000); } break; + case MSG_SLOW_RECEIVER: { + // Several back to back, to illustrate dispatch policy + Intent intent = new Intent(ActivityTestMain.this, SlowReceiver.class); + intent.setAction(SLOW_RECEIVER_ACTION); + intent.putExtra(SLOW_RECEIVER_EXTRA, 1); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + intent.putExtra(SLOW_RECEIVER_EXTRA, 2); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + intent.putExtra(SLOW_RECEIVER_EXTRA, 3); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + } break; + case MSG_SLOW_ALARM_RECEIVER: { + // Several back to back, to illustrate dispatch policy + Intent intent = new Intent(ActivityTestMain.this, SlowReceiver.class); + intent.setAction(SLOW_RECEIVER_ACTION); + intent.putExtra(SLOW_RECEIVER_EXTRA, 1); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + intent.putExtra(SLOW_RECEIVER_EXTRA, 2); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + intent.putExtra(SLOW_RECEIVER_EXTRA, 3); + sendOrderedBroadcast(intent, null, mSlowReceiverCompletion, mHandler, + Activity.RESULT_OK, null, null); + + // Also send a broadcast alarm to evaluate the alarm fast-forward policy + intent.putExtra(SLOW_RECEIVER_EXTRA, 4); + PendingIntent pi = PendingIntent.getBroadcast(ActivityTestMain.this, 1, intent, 0); + AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + long now = SystemClock.elapsedRealtime(); + Log.i(TAG, "Setting alarm for now + 5 seconds"); + am.setExact(AlarmManager.ELAPSED_REALTIME, now + 5_000, pi); + } break; } super.handleMessage(msg); } }; + final BroadcastReceiver mSlowReceiverCompletion = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int extra = intent.getIntExtra(SLOW_RECEIVER_EXTRA, -1); + final String msg = "Slow receiver " + extra + " completed"; + Toast.makeText(ActivityTestMain.this, msg, Toast.LENGTH_LONG) + .show(); + Log.i(TAG, msg); + } + }; + class BroadcastResultReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -387,6 +436,18 @@ public class ActivityTestMain extends Activity { return true; } }); + menu.add("Slow receiver").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + scheduleSlowReceiver(); + return true; + } + }); + menu.add("Slow alarm receiver").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + scheduleSlowAlarmReceiver(); + return true; + } + }); menu.add("Spam!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { scheduleSpam(false); @@ -469,6 +530,7 @@ public class ActivityTestMain extends Activity { protected void onStop() { super.onStop(); mHandler.removeMessages(MSG_SPAM_ALARM); + mHandler.removeMessages(MSG_SLOW_RECEIVER); for (ServiceConnection conn : mConnections) { unbindService(conn); } @@ -544,6 +606,16 @@ public class ActivityTestMain extends Activity { mHandler.sendMessageDelayed(msg, delay); } + void scheduleSlowReceiver() { + mHandler.removeMessages(MSG_SLOW_RECEIVER); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SLOW_RECEIVER), 500); + } + + void scheduleSlowAlarmReceiver() { + mHandler.removeMessages(MSG_SLOW_ALARM_RECEIVER); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SLOW_ALARM_RECEIVER), 500); + } + private View scrollWrap(View view) { ScrollView scroller = new ScrollView(this); scroller.addView(view, new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java b/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java new file mode 100644 index 000000000000..0437a289741c --- /dev/null +++ b/tests/ActivityTests/src/com/google/android/test/activity/SlowReceiver.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 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.google.android.test.activity; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.SystemClock; +import android.util.Log; + +public class SlowReceiver extends BroadcastReceiver { + private static final String TAG = "SlowReceiver"; + private static final long RECEIVER_DELAY = 6_000; + + @Override + public void onReceive(Context context, Intent intent) { + final int extra = intent.getIntExtra(ActivityTestMain.SLOW_RECEIVER_EXTRA, -1); + if (extra == 1) { + Log.i(TAG, "Received broadcast 1; delaying return by " + RECEIVER_DELAY + " ms"); + long now = SystemClock.elapsedRealtime(); + final long end = now + RECEIVER_DELAY; + while (now < end) { + try { + Thread.sleep(end - now); + } catch (InterruptedException e) { } + now = SystemClock.elapsedRealtime(); + } + } else { + Log.i(TAG, "Extra parameter not 1, returning immediately"); + } + Log.i(TAG, "Returning from onReceive()"); + } +} |