Merge "Added subscription to binder death which removes dead callbacks"
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index d5c386f..a652055 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -424,7 +424,7 @@
final int userId = createManagedProfile();
// Start the profile initially, then stop it. Similar to setQuietModeEnabled.
startUserInBackgroundAndWaitForUnlock(userId);
- stopUser(userId, true);
+ stopUserAfterWaitingForBroadcastIdle(userId, true);
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -479,7 +479,7 @@
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
- stopUser(userId, true);
+ stopUserAfterWaitingForBroadcastIdle(userId, true);
SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -676,6 +676,19 @@
return success[0];
}
+ /**
+ * Waits for broadcast idle before stopping a user, to prevent timeouts on stop user.
+ * Stopping a user heavily depends on broadcast queue, and that gets crowded after user creation
+ * or user switches, which leads to a timeout on stopping user and cause the tests to be flaky.
+ * Do not call this method while timing is on. i.e. between mRunner.resumeTiming() and
+ * mRunner.pauseTiming(). Otherwise it would cause the test results to be spiky.
+ */
+ private void stopUserAfterWaitingForBroadcastIdle(int userId, boolean force)
+ throws RemoteException {
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle");
+ stopUser(userId, force);
+ }
+
private void stopUser(int userId, boolean force) throws RemoteException {
final CountDownLatch latch = new CountDownLatch(1);
mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
@@ -711,7 +724,7 @@
attestTrue("Didn't switch back to user, " + origUser, origUser == mAm.getCurrentUser());
if (stopNewUser) {
- stopUser(testUser, true);
+ stopUserAfterWaitingForBroadcastIdle(testUser, true);
attestFalse("Failed to stop user " + testUser, mAm.isUserRunning(testUser));
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 795c430..317f8eb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -32264,6 +32264,7 @@
method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.QUERY_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.content.pm.UserProperties getUserProperties(@NonNull android.os.UserHandle);
method public android.os.Bundle getUserRestrictions();
method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+ method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}) public java.util.List<android.os.UserHandle> getVisibleUsers();
method public boolean hasUserRestriction(String);
method public boolean isDemoUser();
method public static boolean isHeadlessSystemUserMode();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a0cc4f0..2b2f202 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -320,6 +320,7 @@
field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND";
field public static final String START_CROSS_PROFILE_ACTIVITIES = "android.permission.START_CROSS_PROFILE_ACTIVITIES";
field public static final String START_REVIEW_PERMISSION_DECISIONS = "android.permission.START_REVIEW_PERMISSION_DECISIONS";
+ field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
@@ -507,7 +508,7 @@
public class ActivityOptions {
method public int getLaunchTaskId();
- method @RequiresPermission("android.permission.START_TASKS_FROM_RECENTS") public void setLaunchTaskId(int);
+ method @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public void setLaunchTaskId(int);
}
public class AlarmManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fefdfd8..f45298a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -42,7 +42,6 @@
field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
field public static final String SET_GAME_SERVICE = "android.permission.SET_GAME_SERVICE";
field public static final String SET_KEYBOARD_LAYOUT = "android.permission.SET_KEYBOARD_LAYOUT";
- field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
@@ -3204,6 +3203,11 @@
method public android.widget.ListView getMenuListView();
}
+ public class RatingBar extends android.widget.AbsSeekBar {
+ field public static final String PLURALS_MAX = "max";
+ field public static final String PLURALS_RATING = "rating";
+ }
+
public class Spinner extends android.widget.AbsSpinner implements android.content.DialogInterface.OnClickListener {
method public boolean isPopupShowing();
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d6b90a2..2a5916d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1906,7 +1906,6 @@
OP_SCHEDULE_EXACT_ALARM,
OP_MANAGE_MEDIA,
OP_TURN_SCREEN_ON,
- OP_GET_USAGE_STATS,
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 8ffc6a2..c1a3c19 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -1008,6 +1008,22 @@
}
}
+ /**
+ * Checks whether the calling companion application is currently bound.
+ *
+ * @return true if application is bound, false otherwise
+ * @hide
+ */
+ @UserHandleAware
+ public boolean isCompanionApplicationBound() {
+ try {
+ return mService.isCompanionApplicationBound(
+ mContext.getOpPackageName(), mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private boolean checkFeaturePresent() {
boolean featurePresent = mService != null;
if (!featurePresent && DEBUG) {
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 6e6e187..17e3132 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -80,4 +80,6 @@
void attachSystemDataTransport(String packageName, int userId, int associationId, in ParcelFileDescriptor fd);
void detachSystemDataTransport(String packageName, int userId, int associationId);
+
+ boolean isCompanionApplicationBound(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5839b87..8e2a5ea 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8065,7 +8065,7 @@
}
PackageParser.Package pkg = parser.parsePackage(apkFile, 0, false);
- if ((flagsBits & GET_SIGNATURES) != 0) {
+ if ((flagsBits & GET_SIGNATURES) != 0 || (flagsBits & GET_SIGNING_CERTIFICATES) != 0) {
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
return PackageParser.generatePackageInfo(pkg, null, (int) flagsBits, 0, 0, null,
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index fa6b118..88649cb 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -129,6 +129,7 @@
boolean isUserRunning(int userId);
boolean isUserForeground(int userId);
boolean isUserVisible(int userId);
+ List<UserHandle> getVisibleUsers();
boolean isUserNameSet(int userId);
boolean hasRestrictedProfiles(int userId);
boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 738f9c9..f6aaee8 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2886,6 +2886,21 @@
}
/**
+ * Gets the visible users (as defined by {@link #isUserVisible()}.
+ *
+ * @return visible users at the moment.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS})
+ public @NonNull List<UserHandle> getVisibleUsers() {
+ try {
+ return mService.getVisibleUsers();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return whether the context user is running in an "unlocked" state.
* <p>
* On devices with direct boot, a user is unlocked only after they've
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index fa84407..7314ad8 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -732,6 +732,10 @@
* @hide Pending API consideration. Currently only used internally by the system.
*/
protected boolean hasDividerBeforeChildAt(int childIndex) {
+ if (mShowDividers == SHOW_DIVIDER_NONE) {
+ // Short-circuit to save iteration over child views.
+ return false;
+ }
if (childIndex == getVirtualChildCount()) {
// Check whether the end divider should draw.
return (mShowDividers & SHOW_DIVIDER_END) != 0;
@@ -746,6 +750,24 @@
}
/**
+ * Determines whether or not there's a divider after a specified child index.
+ *
+ * @param childIndex Index of child to check for following divider
+ * @return true if there should be a divider after the child at childIndex
+ */
+ private boolean hasDividerAfterChildAt(int childIndex) {
+ if (mShowDividers == SHOW_DIVIDER_NONE) {
+ // Short-circuit to save iteration over child views.
+ return false;
+ }
+ if (allViewsAreGoneAfter(childIndex)) {
+ // This is the last view that's not gone, check if end divider is enabled.
+ return (mShowDividers & SHOW_DIVIDER_END) != 0;
+ }
+ return (mShowDividers & SHOW_DIVIDER_MIDDLE) != 0;
+ }
+
+ /**
* Checks whether all (virtual) child views before the given index are gone.
*/
private boolean allViewsAreGoneBefore(int childIndex) {
@@ -759,6 +781,20 @@
}
/**
+ * Checks whether all (virtual) child views after the given index are gone.
+ */
+ private boolean allViewsAreGoneAfter(int childIndex) {
+ final int count = getVirtualChildCount();
+ for (int i = childIndex + 1; i < count; i++) {
+ final View child = getVirtualChildAt(i);
+ if (child != null && child.getVisibility() != GONE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Measures the children when the orientation of this LinearLayout is set
* to {@link #VERTICAL}.
*
@@ -1295,6 +1331,7 @@
if (useLargestChild &&
(widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
+ nonSkippedChildCount = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -1308,6 +1345,11 @@
continue;
}
+ nonSkippedChildCount++;
+ if (hasDividerBeforeChildAt(i)) {
+ mTotalLength += mDividerWidth;
+ }
+
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
if (isExactly) {
@@ -1319,6 +1361,10 @@
lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
}
}
+
+ if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
+ mTotalLength += mDividerWidth;
+ }
}
// Add in our padding
@@ -1347,6 +1393,7 @@
maxHeight = -1;
mTotalLength = 0;
+ nonSkippedChildCount = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -1354,6 +1401,11 @@
continue;
}
+ nonSkippedChildCount++;
+ if (hasDividerBeforeChildAt(i)) {
+ mTotalLength += mDividerWidth;
+ }
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final float childWeight = lp.weight;
if (childWeight > 0) {
@@ -1423,6 +1475,10 @@
}
}
+ if (nonSkippedChildCount > 0 && hasDividerBeforeChildAt(count)) {
+ mTotalLength += mDividerWidth;
+ }
+
// Add in our padding
mTotalLength += mPaddingLeft + mPaddingRight;
// TODO: Should we update widthSize with the new total length?
@@ -1810,7 +1866,13 @@
break;
}
- if (hasDividerBeforeChildAt(childIndex)) {
+ if (isLayoutRtl) {
+ // Because rtl rendering occurs in the reverse direction, we need to check
+ // after the child rather than before (since after=left in this context)
+ if (hasDividerAfterChildAt(childIndex)) {
+ childLeft += mDividerWidth;
+ }
+ } else if (hasDividerBeforeChildAt(childIndex)) {
childLeft += mDividerWidth;
}
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index f946fe6..ec0d862 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -16,17 +16,22 @@
package android.widget;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.Shape;
import android.util.AttributeSet;
+import android.util.PluralsMessageFormatter;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inspector.InspectableProperty;
import com.android.internal.R;
+import java.util.HashMap;
+
+
/**
* A RatingBar is an extension of SeekBar and ProgressBar that shows a rating in
* stars. The user can touch/drag or use arrow keys to set the rating when using
@@ -53,6 +58,20 @@
public class RatingBar extends AbsSeekBar {
/**
+ * Key used for generating Text-to-Speech output regarding the current star rating.
+ * @hide
+ */
+ @TestApi
+ public static final String PLURALS_RATING = "rating";
+
+ /**
+ * Key used for generating Text-to-Speech output regarding the maximum star count.
+ * @hide
+ */
+ @TestApi
+ public static final String PLURALS_MAX = "max";
+
+ /**
* A callback that notifies clients when the rating has been changed. This
* includes changes that were initiated by the user through a touch gesture
* or arrow key/trackball as well as changes that were initiated
@@ -354,6 +373,16 @@
if (canUserSetProgress()) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS);
}
+
+ final float scaledMax = getMax() * getStepSize();
+ final HashMap<String, Object> params = new HashMap();
+ params.put(PLURALS_RATING, getRating());
+ params.put(PLURALS_MAX, scaledMax);
+ info.setStateDescription(PluralsMessageFormatter.format(
+ getContext().getResources(),
+ params,
+ R.string.rating_label
+ ));
}
@Override
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1c7c582..cd1c23c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4891,24 +4891,24 @@
*
* <p>Line-break style specifies the line-break strategies that can be used
* for text wrapping. The line-break style affects rule-based line breaking
- * by specifying the strictness of line-breaking rules.</p>
+ * by specifying the strictness of line-breaking rules.
*
- * <p>The following are types of line-break styles:</p>
+ * <p>The following are types of line-break styles:
* <ul>
- * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_LOOSE}</li>
- * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_NORMAL}</li>
- * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}</li>
+ * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_LOOSE}
+ * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_NORMAL}
+ * <li>{@link LineBreakConfig#LINE_BREAK_STYLE_STRICT}
* </ul>
*
* <p>The default line-break style is
* {@link LineBreakConfig#LINE_BREAK_STYLE_NONE}, which specifies that no
- * line-breaking rules are used.</p>
+ * line-breaking rules are used.
*
* <p>See the
* <a href="https://www.w3.org/TR/css-text-3/#line-break-property" class="external">
- * line-break property</a> for more information.</p>
+ * line-break property</a> for more information.
*
- * @param lineBreakStyle The line break style for the text.
+ * @param lineBreakStyle The line-break style for the text.
*/
public void setLineBreakStyle(@LineBreakConfig.LineBreakStyle int lineBreakStyle) {
if (mLineBreakStyle != lineBreakStyle) {
@@ -4927,17 +4927,17 @@
* <p>The line-break word style affects dictionary-based line breaking by
* providing phrase-based line-breaking opportunities. Use
* {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_PHRASE} to specify
- * phrase-based line breaking.</p>
+ * phrase-based line breaking.
*
* <p>The default line-break word style is
* {@link LineBreakConfig#LINE_BREAK_WORD_STYLE_NONE}, which specifies that
- * no line-breaking word style is used.</p>
+ * no line-breaking word style is used.
*
* <p>See the
* <a href="https://www.w3.org/TR/css-text-3/#word-break-property" class="external">
- * word-break property</a> for more information.</p>
+ * word-break property</a> for more information.
*
- * @param lineBreakWordStyle The line break word style for the text.
+ * @param lineBreakWordStyle The line-break word style for the text.
*/
public void setLineBreakWordStyle(@LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
mUserSpeficiedLineBreakwordStyle = true;
diff --git a/core/res/Android.bp b/core/res/Android.bp
index c42517d..93ce783 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -73,18 +73,18 @@
":remote-color-resources-compile-colors",
],
out: ["remote-color-resources.apk"],
- cmd: "$(location aapt2) link -o $(out) --manifest $(in)"
+ cmd: "$(location aapt2) link -o $(out) --manifest $(in)",
}
genrule {
name: "remote-color-resources-arsc",
srcs: [":remote-color-resources-apk"],
out: ["res/raw/remote_views_color_resources.arsc"],
- cmd: "mkdir -p $(genDir)/remote-color-resources-arsc && "
- + "unzip -x $(in) resources.arsc -d $(genDir)/remote-color-resources-arsc && "
- + "mkdir -p $$(dirname $(out)) && "
- + "mv $(genDir)/remote-color-resources-arsc/resources.arsc $(out) && "
- + "echo 'Created $(out)'"
+ cmd: "mkdir -p $(genDir)/remote-color-resources-arsc && " +
+ "unzip -x $(in) resources.arsc -d $(genDir)/remote-color-resources-arsc && " +
+ "mkdir -p $$(dirname $(out)) && " +
+ "mv $(genDir)/remote-color-resources-arsc/resources.arsc $(out) && " +
+ "echo 'Created $(out)'",
}
genrule {
@@ -95,11 +95,11 @@
"remote_color_resources_res/symbols.xml",
],
out: ["remote_views_color_resources.zip"],
- cmd: "INPUTS=($(in)) && "
- + "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && "
- + "mkdir -p $$RES_DIR/values && "
- + "cp $${INPUTS[1]} $$RES_DIR/values && "
- + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR"
+ cmd: "INPUTS=($(in)) && " +
+ "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && " +
+ "mkdir -p $$RES_DIR/values && " +
+ "cp $${INPUTS[1]} $$RES_DIR/values && " +
+ "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR",
}
android_app {
@@ -154,31 +154,21 @@
cmd: "cp $(in) $(out)",
}
-// This logic can be removed once robolectric's transition to binary resources is complete
-filegroup {
- name: "robolectric_framework_raw_res_files",
- srcs: [
- "assets/**/*",
- "res/**/*",
- ":remote-color-resources-arsc",
- ],
-}
-
// Generate a text file containing a list of permissions that non-system apps
// are allowed to obtain.
genrule {
- name: "permission-list-normal",
- out: ["permission-list-normal.txt"],
- srcs: ["AndroidManifest.xml"],
- cmd: "cat $(in) " +
- // xmllint has trouble accessing attributes under the android namespace.
- // Strip these prefixes prior to processing with xmllint.
- " | sed -r 's/android:(name|protectionLevel)/\\1/g' " +
- " | $(location xmllint) /dev/stdin --xpath " +
- " '//permission[not(contains(@protectionLevel, \"signature\"))]/@name'" +
- // The result of xmllint is name="value" pairs. Format these to just the
- // permission name, one per-line.
- " | sed -r 's/\\s*name=\\s*//g' | tr -d '\"'" +
- " > $(out)",
- tools: ["xmllint"]
+ name: "permission-list-normal",
+ out: ["permission-list-normal.txt"],
+ srcs: ["AndroidManifest.xml"],
+ cmd: "cat $(in) " +
+ // xmllint has trouble accessing attributes under the android namespace.
+ // Strip these prefixes prior to processing with xmllint.
+ " | sed -r 's/android:(name|protectionLevel)/\\1/g' " +
+ " | $(location xmllint) /dev/stdin --xpath " +
+ " '//permission[not(contains(@protectionLevel, \"signature\"))]/@name'" +
+ // The result of xmllint is name="value" pairs. Format these to just the
+ // permission name, one per-line.
+ " | sed -r 's/\\s*name=\\s*//g' | tr -d '\"'" +
+ " > $(out)",
+ tools: ["xmllint"],
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3fbd797..cd518ce 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2975,7 +2975,7 @@
<permission android:name="android.permission.REAL_GET_TASKS"
android:protectionLevel="signature|privileged" />
- <!-- @TestApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+ <!-- @SystemApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
@hide -->
<permission android:name="android.permission.START_TASKS_FROM_RECENTS"
android:protectionLevel="signature|privileged|recents" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 11a3361..e5b1cf9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3365,6 +3365,13 @@
<!-- Default not selected text used by accessibility for an element that can be selected or unselected. [CHAR LIMIT=NONE] -->
<string name="not_selected">not selected</string>
+ <!-- Default label text used by accessibility for ratingbars. [CHAR LIMIT=NONE] -->
+ <string name ="rating_label">{ rating, plural,
+ =1 {One star out of {max}}
+ other {# stars out of {max}}
+ }
+ </string>
+
<!-- Default state description for indeterminate progressbar. [CHAR LIMIT=NONE] -->
<string name="in_progress">in progress</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3afa6ef..d7836c1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -925,6 +925,7 @@
<java-symbol type="string" name="mobile_provisioning_apn" />
<java-symbol type="string" name="mobile_provisioning_url" />
<java-symbol type="string" name="quick_contacts_not_available" />
+ <java-symbol type="string" name="rating_label" />
<java-symbol type="string" name="reboot_to_update_package" />
<java-symbol type="string" name="reboot_to_update_prepare" />
<java-symbol type="string" name="reboot_to_update_title" />
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index 48aecd6..d0327159 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -28,7 +28,7 @@
*
* <p>See the
* <a href="https://www.w3.org/TR/css-text-3/#line-break-property" class="external">
- * line-break property</a> for more information.</p>
+ * line-break property</a> for more information.
*/
public final class LineBreakConfig {
@@ -72,7 +72,7 @@
*
* <p>Support for this line-break word style depends on locale. If the
* current locale does not support phrase-based text wrapping, this setting
- * has no effect.</p>
+ * has no effect.
*/
public static final int LINE_BREAK_WORD_STYLE_PHRASE = 1;
@@ -194,7 +194,7 @@
* Constructor with line-break parameters.
*
* <p>Use {@link LineBreakConfig.Builder} to create the
- * {@code LineBreakConfig} instance.</p>
+ * {@code LineBreakConfig} instance.
*/
private LineBreakConfig(@LineBreakStyle int lineBreakStyle,
@LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 677f0a2..29d6765 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -155,10 +155,10 @@
new ReleasedFlag(603, false);
public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE_BACKEND =
- new UnreleasedFlag(604, true);
+ new UnreleasedFlag(604, false);
public static final UnreleasedFlag NEW_STATUS_BAR_PIPELINE_FRONTEND =
- new UnreleasedFlag(605, true);
+ new UnreleasedFlag(605, false);
/***************************************/
// 700 - dialer/calls
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 8b37aab..35f32ca 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -53,8 +53,7 @@
import android.view.WindowManager;
import android.widget.Toast;
-import androidx.annotation.NonNull;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.R;
@@ -137,7 +136,7 @@
}
@Override
- public IBinder onBind(@NonNull Intent intent) {
+ public IBinder onBind(Intent intent) {
registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS),
Context.RECEIVER_EXPORTED);
final Messenger m = new Messenger(mHandler);
@@ -184,13 +183,23 @@
}
}
- /** Respond to incoming Message via Binder (Messenger) */
@MainThread
private boolean handleMessage(Message msg) {
final Messenger replyTo = msg.replyTo;
- final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri);
- RequestCallback requestCallback = new RequestCallbackImpl(replyTo);
+ final Consumer<Uri> onSaved = (uri) -> reportUri(replyTo, uri);
+ RequestCallback callback = new RequestCallbackImpl(replyTo);
+ ScreenshotHelper.ScreenshotRequest request =
+ (ScreenshotHelper.ScreenshotRequest) msg.obj;
+
+ handleRequest(request, onSaved, callback);
+ return true;
+ }
+
+ @MainThread
+ @VisibleForTesting
+ void handleRequest(ScreenshotHelper.ScreenshotRequest request, Consumer<Uri> onSaved,
+ RequestCallback callback) {
// If the storage for this user is locked, we have no place to store
// the screenshot, so skip taking it instead of showing a misleading
// animation and error notification.
@@ -198,8 +207,8 @@
Log.w(TAG, "Skipping screenshot because storage is locked!");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_save_user_locked_text);
- requestCallback.reportError();
- return true;
+ callback.reportError();
+ return;
}
if (mDevicePolicyManager.getScreenCaptureDisabled(null, UserHandle.USER_ALL)) {
@@ -211,33 +220,26 @@
() -> mContext.getString(R.string.screenshot_blocked_by_admin));
mHandler.post(() ->
Toast.makeText(mContext, blockedByAdminText, Toast.LENGTH_SHORT).show());
- requestCallback.reportError();
+ callback.reportError();
});
- return true;
+ return;
}
- ScreenshotHelper.ScreenshotRequest screenshotRequest =
- (ScreenshotHelper.ScreenshotRequest) msg.obj;
-
- ComponentName topComponent = screenshotRequest.getTopComponent();
- mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
- topComponent == null ? "" : topComponent.getPackageName());
-
if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
Log.d(TAG, "handleMessage: Using request processor");
- mProcessor.processAsync(screenshotRequest,
- (request) -> dispatchToController(request, uriConsumer, requestCallback));
- return true;
+ mProcessor.processAsync(request,
+ (r) -> dispatchToController(r, onSaved, callback));
}
- dispatchToController(screenshotRequest, uriConsumer, requestCallback);
- return true;
+ dispatchToController(request, onSaved, callback);
}
private void dispatchToController(ScreenshotHelper.ScreenshotRequest request,
Consumer<Uri> uriConsumer, RequestCallback callback) {
ComponentName topComponent = request.getTopComponent();
+ mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(request.getSource()), 0,
+ topComponent == null ? "" : topComponent.getPackageName());
switch (request.getType()) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index 9e5dab1..f8449ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -90,6 +90,18 @@
stableIndex = -1
}
+ /**
+ * Erases bookkeeping traces stored on an entry when it is removed from the notif list.
+ * This can happen if the entry is removed from a group that was broken up or if the entry was
+ * filtered out during any of the filtering steps.
+ */
+ fun detach() {
+ parent = null
+ section = null
+ promoter = null
+ // stableIndex = -1 // TODO(b/241229236): Clear this once we fix the stability fragility
+ }
+
companion object {
@JvmStatic
fun create(): ListAttachState {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 14cc6bf..3eaa988 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -958,9 +958,7 @@
* filtered out during any of the filtering steps.
*/
private void annulAddition(ListEntry entry) {
- entry.setParent(null);
- entry.getAttachState().setSection(null);
- entry.getAttachState().setPromoter(null);
+ entry.getAttachState().detach();
}
private void assignSections() {
@@ -1198,9 +1196,9 @@
o2.getSectionIndex());
if (cmp != 0) return cmp;
- int index1 = canReorder(o1) ? -1 : o1.getPreviousAttachState().getStableIndex();
- int index2 = canReorder(o2) ? -1 : o2.getPreviousAttachState().getStableIndex();
- cmp = Integer.compare(index1, index2);
+ cmp = Integer.compare(
+ getStableOrderIndex(o1),
+ getStableOrderIndex(o2));
if (cmp != 0) return cmp;
NotifComparator sectionComparator = getSectionComparator(o1, o2);
@@ -1214,31 +1212,32 @@
if (cmp != 0) return cmp;
}
- final NotificationEntry rep1 = o1.getRepresentativeEntry();
- final NotificationEntry rep2 = o2.getRepresentativeEntry();
- cmp = rep1.getRanking().getRank() - rep2.getRanking().getRank();
+ cmp = Integer.compare(
+ o1.getRepresentativeEntry().getRanking().getRank(),
+ o2.getRepresentativeEntry().getRanking().getRank());
if (cmp != 0) return cmp;
- cmp = Long.compare(
- rep2.getSbn().getNotification().when,
- rep1.getSbn().getNotification().when);
+ cmp = -1 * Long.compare(
+ o1.getRepresentativeEntry().getSbn().getNotification().when,
+ o2.getRepresentativeEntry().getSbn().getNotification().when);
return cmp;
};
private final Comparator<NotificationEntry> mGroupChildrenComparator = (o1, o2) -> {
- int index1 = canReorder(o1) ? -1 : o1.getPreviousAttachState().getStableIndex();
- int index2 = canReorder(o2) ? -1 : o2.getPreviousAttachState().getStableIndex();
- int cmp = Integer.compare(index1, index2);
+ int cmp = Integer.compare(
+ getStableOrderIndex(o1),
+ getStableOrderIndex(o2));
if (cmp != 0) return cmp;
- cmp = o1.getRepresentativeEntry().getRanking().getRank()
- - o2.getRepresentativeEntry().getRanking().getRank();
+ cmp = Integer.compare(
+ o1.getRepresentativeEntry().getRanking().getRank(),
+ o2.getRepresentativeEntry().getRanking().getRank());
if (cmp != 0) return cmp;
- cmp = Long.compare(
- o2.getRepresentativeEntry().getSbn().getNotification().when,
- o1.getRepresentativeEntry().getSbn().getNotification().when);
+ cmp = -1 * Long.compare(
+ o1.getRepresentativeEntry().getSbn().getNotification().when,
+ o2.getRepresentativeEntry().getSbn().getNotification().when);
return cmp;
};
@@ -1248,8 +1247,16 @@
*/
private boolean mForceReorderable = false;
- private boolean canReorder(ListEntry entry) {
- return mForceReorderable || getStabilityManager().isEntryReorderingAllowed(entry);
+ private int getStableOrderIndex(ListEntry entry) {
+ if (mForceReorderable) {
+ // this is used to determine if the list is correctly sorted
+ return -1;
+ }
+ if (getStabilityManager().isEntryReorderingAllowed(entry)) {
+ // let the stability manager constrain or allow reordering
+ return -1;
+ }
+ return entry.getPreviousAttachState().getStableIndex();
}
private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java
deleted file mode 100644
index d731753..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.android.systemui.util;
-
-import static androidx.lifecycle.Lifecycle.State.DESTROYED;
-import static androidx.lifecycle.Lifecycle.State.RESUMED;
-
-import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
-
-/**
- * Tools for generating lifecycle from sysui objects.
- */
-public class SysuiLifecycle {
-
- private SysuiLifecycle() {
- }
-
- /**
- * Get a lifecycle that will be put into the resumed state when the view is attached
- * and goes to the destroyed state when the view is detached.
- */
- public static LifecycleOwner viewAttachLifecycle(View v) {
- return new ViewLifecycle(v);
- }
-
- private static class ViewLifecycle implements LifecycleOwner, OnAttachStateChangeListener {
- private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
-
- ViewLifecycle(View v) {
- v.addOnAttachStateChangeListener(this);
- if (v.isAttachedToWindow()) {
- mLifecycle.markState(RESUMED);
- }
- }
-
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycle;
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- mLifecycle.markState(RESUMED);
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- mLifecycle.markState(DESTROYED);
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
new file mode 100644
index 0000000..83e56da
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 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.systemui.screenshot
+
+import android.app.Application
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResources.Strings.SystemUi.SCREENSHOT_BLOCKED_BY_ADMIN
+import android.app.admin.DevicePolicyResourcesManager
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config.HARDWARE
+import android.graphics.ColorSpace
+import android.graphics.Insets
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.util.ScreenshotHelper
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_CHORD
+import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when` as whenever
+
+private const val USER_ID = 1
+private const val TASK_ID = 1
+
+@RunWith(AndroidTestingRunner::class)
+class TakeScreenshotServiceTest : SysuiTestCase() {
+
+ private val application = mock<Application>()
+ private val controller = mock<ScreenshotController>()
+ private val userManager = mock<UserManager>()
+ private val requestProcessor = mock<RequestProcessor>()
+ private val devicePolicyManager = mock<DevicePolicyManager>()
+ private val devicePolicyResourcesManager = mock<DevicePolicyResourcesManager>()
+ private val notificationsController = mock<ScreenshotNotificationsController>()
+ private val callback = mock<RequestCallback>()
+
+ private val eventLogger = UiEventLoggerFake()
+ private val flags = FakeFeatureFlags()
+ private val topComponent = ComponentName(mContext, TakeScreenshotServiceTest::class.java)
+
+ private val service = TakeScreenshotService(
+ controller, userManager, devicePolicyManager, eventLogger,
+ notificationsController, mContext, Runnable::run, flags, requestProcessor)
+
+ @Before
+ fun setUp() {
+ whenever(devicePolicyManager.resources).thenReturn(devicePolicyResourcesManager)
+ whenever(devicePolicyManager.getScreenCaptureDisabled(
+ /* admin component (null: any admin) */ isNull(), eq(UserHandle.USER_ALL)))
+ .thenReturn(false)
+ whenever(userManager.isUserUnlocked).thenReturn(true)
+
+ flags.set(SCREENSHOT_REQUEST_PROCESSOR, false)
+
+ service.attach(
+ mContext,
+ /* thread = */ null,
+ /* className = */ null,
+ /* token = */ null,
+ application,
+ /* activityManager = */ null)
+ }
+
+ @Test
+ fun testServiceLifecycle() {
+ service.onCreate()
+ service.onBind(null /* unused: Intent */)
+
+ service.onUnbind(null /* unused: Intent */)
+ verify(controller).removeWindow()
+
+ service.onDestroy()
+ verify(controller).onDestroy()
+ }
+
+ @Test
+ fun takeScreenshotFullscreen() {
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_FULLSCREEN,
+ SCREENSHOT_KEY_CHORD,
+ topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(controller).takeScreenshotFullscreen(
+ eq(topComponent),
+ /* onSavedListener = */ any(),
+ /* requestCallback = */ any())
+
+ assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+ val logEvent = eventLogger.get(0)
+
+ assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_KEY_CHORD.id)
+ assertEquals("Expected supplied package name",
+ topComponent.packageName, eventLogger.get(0).packageName)
+ }
+
+ @Test
+ fun takeScreenshotPartial() {
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_SELECTED_REGION,
+ SCREENSHOT_KEY_CHORD,
+ /* topComponent = */ null)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(controller).takeScreenshotPartial(
+ /* topComponent = */ isNull(),
+ /* onSavedListener = */ any(),
+ /* requestCallback = */ any())
+
+ assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+ val logEvent = eventLogger.get(0)
+
+ assertEquals("Expected SCREENSHOT_REQUESTED UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_KEY_CHORD.id)
+ assertEquals("Expected empty package name in UiEvent", "", eventLogger.get(0).packageName)
+ }
+
+ @Test
+ fun takeScreenshotProvidedImage() {
+ val bounds = Rect(50, 50, 150, 150)
+ val bitmap = makeHardwareBitmap(100, 100)
+ val bitmapBundle = ScreenshotHelper.HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
+
+ val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW,
+ bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(controller).handleImageAsScreenshot(
+ argThat { b -> b.equalsHardwareBitmap(bitmap) },
+ eq(bounds),
+ eq(Insets.NONE), eq(TASK_ID), eq(USER_ID), eq(topComponent),
+ /* onSavedListener = */ any(), /* requestCallback = */ any())
+
+ assertEquals("Expected one UiEvent", eventLogger.numLogs(), 1)
+ val logEvent = eventLogger.get(0)
+
+ assertEquals("Expected SCREENSHOT_REQUESTED_* UiEvent",
+ logEvent.eventId, SCREENSHOT_REQUESTED_OVERVIEW.id)
+ assertEquals("Expected supplied package name",
+ topComponent.packageName, eventLogger.get(0).packageName)
+ }
+
+ @Test
+ fun takeScreenshotFullscreen_userLocked() {
+ whenever(userManager.isUserUnlocked).thenReturn(false)
+
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_FULLSCREEN,
+ SCREENSHOT_KEY_CHORD,
+ topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ verify(notificationsController).notifyScreenshotError(anyInt())
+ verify(callback).reportError()
+ verifyZeroInteractions(controller)
+ }
+
+ @Test
+ fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers() {
+ whenever(devicePolicyManager.getScreenCaptureDisabled(
+ isNull(), eq(UserHandle.USER_ALL))
+ ).thenReturn(true)
+
+ whenever(devicePolicyResourcesManager.getString(
+ eq(SCREENSHOT_BLOCKED_BY_ADMIN),
+ /* Supplier<String> */ any(),
+ )).thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
+
+ val request = ScreenshotRequest(
+ TAKE_SCREENSHOT_FULLSCREEN,
+ SCREENSHOT_KEY_CHORD,
+ topComponent)
+
+ service.handleRequest(request, { /* onSaved */ }, callback)
+
+ // error shown: Toast.makeText(...).show(), untestable
+ verify(callback).reportError()
+ verifyZeroInteractions(controller)
+ }
+}
+
+private fun Bitmap.equalsHardwareBitmap(other: Bitmap): Boolean {
+ return config == HARDWARE &&
+ other.config == HARDWARE &&
+ hardwareBuffer == other.hardwareBuffer &&
+ colorSpace == other.colorSpace
+}
+
+/** A hardware Bitmap is mandated by use of ScreenshotHelper.HardwareBitmapBundler */
+private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
+ val buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888, 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+ return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
deleted file mode 100644
index 4f509ea..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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.android.systemui.util;
-
-import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
-import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
-import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
-import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
-import static androidx.lifecycle.Lifecycle.Event.ON_START;
-import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
-
-import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.testing.ViewUtils;
-import android.view.View;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleEventObserver;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SysuiLifecycleTest extends SysuiTestCase {
-
- private View mView;
-
- @Before
- public void setUp() {
- mView = new View(mContext);
- }
-
- @After
- public void tearDown() {
- if (mView.isAttachedToWindow()) {
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
- }
- }
-
- @Test
- public void testAttach() {
- LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- lifecycle.getLifecycle().addObserver(observer);
-
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
-
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_CREATE));
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_START));
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_RESUME));
- }
-
- @Test
- public void testDetach() {
- LifecycleEventObserver observer = mock(LifecycleEventObserver.class);
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- lifecycle.getLifecycle().addObserver(observer);
-
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
-
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
-
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_PAUSE));
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_STOP));
- verify(observer).onStateChanged(eq(lifecycle), eq(ON_DESTROY));
- }
-
- @Test
- public void testStateBeforeAttach() {
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // THEN the lifecycle state should be INITIAZED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(
- Lifecycle.State.INITIALIZED);
- }
-
- @Test
- public void testStateAfterAttach() {
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // AND the view is attached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- // THEN the lifecycle state should be RESUMED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED);
- }
-
- @Test
- public void testStateAfterDetach() {
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // AND the view is detached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
- // THEN the lifecycle state should be DESTROYED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.DESTROYED);
- }
-
- @Test
- public void testStateAfterReattach() {
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // AND the view is re-attached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- // THEN the lifecycle state should still be DESTROYED, err RESUMED?
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED);
- }
-
- @Test
- public void testStateWhenViewAlreadyAttached() {
- // GIVEN that a view is already attached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // THEN the lifecycle state should be RESUMED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED);
- }
-
- @Test
- public void testStateWhenViewAlreadyDetached() {
- // GIVEN that a view is already detached
- ViewUtils.attachView(mView);
- TestableLooper.get(this).processAllMessages();
- ViewUtils.detachView(mView);
- TestableLooper.get(this).processAllMessages();
- // WHEN a lifecycle is obtained from a view
- LifecycleOwner lifecycle = viewAttachLifecycle(mView);
- // THEN the lifecycle state should be INITIALIZED
- assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(
- Lifecycle.State.INITIALIZED);
- }
-}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index fa043f8..265fbc5 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,7 +17,6 @@
package com.android.server.companion;
-import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES;
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
@@ -88,7 +87,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
-import android.util.Base64;
import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
@@ -756,6 +754,11 @@
mDevicePresenceMonitor.onSelfManagedDeviceDisconnected(associationId);
}
+ @Override
+ public boolean isCompanionApplicationBound(String packageName, int userId) {
+ return mCompanionAppController.isCompanionApplicationBound(userId, packageName);
+ }
+
private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
boolean active) throws RemoteException {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 734f455..8521cea 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2401,13 +2401,13 @@
mEnableOffloadQueue = SystemProperties.getBoolean(
"persist.device_config.activity_manager_native_boot.offload_queue_enabled", true);
- mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mFgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"foreground", foreConstants, false);
- mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mBgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"background", backConstants, true);
- mBgOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mBgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"offload_bg", offloadConstants, true);
- mFgOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
+ mFgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
"offload_fg", foreConstants, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
@@ -10627,7 +10627,7 @@
pw.println();
for (BroadcastQueue queue : mBroadcastQueues) {
pw.println(" mBroadcastsScheduled [" + queue.mQueueName + "]="
- + queue.mBroadcastsScheduled);
+ + queue.hasBroadcastsScheduled());
}
pw.println(" mHandler:");
mHandler.dump(new PrintWriterPrinter(pw), " ");
@@ -15186,7 +15186,7 @@
// It's not the current receiver, but it might be starting up to become one
for (BroadcastQueue queue : mBroadcastQueues) {
- final BroadcastRecord r = queue.mPendingBroadcast;
+ final BroadcastRecord r = queue.getPendingBroadcastLocked();
if (r != null && r.curApp == app) {
// found it; report which queue it's in
receivingQueues.add(queue);
@@ -15301,7 +15301,7 @@
@GuardedBy("this")
final boolean canGcNowLocked() {
for (BroadcastQueue q : mBroadcastQueues) {
- if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isIdle()) {
+ if (!q.isIdle()) {
return false;
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 31d9f96..7500cdd 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -16,236 +16,37 @@
package com.android.server.am;
-import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
-import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
-import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
-import static android.text.TextUtils.formatSimple;
-
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
-import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
-import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
-import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER;
-import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_START_RECEIVER;
-
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
-import android.app.IApplicationThread;
-import android.app.PendingIntent;
-import android.app.RemoteServiceException.CannotDeliverBroadcastException;
-import android.app.usage.UsageEvents.Event;
-import android.app.usage.UsageStatsManagerInternal;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.IIntentReceiver;
-import android.content.IIntentSender;
import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerExemptionManager.ReasonCode;
-import android.os.PowerExemptionManager.TempAllowListType;
-import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.permission.IPermissionManager;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Slog;
-import android.util.SparseIntArray;
-import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.os.TimeoutRecord;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.LocalServices;
-import com.android.server.pm.UserManagerInternal;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
import java.util.Set;
/**
- * BROADCASTS
- *
- * We keep three broadcast queues and associated bookkeeping, one for those at
- * foreground priority, and one for normal (background-priority) broadcasts, and one to
- * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
+ * Queue of broadcast intents and associated bookkeeping.
*/
-public final class BroadcastQueue {
- private static final String TAG = "BroadcastQueue";
- private static final String TAG_MU = TAG + POSTFIX_MU;
- private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
-
- static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
- static final int MAX_BROADCAST_SUMMARY_HISTORY
- = ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
-
+public abstract class BroadcastQueue {
final ActivityManagerService mService;
-
- /**
- * Behavioral parameters such as timeouts and deferral policy, tracking Settings
- * for runtime configurability
- */
+ final Handler mHandler;
final BroadcastConstants mConstants;
-
- /**
- * Recognizable moniker for this queue
- */
final String mQueueName;
- /**
- * If true, we can delay broadcasts while waiting services to finish in the previous
- * receiver's process.
- */
- final boolean mDelayBehindServices;
-
- /**
- * Lists of all active broadcasts that are to be executed immediately
- * (without waiting for another broadcast to finish). Currently this only
- * contains broadcasts to registered receivers, to avoid spinning up
- * a bunch of processes to execute IntentReceiver components. Background-
- * and foreground-priority broadcasts are queued separately.
- */
- final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
-
- /**
- * Tracking of the ordered broadcast queue, including deferral policy and alarm
- * prioritization.
- */
- final BroadcastDispatcher mDispatcher;
-
- /**
- * Refcounting for completion callbacks of split/deferred broadcasts. The key
- * is an opaque integer token assigned lazily when a broadcast is first split
- * into multiple BroadcastRecord objects.
- */
- final SparseIntArray mSplitRefcounts = new SparseIntArray();
- private int mNextToken = 0;
-
- /**
- * Historical data of past broadcasts, for debugging. This is a ring buffer
- * whose last element is at mHistoryNext.
- */
- final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY];
- int mHistoryNext = 0;
-
- /**
- * Summary of historical data of past broadcasts, for debugging. This is a
- * ring buffer whose last element is at mSummaryHistoryNext.
- */
- final Intent[] mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
- int mSummaryHistoryNext = 0;
-
- /**
- * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring
- * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall
- * clock time, not elapsed.
- */
- final long[] mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
- final long[] mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
- final long[] mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
-
- /**
- * Set when we current have a BROADCAST_INTENT_MSG in flight.
- */
- boolean mBroadcastsScheduled = false;
-
- /**
- * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
- */
- boolean mPendingBroadcastTimeoutMessage;
-
- /**
- * Intent broadcasts that we have tried to start, but are
- * waiting for the application's process to be created. We only
- * need one per scheduling class (instead of a list) because we always
- * process broadcasts one at a time, so no others can be started while
- * waiting for this one.
- */
- BroadcastRecord mPendingBroadcast = null;
-
- /**
- * The receiver index that is pending, to restart the broadcast if needed.
- */
- int mPendingBroadcastRecvIndex;
-
- static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
- static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
-
- // log latency metrics for ordered broadcasts during BOOT_COMPLETED processing
- boolean mLogLatencyMetrics = true;
-
- final BroadcastHandler mHandler;
-
- private final class BroadcastHandler extends Handler {
- public BroadcastHandler(Looper looper) {
- super(looper, null, true);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case BROADCAST_INTENT_MSG: {
- if (DEBUG_BROADCAST) Slog.v(
- TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
- + mQueueName + "]");
- processNextBroadcast(true);
- } break;
- case BROADCAST_TIMEOUT_MSG: {
- synchronized (mService) {
- broadcastTimeoutLocked(true);
- }
- } break;
- }
- }
- }
-
BroadcastQueue(ActivityManagerService service, Handler handler,
- String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
+ String name, BroadcastConstants constants) {
mService = service;
- mHandler = new BroadcastHandler(handler.getLooper());
+ mHandler = handler;
mQueueName = name;
- mDelayBehindServices = allowDelayBehindServices;
-
mConstants = constants;
- mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
}
void start(ContentResolver resolver) {
- mDispatcher.start();
mConstants.startObserving(mHandler, resolver);
}
@@ -254,2322 +55,62 @@
return mQueueName;
}
- public boolean isPendingBroadcastProcessLocked(int pid) {
- return mPendingBroadcast != null && mPendingBroadcast.curApp.getPid() == pid;
- }
+ public abstract boolean isDelayBehindServices();
- boolean isPendingBroadcastProcessLocked(ProcessRecord app) {
- return mPendingBroadcast != null && mPendingBroadcast.curApp == app;
- }
+ public abstract boolean hasBroadcastsScheduled();
- public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
- r.enqueueClockTime = System.currentTimeMillis();
- r.enqueueTime = SystemClock.uptimeMillis();
- r.enqueueRealTime = SystemClock.elapsedRealtime();
- mParallelBroadcasts.add(r);
- enqueueBroadcastHelper(r);
- }
+ public abstract BroadcastRecord getPendingBroadcastLocked();
- public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
- r.enqueueClockTime = System.currentTimeMillis();
- r.enqueueTime = SystemClock.uptimeMillis();
- r.enqueueRealTime = SystemClock.elapsedRealtime();
- mDispatcher.enqueueOrderedBroadcastLocked(r);
- enqueueBroadcastHelper(r);
- }
+ public abstract BroadcastRecord getActiveBroadcastLocked();
- /**
- * Don't call this method directly; call enqueueParallelBroadcastLocked or
- * enqueueOrderedBroadcastLocked.
- */
- private void enqueueBroadcastHelper(BroadcastRecord r) {
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
- System.identityHashCode(r));
- }
- }
+ public abstract boolean isPendingBroadcastProcessLocked(int pid);
- /**
- * Find the same intent from queued parallel broadcast, replace with a new one and return
- * the old one.
- */
- public final BroadcastRecord replaceParallelBroadcastLocked(BroadcastRecord r) {
- return replaceBroadcastLocked(mParallelBroadcasts, r, "PARALLEL");
- }
+ public abstract boolean isPendingBroadcastProcessLocked(ProcessRecord app);
- /**
- * Find the same intent from queued ordered broadcast, replace with a new one and return
- * the old one.
- */
- public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) {
- return mDispatcher.replaceBroadcastLocked(r, "ORDERED");
- }
+ public abstract void enqueueParallelBroadcastLocked(BroadcastRecord r);
- private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue,
- BroadcastRecord r, String typeForLogging) {
- final Intent intent = r.intent;
- for (int i = queue.size() - 1; i > 0; i--) {
- final BroadcastRecord old = queue.get(i);
- if (old.userId == r.userId && intent.filterEquals(old.intent)) {
- if (DEBUG_BROADCAST) {
- Slog.v(TAG_BROADCAST, "***** DROPPING "
- + typeForLogging + " [" + mQueueName + "]: " + intent);
- }
- queue.set(i, r);
- return old;
- }
- }
- return null;
- }
+ public abstract void enqueueOrderedBroadcastLocked(BroadcastRecord r);
- private final void processCurBroadcastLocked(BroadcastRecord r,
- ProcessRecord app) throws RemoteException {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Process cur broadcast " + r + " for app " + app);
- final IApplicationThread thread = app.getThread();
- if (thread == null) {
- throw new RemoteException();
- }
- if (app.isInFullBackup()) {
- skipReceiverLocked(r);
- return;
- }
+ public abstract BroadcastRecord replaceParallelBroadcastLocked(BroadcastRecord r);
- r.receiver = thread.asBinder();
- r.curApp = app;
- final ProcessReceiverRecord prr = app.mReceivers;
- prr.addCurReceiver(r);
- app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
- // Don't bump its LRU position if it's in the background restricted.
- if (mService.mInternal.getRestrictionLevel(app.info.packageName, app.userId)
- < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
- mService.updateLruProcessLocked(app, false, null);
- }
- // Make sure the oom adj score is updated before delivering the broadcast.
- // Force an update, even if there are other pending requests, overall it still saves time,
- // because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
- mService.enqueueOomAdjTargetLocked(app);
- mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
+ public abstract BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r);
- // Tell the application to launch this receiver.
- maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
- r.intent.setComponent(r.curComponent);
+ public abstract void updateUidReadyForBootCompletedBroadcastLocked(int uid);
- boolean started = false;
- try {
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "Delivering to component " + r.curComponent
- + ": " + r);
- mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
- PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
- thread.scheduleReceiver(prepareReceiverIntent(r.intent, r.curFilteredExtras),
- r.curReceiver, null /* compatInfo (unused but need to keep method signature) */,
- r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
- app.mState.getReportedProcState());
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Process cur broadcast " + r + " DELIVERED for app " + app);
- started = true;
- } finally {
- if (!started) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Process cur broadcast " + r + ": NOT STARTED!");
- r.receiver = null;
- r.curApp = null;
- prr.removeCurReceiver(r);
- }
- }
+ public abstract boolean sendPendingBroadcastsLocked(ProcessRecord app);
- // if something bad happens here, launch the app and try again
- if (app.isKilled()) {
- throw new RemoteException("app gets killed during broadcasting");
- }
- }
+ public abstract void skipPendingBroadcastLocked(int pid);
- /**
- * Called by ActivityManagerService to notify that the uid has process started, if there is any
- * deferred BOOT_COMPLETED broadcast, the BroadcastDispatcher can dispatch the broadcast now.
- * @param uid
- */
- public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
- mDispatcher.updateUidReadyForBootCompletedBroadcastLocked(uid);
- }
+ public abstract void skipCurrentReceiverLocked(ProcessRecord app);
- public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
- boolean didSomething = false;
- final BroadcastRecord br = mPendingBroadcast;
- if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
- if (br.curApp != app) {
- Slog.e(TAG, "App mismatch when sending pending broadcast to "
- + app.processName + ", intended target is " + br.curApp.processName);
- return false;
- }
- try {
- mPendingBroadcast = null;
- br.mIsReceiverAppRunning = false;
- processCurBroadcastLocked(br, app);
- didSomething = true;
- } catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting receiver "
- + br.curComponent.flattenToShortString(), e);
- logBroadcastReceiverDiscardLocked(br);
- finishReceiverLocked(br, br.resultCode, br.resultData,
- br.resultExtras, br.resultAbort, false);
- scheduleBroadcastsLocked();
- // We need to reset the state if we failed to start the receiver.
- br.state = BroadcastRecord.IDLE;
- throw new RuntimeException(e.getMessage());
- }
- }
- return didSomething;
- }
+ public abstract void scheduleBroadcastsLocked();
- public void skipPendingBroadcastLocked(int pid) {
- final BroadcastRecord br = mPendingBroadcast;
- if (br != null && br.curApp.getPid() == pid) {
- br.state = BroadcastRecord.IDLE;
- br.nextReceiver = mPendingBroadcastRecvIndex;
- mPendingBroadcast = null;
- scheduleBroadcastsLocked();
- }
- }
+ public abstract BroadcastRecord getMatchingOrderedReceiver(IBinder receiver);
- // Skip the current receiver, if any, that is in flight to the given process
- public void skipCurrentReceiverLocked(ProcessRecord app) {
- BroadcastRecord r = null;
- final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
- if (curActive != null && curActive.curApp == app) {
- // confirmed: the current active broadcast is to the given app
- r = curActive;
- }
+ public abstract boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
+ String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices);
- // If the current active broadcast isn't this BUT we're waiting for
- // mPendingBroadcast to spin up the target app, that's what we use.
- if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "[" + mQueueName + "] skip & discard pending app " + r);
- r = mPendingBroadcast;
- }
+ public abstract void backgroundServicesFinishedLocked(int userId);
- if (r != null) {
- skipReceiverLocked(r);
- }
- }
-
- private void skipReceiverLocked(BroadcastRecord r) {
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- }
-
- public void scheduleBroadcastsLocked() {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
- + mQueueName + "]: current="
- + mBroadcastsScheduled);
-
- if (mBroadcastsScheduled) {
- return;
- }
- mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
- mBroadcastsScheduled = true;
- }
-
- public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
- BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
- if (br != null && br.receiver == receiver) {
- return br;
- }
- return null;
- }
-
- // > 0 only, no worry about "eventual" recycling
- private int nextSplitTokenLocked() {
- int next = mNextToken + 1;
- if (next <= 0) {
- next = 1;
- }
- mNextToken = next;
- return next;
- }
-
- private void postActivityStartTokenRemoval(ProcessRecord app, BroadcastRecord r) {
- // the receiver had run for less than allowed bg activity start timeout,
- // so allow the process to still start activities from bg for some more time
- String msgToken = (app.toShortString() + r.toString()).intern();
- // first, if there exists a past scheduled request to remove this token, drop
- // that request - we don't want the token to be swept from under our feet...
- mHandler.removeCallbacksAndMessages(msgToken);
- // ...then schedule the removal of the token after the extended timeout
- mHandler.postAtTime(() -> {
- synchronized (mService) {
- app.removeAllowBackgroundActivityStartsToken(r);
- }
- }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
- }
-
- public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
- String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
- final int state = r.state;
- final ActivityInfo receiver = r.curReceiver;
- final long finishTime = SystemClock.uptimeMillis();
- final long elapsed = finishTime - r.receiverTime;
- r.state = BroadcastRecord.IDLE;
- final int curIndex = r.nextReceiver - 1;
- if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
- final Object curReceiver = r.receivers.get(curIndex);
- FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
- r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
- ActivityManagerService.getShortAction(r.intent.getAction()),
- curReceiver instanceof BroadcastFilter
- ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
- : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
- r.mIsReceiverAppRunning
- ? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM
- : BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
- r.dispatchTime - r.enqueueTime,
- r.receiverTime - r.dispatchTime,
- finishTime - r.receiverTime);
- }
- if (state == BroadcastRecord.IDLE) {
- Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
- }
- if (r.allowBackgroundActivityStarts && r.curApp != null) {
- if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
- // if the receiver has run for more than allowed bg activity start timeout,
- // just remove the token for this process now and we're done
- r.curApp.removeAllowBackgroundActivityStartsToken(r);
- } else {
- // It gets more time; post the removal to happen at the appropriate moment
- postActivityStartTokenRemoval(r.curApp, r);
- }
- }
- // If we're abandoning this broadcast before any receivers were actually spun up,
- // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
- if (r.nextReceiver > 0) {
- r.duration[r.nextReceiver - 1] = elapsed;
- }
-
- // if this receiver was slow, impose deferral policy on the app. This will kick in
- // when processNextBroadcastLocked() next finds this uid as a receiver identity.
- if (!r.timeoutExempt) {
- // r.curApp can be null if finish has raced with process death - benign
- // edge case, and we just ignore it because we're already cleaning up
- // as expected.
- if (r.curApp != null
- && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
- // Core system packages are exempt from deferral policy
- if (!UserHandle.isCore(r.curApp.uid)) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1)
- + " was slow: " + receiver + " br=" + r);
- }
- mDispatcher.startDeferring(r.curApp.uid);
- } else {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid
- + " receiver was slow but not deferring: "
- + receiver + " br=" + r);
- }
- }
- }
- } else {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Finished broadcast " + r.intent.getAction()
- + " is exempt from deferral policy");
- }
- }
-
- r.receiver = null;
- r.intent.setComponent(null);
- if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
- r.curApp.mReceivers.removeCurReceiver(r);
- mService.enqueueOomAdjTargetLocked(r.curApp);
- }
- if (r.curFilter != null) {
- r.curFilter.receiverList.curBroadcast = null;
- }
- r.curFilter = null;
- r.curReceiver = null;
- r.curApp = null;
- r.curFilteredExtras = null;
- mPendingBroadcast = null;
-
- r.resultCode = resultCode;
- r.resultData = resultData;
- r.resultExtras = resultExtras;
- if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
- r.resultAbort = resultAbort;
- } else {
- r.resultAbort = false;
- }
-
- // If we want to wait behind services *AND* we're finishing the head/
- // active broadcast on its queue
- if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
- && r.queue.mDispatcher.getActiveBroadcastLocked() == r) {
- ActivityInfo nextReceiver;
- if (r.nextReceiver < r.receivers.size()) {
- Object obj = r.receivers.get(r.nextReceiver);
- nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
- } else {
- nextReceiver = null;
- }
- // Don't do this if the next receive is in the same process as the current one.
- if (receiver == null || nextReceiver == null
- || receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
- || !receiver.processName.equals(nextReceiver.processName)) {
- // In this case, we are ready to process the next receiver for the current broadcast,
- // but are on a queue that would like to wait for services to finish before moving
- // on. If there are background services currently starting, then we will go into a
- // special state where we hold off on continuing this broadcast until they are done.
- if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
- Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());
- r.state = BroadcastRecord.WAITING_SERVICES;
- return false;
- }
- }
- }
-
- r.curComponent = null;
-
- // We will process the next receiver right now if this is finishing
- // an app receiver (which is always asynchronous) or after we have
- // come back from calling a receiver.
- return state == BroadcastRecord.APP_RECEIVE
- || state == BroadcastRecord.CALL_DONE_RECEIVE;
- }
-
- public void backgroundServicesFinishedLocked(int userId) {
- BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
- if (br != null) {
- if (br.userId == userId && br.state == BroadcastRecord.WAITING_SERVICES) {
- Slog.i(TAG, "Resuming delayed broadcast");
- br.curComponent = null;
- br.state = BroadcastRecord.IDLE;
- processNextBroadcastLocked(false, false);
- }
- }
- }
-
- void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
+ public abstract void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser,
int receiverUid, int callingUid, long dispatchDelay,
- long receiveDelay) throws RemoteException {
- // Send the intent to the receiver asynchronously using one-way binder calls.
- if (app != null) {
- final IApplicationThread thread = app.getThread();
- if (thread != null) {
- // If we have an app thread, do the call through that so it is
- // correctly ordered with other one-way calls.
- try {
- thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky, sendingUser,
- app.mState.getReportedProcState());
- // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
- // DeadObjectException when the process isn't actually dead.
- //} catch (DeadObjectException ex) {
- // Failed to call into the process. It's dying so just let it die and move on.
- // throw ex;
- } catch (RemoteException ex) {
- // Failed to call into the process. It's either dying or wedged. Kill it gently.
- synchronized (mService) {
- Slog.w(TAG, "Can't deliver broadcast to " + app.processName
- + " (pid " + app.getPid() + "). Crashing it.");
- app.scheduleCrashLocked("can't deliver broadcast",
- CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
- }
- throw ex;
- }
- } else {
- // Application has died. Receiver doesn't exist.
- throw new RemoteException("app.thread must not be null");
- }
- } else {
- receiver.performReceive(intent, resultCode, data, extras, ordered,
- sticky, sendingUser);
- }
- if (!ordered) {
- FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
- receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
- callingUid == -1 ? Process.SYSTEM_UID : callingUid,
- ActivityManagerService.getShortAction(intent.getAction()),
- BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
- BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
- dispatchDelay, receiveDelay, 0 /* finish_delay */);
- }
- }
+ long receiveDelay) throws RemoteException;
- private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
- BroadcastFilter filter, boolean ordered, int index) {
- boolean skip = false;
- if (r.options != null && !r.options.testRequireCompatChange(filter.owningUid)) {
- Slog.w(TAG, "Compat change filtered: broadcasting " + r.intent.toString()
- + " to uid " + filter.owningUid + " due to compat change "
- + r.options.getRequireCompatChangeId());
- skip = true;
- }
- if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
- filter.packageName, filter.owningUid)) {
- Slog.w(TAG, "Association not allowed: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
- + filter);
- skip = true;
- }
- if (!skip && !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
- r.callingPid, r.resolvedType, filter.receiverList.uid)) {
- Slog.w(TAG, "Firewall blocked: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
- + filter);
- skip = true;
- }
- // Check that the sender has permission to send to this receiver
- if (filter.requiredPermission != null) {
- int perm = mService.checkComponentPermission(filter.requiredPermission,
- r.callingPid, r.callingUid, -1, true);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
- + " requires " + filter.requiredPermission
- + " due to registered receiver " + filter);
- skip = true;
- } else {
- final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
- if (opCode != AppOpsManager.OP_NONE
- && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
- r.callerPackage, r.callerFeatureId, "Broadcast sent to protected receiver")
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: broadcasting "
- + r.intent.toString()
- + " from " + r.callerPackage + " (pid="
- + r.callingPid + ", uid=" + r.callingUid + ")"
- + " requires appop " + AppOpsManager.permissionToOp(
- filter.requiredPermission)
- + " due to registered receiver " + filter);
- skip = true;
- }
- }
- }
+ public abstract void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj);
- if (!skip && (filter.receiverList.app == null || filter.receiverList.app.isKilled()
- || filter.receiverList.app.mErrorState.isCrashing())) {
- Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
- + " to " + filter.receiverList + ": process gone or crashing");
- skip = true;
- }
+ public abstract boolean cleanupDisabledPackageReceiversLocked(
+ String packageName, Set<String> filterByClasses, int userId, boolean doit);
- // Ensure that broadcasts are only sent to other Instant Apps if they are marked as
- // visible to Instant Apps.
- final boolean visibleToInstantApps =
- (r.intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
+ public abstract boolean isIdle();
- if (!skip && !visibleToInstantApps && filter.instantApp
- && filter.receiverList.uid != r.callingUid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")"
- + " not specifying FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS");
- skip = true;
- }
+ public abstract void cancelDeferrals();
- if (!skip && !filter.visibleToInstantApp && r.callerInstantApp
- && filter.receiverList.uid != r.callingUid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires receiver be visible to instant apps"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
+ public abstract String describeState();
- // Check that the receiver has the required permission(s) to receive this broadcast.
- if (!skip && r.requiredPermissions != null && r.requiredPermissions.length > 0) {
- for (int i = 0; i < r.requiredPermissions.length; i++) {
- String requiredPermission = r.requiredPermissions[i];
- int perm = mService.checkComponentPermission(requiredPermission,
- filter.receiverList.pid, filter.receiverList.uid, -1, true);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires " + requiredPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
- && mService.getAppOpsManager().noteOpNoThrow(appOp,
- filter.receiverList.uid, filter.packageName, filter.featureId,
- "Broadcast delivered to registered receiver " + filter.receiverId)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires appop " + AppOpsManager.permissionToOp(
- requiredPermission)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- }
- }
- if (!skip && (r.requiredPermissions == null || r.requiredPermissions.length == 0)) {
- int perm = mService.checkComponentPermission(null,
- filter.receiverList.pid, filter.receiverList.uid, -1, true);
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: security check failed when receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- }
- // Check that the receiver does *not* have any excluded permissions
- if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
- for (int i = 0; i < r.excludedPermissions.length; i++) {
- String excludedPermission = r.excludedPermissions[i];
- final int perm = mService.checkComponentPermission(excludedPermission,
- filter.receiverList.pid, filter.receiverList.uid, -1, true);
+ public abstract void dumpDebug(ProtoOutputStream proto, long fieldId);
- int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
- if (appOp != AppOpsManager.OP_NONE) {
- // When there is an app op associated with the permission,
- // skip when both the permission and the app op are
- // granted.
- if ((perm == PackageManager.PERMISSION_GRANTED) && (
- mService.getAppOpsManager().checkOpNoThrow(appOp,
- filter.receiverList.uid,
- filter.packageName)
- == AppOpsManager.MODE_ALLOWED)) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " excludes appop " + AppOpsManager.permissionToOp(
- excludedPermission)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- } else {
- // When there is no app op associated with the permission,
- // skip when permission is granted.
- if (perm == PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " excludes " + excludedPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- }
- }
- }
-
- // Check that the receiver does *not* belong to any of the excluded packages
- if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) {
- if (ArrayUtils.contains(r.excludedPackages, filter.packageName)) {
- Slog.w(TAG, "Skipping delivery of excluded package "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " excludes package " + filter.packageName
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- }
-
- // If the broadcast also requires an app op check that as well.
- if (!skip && r.appOp != AppOpsManager.OP_NONE
- && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
- filter.receiverList.uid, filter.packageName, filter.featureId,
- "Broadcast delivered to registered receiver " + filter.receiverId)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent.toString()
- + " to " + filter.receiverList.app
- + " (pid=" + filter.receiverList.pid
- + ", uid=" + filter.receiverList.uid + ")"
- + " requires appop " + AppOpsManager.opToName(r.appOp)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
-
- // Ensure that broadcasts are only sent to other apps if they are explicitly marked as
- // exported, or are System level broadcasts
- if (!skip && !filter.exported && mService.checkComponentPermission(null, r.callingPid,
- r.callingUid, filter.receiverList.uid, filter.exported)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Exported Denial: sending "
- + r.intent.toString()
- + ", action: " + r.intent.getAction()
- + " from " + r.callerPackage
- + " (uid=" + r.callingUid + ")"
- + " due to receiver " + filter.receiverList.app
- + " (uid " + filter.receiverList.uid + ")"
- + " not specifying RECEIVER_EXPORTED");
- skip = true;
- }
-
- // Filter packages in the intent extras, skipping delivery if none of the packages is
- // visible to the receiver.
- Bundle filteredExtras = null;
- if (!skip && r.filterExtrasForReceiver != null) {
- final Bundle extras = r.intent.getExtras();
- if (extras != null) {
- filteredExtras = r.filterExtrasForReceiver.apply(filter.receiverList.uid, extras);
- if (filteredExtras == null) {
- if (DEBUG_BROADCAST) {
- Slog.v(TAG, "Skipping delivery to "
- + filter.receiverList.app
- + " : receiver is filtered by the package visibility");
- }
- skip = true;
- }
- }
- }
-
- if (skip) {
- r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
- return;
- }
-
- // If permissions need a review before any of the app components can run, we drop
- // the broadcast and if the calling app is in the foreground and the broadcast is
- // explicit we launch the review UI passing it a pending intent to send the skipped
- // broadcast.
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
- filter.owningUserId)) {
- r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
- return;
- }
-
- r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
-
- // If this is not being sent as an ordered broadcast, then we
- // don't want to touch the fields that keep track of the current
- // state of ordered broadcasts.
- if (ordered) {
- r.receiver = filter.receiverList.receiver.asBinder();
- r.curFilter = filter;
- filter.receiverList.curBroadcast = r;
- r.state = BroadcastRecord.CALL_IN_RECEIVE;
- if (filter.receiverList.app != null) {
- // Bump hosting application to no longer be in background
- // scheduling class. Note that we can't do that if there
- // isn't an app... but we can only be in that case for
- // things that directly call the IActivityManager API, which
- // are already core system stuff so don't matter for this.
- r.curApp = filter.receiverList.app;
- filter.receiverList.app.mReceivers.addCurReceiver(r);
- mService.enqueueOomAdjTargetLocked(r.curApp);
- mService.updateOomAdjPendingTargetsLocked(
- OOM_ADJ_REASON_START_RECEIVER);
- }
- } else if (filter.receiverList.app != null) {
- mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app,
- OOM_ADJ_REASON_START_RECEIVER);
- }
-
- try {
- if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
- "Delivering to " + filter + " : " + r);
- if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
- // Skip delivery if full backup in progress
- // If it's an ordered broadcast, we need to continue to the next receiver.
- if (ordered) {
- skipReceiverLocked(r);
- }
- } else {
- r.receiverTime = SystemClock.uptimeMillis();
- maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
- maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
- maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
- performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- prepareReceiverIntent(r.intent, filteredExtras), r.resultCode, r.resultData,
- r.resultExtras, r.ordered, r.initialSticky, r.userId,
- filter.receiverList.uid, r.callingUid,
- r.dispatchTime - r.enqueueTime,
- r.receiverTime - r.dispatchTime);
- // parallel broadcasts are fire-and-forget, not bookended by a call to
- // finishReceiverLocked(), so we manage their activity-start token here
- if (filter.receiverList.app != null
- && r.allowBackgroundActivityStarts && !r.ordered) {
- postActivityStartTokenRemoval(filter.receiverList.app, r);
- }
- }
- if (ordered) {
- r.state = BroadcastRecord.CALL_DONE_RECEIVE;
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
- // Clean up ProcessRecord state related to this broadcast attempt
- if (filter.receiverList.app != null) {
- filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
- if (ordered) {
- filter.receiverList.app.mReceivers.removeCurReceiver(r);
- // Something wrong, its oom adj could be downgraded, but not in a hurry.
- mService.enqueueOomAdjTargetLocked(r.curApp);
- }
- }
- // And BroadcastRecord state related to ordered delivery, if appropriate
- if (ordered) {
- r.receiver = null;
- r.curFilter = null;
- filter.receiverList.curBroadcast = null;
- }
- }
- }
-
- private boolean requestStartTargetPermissionsReviewIfNeededLocked(
- BroadcastRecord receiverRecord, String receivingPackageName,
- final int receivingUserId) {
- if (!mService.getPackageManagerInternal().isPermissionsReviewRequired(
- receivingPackageName, receivingUserId)) {
- return true;
- }
-
- final boolean callerForeground = receiverRecord.callerApp != null
- ? receiverRecord.callerApp.mState.getSetSchedGroup()
- != ProcessList.SCHED_GROUP_BACKGROUND : true;
-
- // Show a permission review UI only for explicit broadcast from a foreground app
- if (callerForeground && receiverRecord.intent.getComponent() != null) {
- IIntentSender target = mService.mPendingIntentController.getIntentSender(
- ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
- receiverRecord.callerFeatureId, receiverRecord.callingUid,
- receiverRecord.userId, null, null, 0,
- new Intent[]{receiverRecord.intent},
- new String[]{receiverRecord.intent.resolveType(mService.mContext
- .getContentResolver())},
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null);
-
- final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, receivingPackageName);
- intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
-
- if (DEBUG_PERMISSIONS_REVIEW) {
- Slog.i(TAG, "u" + receivingUserId + " Launching permission review for package "
- + receivingPackageName);
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mService.mContext.startActivityAsUser(intent, new UserHandle(receivingUserId));
- }
- });
- } else {
- Slog.w(TAG, "u" + receivingUserId + " Receiving a broadcast in package"
- + receivingPackageName + " requires a permissions review");
- }
-
- return false;
- }
-
- void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
- @Nullable BroadcastOptions brOptions) {
- if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
- return;
- }
- long duration = brOptions.getTemporaryAppAllowlistDuration();
- final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
- final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
- final String reason = brOptions.getTemporaryAppAllowlistReason();
-
- if (duration > Integer.MAX_VALUE) {
- duration = Integer.MAX_VALUE;
- }
- // XXX ideally we should pause the broadcast until everything behind this is done,
- // or else we will likely start dispatching the broadcast before we have opened
- // access to the app (there is a lot of asynchronicity behind this). It is probably
- // not that big a deal, however, because the main purpose here is to allow apps
- // to hold wake locks, and they will be able to acquire their wake lock immediately
- // it just won't be enabled until we get through this work.
- StringBuilder b = new StringBuilder();
- b.append("broadcast:");
- UserHandle.formatUid(b, r.callingUid);
- b.append(":");
- if (r.intent.getAction() != null) {
- b.append(r.intent.getAction());
- } else if (r.intent.getComponent() != null) {
- r.intent.getComponent().appendShortString(b);
- } else if (r.intent.getData() != null) {
- b.append(r.intent.getData());
- }
- b.append(",reason:");
- b.append(reason);
- if (DEBUG_BROADCAST) {
- Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
- + " type=" + type + " : " + b.toString());
- }
- mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
- r.callingUid);
- }
-
- /**
- * Return true if all given permissions are signature-only perms.
- */
- final boolean isSignaturePerm(String[] perms) {
- if (perms == null) {
- return false;
- }
- IPermissionManager pm = AppGlobals.getPermissionManager();
- for (int i = perms.length-1; i >= 0; i--) {
- try {
- PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
- if (pi == null) {
- // a required permission that no package has actually
- // defined cannot be signature-required.
- return false;
- }
- if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE
- | PermissionInfo.PROTECTION_FLAG_PRIVILEGED))
- != PermissionInfo.PROTECTION_SIGNATURE) {
- // If this a signature permission and NOT allowed for privileged apps, it
- // is okay... otherwise, nope!
- return false;
- }
- } catch (RemoteException e) {
- return false;
- }
- }
- return true;
- }
-
- private void processNextBroadcast(boolean fromMsg) {
- synchronized (mService) {
- processNextBroadcastLocked(fromMsg, false);
- }
- }
-
- static String broadcastDescription(BroadcastRecord r, ComponentName component) {
- return r.intent.toString()
- + " from " + r.callerPackage + " (pid=" + r.callingPid
- + ", uid=" + r.callingUid + ") to " + component.flattenToShortString();
- }
-
- private static Intent prepareReceiverIntent(@NonNull Intent originalIntent,
- @Nullable Bundle filteredExtras) {
- final Intent intent = new Intent(originalIntent);
- if (filteredExtras != null) {
- intent.replaceExtras(filteredExtras);
- }
- return intent;
- }
-
- final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
- BroadcastRecord r;
-
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
- + mQueueName + "]: "
- + mParallelBroadcasts.size() + " parallel broadcasts; "
- + mDispatcher.describeStateLocked());
-
- mService.updateCpuStats();
-
- if (fromMsg) {
- mBroadcastsScheduled = false;
- }
-
- // First, deliver any non-serialized broadcasts right away.
- while (mParallelBroadcasts.size() > 0) {
- r = mParallelBroadcasts.remove(0);
- r.dispatchTime = SystemClock.uptimeMillis();
- r.dispatchRealTime = SystemClock.elapsedRealtime();
- r.dispatchClockTime = System.currentTimeMillis();
- r.mIsReceiverAppRunning = true;
-
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
- System.identityHashCode(r));
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
- System.identityHashCode(r));
- }
-
- final int N = r.receivers.size();
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
- + mQueueName + "] " + r);
- for (int i=0; i<N; i++) {
- Object target = r.receivers.get(i);
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Delivering non-ordered on [" + mQueueName + "] to registered "
- + target + ": " + r);
- deliverToRegisteredReceiverLocked(r,
- (BroadcastFilter) target, false, i);
- }
- addBroadcastToHistoryLocked(r);
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
- + mQueueName + "] " + r);
- }
-
- // Now take care of the next serialized one...
-
- // If we are waiting for a process to come up to handle the next
- // broadcast, then do nothing at this point. Just in case, we
- // check that the process we're waiting for still exists.
- if (mPendingBroadcast != null) {
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "processNextBroadcast [" + mQueueName + "]: waiting for "
- + mPendingBroadcast.curApp);
-
- boolean isDead;
- if (mPendingBroadcast.curApp.getPid() > 0) {
- synchronized (mService.mPidsSelfLocked) {
- ProcessRecord proc = mService.mPidsSelfLocked.get(
- mPendingBroadcast.curApp.getPid());
- isDead = proc == null || proc.mErrorState.isCrashing();
- }
- } else {
- final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
- mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
- isDead = proc == null || !proc.isPendingStart();
- }
- if (!isDead) {
- // It's still alive, so keep waiting
- return;
- } else {
- Slog.w(TAG, "pending app ["
- + mQueueName + "]" + mPendingBroadcast.curApp
- + " died before responding to broadcast");
- mPendingBroadcast.state = BroadcastRecord.IDLE;
- mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
- mPendingBroadcast = null;
- }
- }
-
- boolean looped = false;
-
- do {
- final long now = SystemClock.uptimeMillis();
- r = mDispatcher.getNextBroadcastLocked(now);
-
- if (r == null) {
- // No more broadcasts are deliverable right now, so all done!
- mDispatcher.scheduleDeferralCheckLocked(false);
- synchronized (mService.mAppProfiler.mProfilerLock) {
- mService.mAppProfiler.scheduleAppGcsLPf();
- }
- if (looped && !skipOomAdj) {
- // If we had finished the last ordered broadcast, then
- // make sure all processes have correct oom and sched
- // adjustments.
- mService.updateOomAdjPendingTargetsLocked(
- OOM_ADJ_REASON_START_RECEIVER);
- }
-
- // when we have no more ordered broadcast on this queue, stop logging
- if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
- mLogLatencyMetrics = false;
- }
-
- return;
- }
-
- boolean forceReceive = false;
-
- // Ensure that even if something goes awry with the timeout
- // detection, we catch "hung" broadcasts here, discard them,
- // and continue to make progress.
- //
- // This is only done if the system is ready so that early-stage receivers
- // don't get executed with timeouts; and of course other timeout-
- // exempt broadcasts are ignored.
- int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
- if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
- if ((numReceivers > 0) &&
- (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
- Slog.w(TAG, "Hung broadcast ["
- + mQueueName + "] discarded after timeout failure:"
- + " now=" + now
- + " dispatchTime=" + r.dispatchTime
- + " startTime=" + r.receiverTime
- + " intent=" + r.intent
- + " numReceivers=" + numReceivers
- + " nextReceiver=" + r.nextReceiver
- + " state=" + r.state);
- broadcastTimeoutLocked(false); // forcibly finish this broadcast
- forceReceive = true;
- r.state = BroadcastRecord.IDLE;
- }
- }
-
- if (r.state != BroadcastRecord.IDLE) {
- if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
- "processNextBroadcast("
- + mQueueName + ") called when not idle (state="
- + r.state + ")");
- return;
- }
-
- // Is the current broadcast is done for any reason?
- if (r.receivers == null || r.nextReceiver >= numReceivers
- || r.resultAbort || forceReceive) {
- // Send the final result if requested
- if (r.resultTo != null) {
- boolean sendResult = true;
-
- // if this was part of a split/deferral complex, update the refcount and only
- // send the completion when we clear all of them
- if (r.splitToken != 0) {
- int newCount = mSplitRefcounts.get(r.splitToken) - 1;
- if (newCount == 0) {
- // done! clear out this record's bookkeeping and deliver
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Sending broadcast completion for split token "
- + r.splitToken + " : " + r.intent.getAction());
- }
- mSplitRefcounts.delete(r.splitToken);
- } else {
- // still have some split broadcast records in flight; update refcount
- // and hold off on the callback
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Result refcount now " + newCount + " for split token "
- + r.splitToken + " : " + r.intent.getAction()
- + " - not sending completion yet");
- }
- sendResult = false;
- mSplitRefcounts.put(r.splitToken, newCount);
- }
- }
- if (sendResult) {
- if (r.callerApp != null) {
- mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
- r.callerApp, OOM_ADJ_REASON_FINISH_RECEIVER);
- }
- try {
- if (DEBUG_BROADCAST) {
- Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
- + r.intent.getAction() + " app=" + r.callerApp);
- }
- if (r.dispatchTime == 0) {
- // The dispatch time here could be 0, in case it's a parallel
- // broadcast but it has a result receiver. Set it to now.
- r.dispatchTime = now;
- }
- r.mIsReceiverAppRunning = true;
- performReceiveLocked(r.callerApp, r.resultTo,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false, r.userId,
- r.callingUid, r.callingUid,
- r.dispatchTime - r.enqueueTime,
- now - r.dispatchTime);
- logBootCompletedBroadcastCompletionLatencyIfPossible(r);
- // Set this to null so that the reference
- // (local and remote) isn't kept in the mBroadcastHistory.
- r.resultTo = null;
- } catch (RemoteException e) {
- r.resultTo = null;
- Slog.w(TAG, "Failure ["
- + mQueueName + "] sending broadcast result of "
- + r.intent, e);
- }
- }
- }
-
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
- cancelBroadcastTimeoutLocked();
-
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
- "Finished with ordered broadcast " + r);
-
- // ... and on to the next...
- addBroadcastToHistoryLocked(r);
- if (r.intent.getComponent() == null && r.intent.getPackage() == null
- && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
- // This was an implicit broadcast... let's record it for posterity.
- mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
- r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
- }
- mDispatcher.retireBroadcastLocked(r);
- r = null;
- looped = true;
- continue;
- }
-
- // Check whether the next receiver is under deferral policy, and handle that
- // accordingly. If the current broadcast was already part of deferred-delivery
- // tracking, we know that it must now be deliverable as-is without re-deferral.
- if (!r.deferred) {
- final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
- if (mDispatcher.isDeferringLocked(receiverUid)) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid
- + " at " + r.nextReceiver + " is under deferral");
- }
- // If this is the only (remaining) receiver in the broadcast, "splitting"
- // doesn't make sense -- just defer it as-is and retire it as the
- // currently active outgoing broadcast.
- BroadcastRecord defer;
- if (r.nextReceiver + 1 == numReceivers) {
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Sole receiver of " + r
- + " is under deferral; setting aside and proceeding");
- }
- defer = r;
- mDispatcher.retireBroadcastLocked(r);
- } else {
- // Nontrivial case; split out 'uid's receivers to a new broadcast record
- // and defer that, then loop and pick up continuing delivery of the current
- // record (now absent those receivers).
-
- // The split operation is guaranteed to match at least at 'nextReceiver'
- defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "Post split:");
- Slog.i(TAG_BROADCAST, "Original broadcast receivers:");
- for (int i = 0; i < r.receivers.size(); i++) {
- Slog.i(TAG_BROADCAST, " " + r.receivers.get(i));
- }
- Slog.i(TAG_BROADCAST, "Split receivers:");
- for (int i = 0; i < defer.receivers.size(); i++) {
- Slog.i(TAG_BROADCAST, " " + defer.receivers.get(i));
- }
- }
- // Track completion refcount as well if relevant
- if (r.resultTo != null) {
- int token = r.splitToken;
- if (token == 0) {
- // first split of this record; refcount for 'r' and 'deferred'
- r.splitToken = defer.splitToken = nextSplitTokenLocked();
- mSplitRefcounts.put(r.splitToken, 2);
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST,
- "Broadcast needs split refcount; using new token "
- + r.splitToken);
- }
- } else {
- // new split from an already-refcounted situation; increment count
- final int curCount = mSplitRefcounts.get(token);
- if (DEBUG_BROADCAST_DEFERRAL) {
- if (curCount == 0) {
- Slog.wtf(TAG_BROADCAST,
- "Split refcount is zero with token for " + r);
- }
- }
- mSplitRefcounts.put(token, curCount + 1);
- if (DEBUG_BROADCAST_DEFERRAL) {
- Slog.i(TAG_BROADCAST, "New split count for token " + token
- + " is " + (curCount + 1));
- }
- }
- }
- }
- mDispatcher.addDeferredBroadcast(receiverUid, defer);
- r = null;
- looped = true;
- continue;
- }
- }
- } while (r == null);
-
- // Get the next receiver...
- int recIdx = r.nextReceiver++;
-
- // Keep track of when this receiver started, and make sure there
- // is a timeout message pending to kill it if need be.
- r.receiverTime = SystemClock.uptimeMillis();
- if (recIdx == 0) {
- r.dispatchTime = r.receiverTime;
- r.dispatchRealTime = SystemClock.elapsedRealtime();
- r.dispatchClockTime = System.currentTimeMillis();
-
- if (mLogLatencyMetrics) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
- r.dispatchClockTime - r.enqueueClockTime);
- }
-
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
- System.identityHashCode(r));
- Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
- System.identityHashCode(r));
- }
- if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
- + mQueueName + "] " + r);
- }
- if (! mPendingBroadcastTimeoutMessage) {
- long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Submitting BROADCAST_TIMEOUT_MSG ["
- + mQueueName + "] for " + r + " at " + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
- }
-
- final BroadcastOptions brOptions = r.options;
- final Object nextReceiver = r.receivers.get(recIdx);
-
- if (nextReceiver instanceof BroadcastFilter) {
- // Simple case: this is a registered receiver who gets
- // a direct call.
- BroadcastFilter filter = (BroadcastFilter)nextReceiver;
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Delivering ordered ["
- + mQueueName + "] to registered "
- + filter + ": " + r);
- r.mIsReceiverAppRunning = true;
- deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
- if (r.receiver == null || !r.ordered) {
- // The receiver has already finished, so schedule to
- // process the next one.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
- + mQueueName + "]: ordered="
- + r.ordered + " receiver=" + r.receiver);
- r.state = BroadcastRecord.IDLE;
- scheduleBroadcastsLocked();
- } else {
- if (filter.receiverList != null) {
- maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
- // r is guaranteed ordered at this point, so we know finishReceiverLocked()
- // will get a callback and handle the activity start token lifecycle.
- }
- }
- return;
- }
-
- // Hard case: need to instantiate the receiver, possibly
- // starting its application process to host it.
-
- ResolveInfo info =
- (ResolveInfo)nextReceiver;
- ComponentName component = new ComponentName(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
-
- boolean skip = false;
- if (brOptions != null &&
- (info.activityInfo.applicationInfo.targetSdkVersion
- < brOptions.getMinManifestReceiverApiLevel() ||
- info.activityInfo.applicationInfo.targetSdkVersion
- > brOptions.getMaxManifestReceiverApiLevel())) {
- Slog.w(TAG, "Target SDK mismatch: receiver " + info.activityInfo
- + " targets " + info.activityInfo.applicationInfo.targetSdkVersion
- + " but delivery restricted to ["
- + brOptions.getMinManifestReceiverApiLevel() + ", "
- + brOptions.getMaxManifestReceiverApiLevel()
- + "] broadcasting " + broadcastDescription(r, component));
- skip = true;
- }
- if (brOptions != null &&
- !brOptions.testRequireCompatChange(info.activityInfo.applicationInfo.uid)) {
- Slog.w(TAG, "Compat change filtered: broadcasting " + broadcastDescription(r, component)
- + " to uid " + info.activityInfo.applicationInfo.uid + " due to compat change "
- + r.options.getRequireCompatChangeId());
- skip = true;
- }
- if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
- component.getPackageName(), info.activityInfo.applicationInfo.uid)) {
- Slog.w(TAG, "Association not allowed: broadcasting "
- + broadcastDescription(r, component));
- skip = true;
- }
- if (!skip) {
- skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
- r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
- if (skip) {
- Slog.w(TAG, "Firewall blocked: broadcasting "
- + broadcastDescription(r, component));
- }
- }
- int perm = mService.checkComponentPermission(info.activityInfo.permission,
- r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
- info.activityInfo.exported);
- if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
- if (!info.activityInfo.exported) {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + broadcastDescription(r, component)
- + " is not exported from uid " + info.activityInfo.applicationInfo.uid);
- } else {
- Slog.w(TAG, "Permission Denial: broadcasting "
- + broadcastDescription(r, component)
- + " requires " + info.activityInfo.permission);
- }
- skip = true;
- } else if (!skip && info.activityInfo.permission != null) {
- final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
- if (opCode != AppOpsManager.OP_NONE && mService.getAppOpsManager().noteOpNoThrow(opCode,
- r.callingUid, r.callerPackage, r.callerFeatureId,
- "Broadcast delivered to " + info.activityInfo.name)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: broadcasting "
- + broadcastDescription(r, component)
- + " requires appop " + AppOpsManager.permissionToOp(
- info.activityInfo.permission));
- skip = true;
- }
- }
-
- boolean isSingleton = false;
- try {
- isSingleton = mService.isSingleton(info.activityInfo.processName,
- info.activityInfo.applicationInfo,
- info.activityInfo.name, info.activityInfo.flags);
- } catch (SecurityException e) {
- Slog.w(TAG, e.getMessage());
- skip = true;
- }
- if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
- if (ActivityManager.checkUidPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS,
- info.activityInfo.applicationInfo.uid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
- + " requests FLAG_SINGLE_USER, but app does not hold "
- + android.Manifest.permission.INTERACT_ACROSS_USERS);
- skip = true;
- }
- }
- if (!skip && info.activityInfo.applicationInfo.isInstantApp()
- && r.callingUid != info.activityInfo.applicationInfo.uid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent
- + " to " + component.flattenToShortString()
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")"
- + " Instant Apps do not support manifest receivers");
- skip = true;
- }
- if (!skip && r.callerInstantApp
- && (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0
- && r.callingUid != info.activityInfo.applicationInfo.uid) {
- Slog.w(TAG, "Instant App Denial: receiving "
- + r.intent
- + " to " + component.flattenToShortString()
- + " requires receiver have visibleToInstantApps set"
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- if (r.curApp != null && r.curApp.mErrorState.isCrashing()) {
- // If the target process is crashing, just skip it.
- Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
- + " to " + r.curApp + ": process crashing");
- skip = true;
- }
- if (!skip) {
- boolean isAvailable = false;
- try {
- isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
- info.activityInfo.packageName,
- UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
- } catch (Exception e) {
- // all such failures mean we skip this receiver
- Slog.w(TAG, "Exception getting recipient info for "
- + info.activityInfo.packageName, e);
- }
- if (!isAvailable) {
- Slog.w(TAG_BROADCAST,
- "Skipping delivery to " + info.activityInfo.packageName + " / "
- + info.activityInfo.applicationInfo.uid
- + " : package no longer available");
- skip = true;
- }
- }
-
- // If permissions need a review before any of the app components can run, we drop
- // the broadcast and if the calling app is in the foreground and the broadcast is
- // explicit we launch the review UI passing it a pending intent to send the skipped
- // broadcast.
- if (!skip) {
- if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
- info.activityInfo.packageName, UserHandle.getUserId(
- info.activityInfo.applicationInfo.uid))) {
- Slog.w(TAG_BROADCAST,
- "Skipping delivery: permission review required for "
- + broadcastDescription(r, component));
- skip = true;
- }
- }
-
- // This is safe to do even if we are skipping the broadcast, and we need
- // this information now to evaluate whether it is going to be allowed to run.
- final int receiverUid = info.activityInfo.applicationInfo.uid;
- // If it's a singleton, it needs to be the same app or a special app
- if (r.callingUid != Process.SYSTEM_UID && isSingleton
- && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
- info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
- }
- String targetProcess = info.activityInfo.processName;
- ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
- info.activityInfo.applicationInfo.uid);
-
- if (!skip) {
- final int allowed = mService.getAppStartModeLOSP(
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
- info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
- if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
- // We won't allow this receiver to be launched if the app has been
- // completely disabled from launches, or it was not explicitly sent
- // to it and the app is in a state that should not receive it
- // (depending on how getAppStartModeLOSP has determined that).
- if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
- Slog.w(TAG, "Background execution disabled: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
- || (r.intent.getComponent() == null
- && r.intent.getPackage() == null
- && ((r.intent.getFlags()
- & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
- && !isSignaturePerm(r.requiredPermissions))) {
- mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
- component.getPackageName());
- Slog.w(TAG, "Background execution not allowed: receiving "
- + r.intent + " to "
- + component.flattenToShortString());
- skip = true;
- }
- }
- }
-
- if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
- && !mService.mUserController
- .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
- 0 /* flags */)) {
- skip = true;
- Slog.w(TAG,
- "Skipping delivery to " + info.activityInfo.packageName + " / "
- + info.activityInfo.applicationInfo.uid + " : user is not running");
- }
-
- if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
- for (int i = 0; i < r.excludedPermissions.length; i++) {
- String excludedPermission = r.excludedPermissions[i];
- try {
- perm = AppGlobals.getPackageManager()
- .checkPermission(excludedPermission,
- info.activityInfo.applicationInfo.packageName,
- UserHandle
- .getUserId(info.activityInfo.applicationInfo.uid));
- } catch (RemoteException e) {
- perm = PackageManager.PERMISSION_DENIED;
- }
-
- int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
- if (appOp != AppOpsManager.OP_NONE) {
- // When there is an app op associated with the permission,
- // skip when both the permission and the app op are
- // granted.
- if ((perm == PackageManager.PERMISSION_GRANTED) && (
- mService.getAppOpsManager().checkOpNoThrow(appOp,
- info.activityInfo.applicationInfo.uid,
- info.activityInfo.packageName)
- == AppOpsManager.MODE_ALLOWED)) {
- skip = true;
- break;
- }
- } else {
- // When there is no app op associated with the permission,
- // skip when permission is granted.
- if (perm == PackageManager.PERMISSION_GRANTED) {
- skip = true;
- break;
- }
- }
- }
- }
-
- // Check that the receiver does *not* belong to any of the excluded packages
- if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) {
- if (ArrayUtils.contains(r.excludedPackages, component.getPackageName())) {
- Slog.w(TAG, "Skipping delivery of excluded package "
- + r.intent + " to "
- + component.flattenToShortString()
- + " excludes package " + component.getPackageName()
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- }
- }
-
- if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
- r.requiredPermissions != null && r.requiredPermissions.length > 0) {
- for (int i = 0; i < r.requiredPermissions.length; i++) {
- String requiredPermission = r.requiredPermissions[i];
- try {
- perm = AppGlobals.getPackageManager().
- checkPermission(requiredPermission,
- info.activityInfo.applicationInfo.packageName,
- UserHandle
- .getUserId(info.activityInfo.applicationInfo.uid));
- } catch (RemoteException e) {
- perm = PackageManager.PERMISSION_DENIED;
- }
- if (perm != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires " + requiredPermission
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- skip = true;
- break;
- }
- int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
- if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
- if (!noteOpForManifestReceiver(appOp, r, info, component)) {
- skip = true;
- break;
- }
- }
- }
- }
- if (!skip && r.appOp != AppOpsManager.OP_NONE) {
- if (!noteOpForManifestReceiver(r.appOp, r, info, component)) {
- skip = true;
- }
- }
-
- // Filter packages in the intent extras, skipping delivery if none of the packages is
- // visible to the receiver.
- Bundle filteredExtras = null;
- if (!skip && r.filterExtrasForReceiver != null) {
- final Bundle extras = r.intent.getExtras();
- if (extras != null) {
- filteredExtras = r.filterExtrasForReceiver.apply(receiverUid, extras);
- if (filteredExtras == null) {
- if (DEBUG_BROADCAST) {
- Slog.v(TAG, "Skipping delivery to "
- + info.activityInfo.packageName + " / " + receiverUid
- + " : receiver is filtered by the package visibility");
- }
- skip = true;
- }
- }
- }
-
- if (skip) {
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Skipping delivery of ordered [" + mQueueName + "] "
- + r + " for reason described above");
- r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
- r.receiver = null;
- r.curFilter = null;
- r.state = BroadcastRecord.IDLE;
- r.manifestSkipCount++;
- scheduleBroadcastsLocked();
- return;
- }
- r.manifestCount++;
-
- r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
- r.state = BroadcastRecord.APP_RECEIVE;
- r.curComponent = component;
- r.curReceiver = info.activityInfo;
- r.curFilteredExtras = filteredExtras;
- if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
- Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
- + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
- + receiverUid);
- }
- final boolean isActivityCapable =
- (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
- maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
-
- // Report that a component is used for explicit broadcasts.
- if (r.intent.getComponent() != null && r.curComponent != null
- && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
- mService.mUsageStatsService.reportEvent(
- r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
- }
-
- // Broadcast is being executed, its package can't be stopped.
- try {
- AppGlobals.getPackageManager().setPackageStoppedState(
- r.curComponent.getPackageName(), false, r.userId);
- } catch (RemoteException e) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + r.curComponent.getPackageName() + ": " + e);
- }
-
- // Is this receiver's application already running?
- if (app != null && app.getThread() != null && !app.isKilled()) {
- try {
- app.addPackage(info.activityInfo.packageName,
- info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
- maybeAddAllowBackgroundActivityStartsToken(app, r);
- r.mIsReceiverAppRunning = true;
- processCurBroadcastLocked(r, app);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception when sending broadcast to "
- + r.curComponent, e);
- } catch (RuntimeException e) {
- Slog.wtf(TAG, "Failed sending broadcast to "
- + r.curComponent + " with " + r.intent, e);
- // If some unexpected exception happened, just skip
- // this broadcast. At this point we are not in the call
- // from a client, so throwing an exception out from here
- // will crash the entire system instead of just whoever
- // sent the broadcast.
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- // We need to reset the state if we failed to start the receiver.
- r.state = BroadcastRecord.IDLE;
- return;
- }
-
- // If a dead object exception was thrown -- fall through to
- // restart the application.
- }
-
- // Not running -- get it started, to be executed when the app comes up.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Need to start app ["
- + mQueueName + "] " + targetProcess + " for broadcast " + r);
- r.curApp = mService.startProcessLocked(targetProcess,
- info.activityInfo.applicationInfo, true,
- r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
- r.intent.getAction(), getHostingRecordTriggerType(r)),
- isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
- (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
- if (r.curApp == null) {
- // Ah, this recipient is unavailable. Finish it if necessary,
- // and mark the broadcast record as ready for the next.
- Slog.w(TAG, "Unable to launch app "
- + info.activityInfo.applicationInfo.packageName + "/"
- + receiverUid + " for broadcast "
- + r.intent + ": process is bad");
- logBroadcastReceiverDiscardLocked(r);
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
- r.state = BroadcastRecord.IDLE;
- return;
- }
-
- maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
- mPendingBroadcast = r;
- mPendingBroadcastRecvIndex = recIdx;
- }
-
- private String getHostingRecordTriggerType(BroadcastRecord r) {
- if (r.alarm) {
- return HostingRecord.TRIGGER_TYPE_ALARM;
- } else if (r.pushMessage) {
- return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE;
- } else if (r.pushMessageOverQuota) {
- return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
- }
- return HostingRecord.TRIGGER_TYPE_UNKNOWN;
- }
-
- @Nullable
- private String getTargetPackage(BroadcastRecord r) {
- if (r.intent == null) {
- return null;
- }
- if (r.intent.getPackage() != null) {
- return r.intent.getPackage();
- } else if (r.intent.getComponent() != null) {
- return r.intent.getComponent().getPackageName();
- }
- return null;
- }
-
- private void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
- // Only log after last receiver.
- // In case of split BOOT_COMPLETED broadcast, make sure only call this method on the
- // last BroadcastRecord of the split broadcast which has non-null resultTo.
- final int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
- if (r.nextReceiver < numReceivers) {
- return;
- }
- final String action = r.intent.getAction();
- int event = 0;
- if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
- event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
- } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
- }
- if (event != 0) {
- final int dispatchLatency = (int)(r.dispatchTime - r.enqueueTime);
- final int completeLatency = (int)
- (SystemClock.uptimeMillis() - r.enqueueTime);
- final int dispatchRealLatency = (int)(r.dispatchRealTime - r.enqueueRealTime);
- final int completeRealLatency = (int)
- (SystemClock.elapsedRealtime() - r.enqueueRealTime);
- int userType = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
- // This method is called very infrequently, no performance issue we call
- // LocalServices.getService() here.
- final UserManagerInternal umInternal = LocalServices.getService(
- UserManagerInternal.class);
- final UserInfo userInfo = umInternal.getUserInfo(r.userId);
- if (userInfo != null) {
- userType = UserManager.getUserTypeForStatsd(userInfo.userType);
- }
- Slog.i(TAG_BROADCAST,
- "BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:"
- + action
- + " dispatchLatency:" + dispatchLatency
- + " completeLatency:" + completeLatency
- + " dispatchRealLatency:" + dispatchRealLatency
- + " completeRealLatency:" + completeRealLatency
- + " receiversSize:" + numReceivers
- + " userId:" + r.userId
- + " userType:" + (userInfo != null? userInfo.userType : null));
- FrameworkStatsLog.write(
- BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED,
- event,
- dispatchLatency,
- completeLatency,
- dispatchRealLatency,
- completeRealLatency,
- r.userId,
- userType);
- }
- }
-
- private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
- if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
- return;
- }
- final String targetPackage = getTargetPackage(r);
- // Ignore non-explicit broadcasts
- if (targetPackage == null) {
- return;
- }
- getUsageStatsManagerInternal().reportBroadcastDispatched(
- r.callingUid, targetPackage, UserHandle.of(r.userId),
- r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
- mService.getUidStateLocked(targetUid));
- }
-
- @NonNull
- private UsageStatsManagerInternal getUsageStatsManagerInternal() {
- final UsageStatsManagerInternal usageStatsManagerInternal =
- LocalServices.getService(UsageStatsManagerInternal.class);
- return usageStatsManagerInternal;
- }
-
- private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
- ComponentName component) {
- if (ArrayUtils.isEmpty(info.activityInfo.attributionTags)) {
- return noteOpForManifestReceiverInner(appOp, r, info, component, null);
- } else {
- // Attribution tags provided, noteOp each tag
- for (String tag : info.activityInfo.attributionTags) {
- if (!noteOpForManifestReceiverInner(appOp, r, info, component, tag)) {
- return false;
- }
- }
- return true;
- }
- }
-
- private boolean noteOpForManifestReceiverInner(int appOp, BroadcastRecord r, ResolveInfo info,
- ComponentName component, String tag) {
- if (mService.getAppOpsManager().noteOpNoThrow(appOp,
- info.activityInfo.applicationInfo.uid,
- info.activityInfo.packageName,
- tag,
- "Broadcast delivered to " + info.activityInfo.name)
- != AppOpsManager.MODE_ALLOWED) {
- Slog.w(TAG, "Appop Denial: receiving "
- + r.intent + " to "
- + component.flattenToShortString()
- + " requires appop " + AppOpsManager.opToName(appOp)
- + " due to sender " + r.callerPackage
- + " (uid " + r.callingUid + ")");
- return false;
- }
- return true;
- }
-
- private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
- if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
- return;
- }
- String msgToken = (proc.toShortString() + r.toString()).intern();
- // first, if there exists a past scheduled request to remove this token, drop
- // that request - we don't want the token to be swept from under our feet...
- mHandler.removeCallbacksAndMessages(msgToken);
- // ...then add the token
- proc.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
- }
-
- final void setBroadcastTimeoutLocked(long timeoutTime) {
- if (! mPendingBroadcastTimeoutMessage) {
- Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
- mHandler.sendMessageAtTime(msg, timeoutTime);
- mPendingBroadcastTimeoutMessage = true;
- }
- }
-
- final void cancelBroadcastTimeoutLocked() {
- if (mPendingBroadcastTimeoutMessage) {
- mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
- mPendingBroadcastTimeoutMessage = false;
- }
- }
-
- final void broadcastTimeoutLocked(boolean fromMsg) {
- if (fromMsg) {
- mPendingBroadcastTimeoutMessage = false;
- }
-
- if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
- return;
- }
-
- long now = SystemClock.uptimeMillis();
- BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
- if (fromMsg) {
- if (!mService.mProcessesReady) {
- // Only process broadcast timeouts if the system is ready; some early
- // broadcasts do heavy work setting up system facilities
- return;
- }
-
- // If the broadcast is generally exempt from timeout tracking, we're done
- if (r.timeoutExempt) {
- if (DEBUG_BROADCAST) {
- Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
- + r.intent.getAction());
- }
- return;
- }
-
- long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
- if (timeoutTime > now) {
- // We can observe premature timeouts because we do not cancel and reset the
- // broadcast timeout message after each receiver finishes. Instead, we set up
- // an initial timeout then kick it down the road a little further as needed
- // when it expires.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Premature timeout ["
- + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
- + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
- return;
- }
- }
-
- if (r.state == BroadcastRecord.WAITING_SERVICES) {
- // In this case the broadcast had already finished, but we had decided to wait
- // for started services to finish as well before going on. So if we have actually
- // waited long enough time timeout the broadcast, let's give up on the whole thing
- // and just move on to the next.
- Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
- ? r.curComponent.flattenToShortString() : "(null)"));
- r.curComponent = null;
- r.state = BroadcastRecord.IDLE;
- processNextBroadcastLocked(false, false);
- return;
- }
-
- // If the receiver app is being debugged we quietly ignore unresponsiveness, just
- // tidying up and moving on to the next broadcast without crashing or ANRing this
- // app just because it's stopped at a breakpoint.
- final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
-
- long timeoutDurationMs = now - r.receiverTime;
- Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
- + ", started " + timeoutDurationMs + "ms ago");
- r.receiverTime = now;
- if (!debugging) {
- r.anrCount++;
- }
-
- ProcessRecord app = null;
- TimeoutRecord timeoutRecord = null;
-
- Object curReceiver;
- if (r.nextReceiver > 0) {
- curReceiver = r.receivers.get(r.nextReceiver-1);
- r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
- } else {
- curReceiver = r.curReceiver;
- }
- Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
- logBroadcastReceiverDiscardLocked(r);
- if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter)curReceiver;
- if (bf.receiverList.pid != 0
- && bf.receiverList.pid != ActivityManagerService.MY_PID) {
- synchronized (mService.mPidsSelfLocked) {
- app = mService.mPidsSelfLocked.get(
- bf.receiverList.pid);
- }
- }
- } else {
- app = r.curApp;
- }
-
- if (app != null) {
- String anrMessage =
- "Broadcast of " + r.intent.toString() + ", waited " + timeoutDurationMs
- + "ms";
- timeoutRecord = TimeoutRecord.forBroadcastReceiver(anrMessage);
- }
-
- if (mPendingBroadcast == r) {
- mPendingBroadcast = null;
- }
-
- // Move on to the next receiver.
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
-
- if (!debugging && timeoutRecord != null) {
- mService.mAnrHelper.appNotResponding(app, timeoutRecord);
- }
- }
-
- private final int ringAdvance(int x, final int increment, final int ringSize) {
- x += increment;
- if (x < 0) return (ringSize - 1);
- else if (x >= ringSize) return 0;
- else return x;
- }
-
- private final void addBroadcastToHistoryLocked(BroadcastRecord original) {
- if (original.callingUid < 0) {
- // This was from a registerReceiver() call; ignore it.
- return;
- }
- original.finishTime = SystemClock.uptimeMillis();
-
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
- createBroadcastTraceTitle(original, BroadcastRecord.DELIVERY_DELIVERED),
- System.identityHashCode(original));
- }
-
- final ApplicationInfo info = original.callerApp != null ? original.callerApp.info : null;
- final String callerPackage = info != null ? info.packageName : original.callerPackage;
- if (callerPackage != null) {
- mService.mHandler.obtainMessage(ActivityManagerService.DISPATCH_SENDING_BROADCAST_EVENT,
- original.callingUid, 0, callerPackage).sendToTarget();
- }
-
- // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords,
- // So don't change the incoming record directly.
- final BroadcastRecord historyRecord = original.maybeStripForHistory();
-
- mBroadcastHistory[mHistoryNext] = historyRecord;
- mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY);
-
- mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent;
- mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime;
- mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime;
- mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis();
- mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY);
- }
-
- boolean cleanupDisabledPackageReceiversLocked(
- String packageName, Set<String> filterByClasses, int userId, boolean doit) {
- boolean didSomething = false;
- for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
- didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
- packageName, filterByClasses, userId, doit);
- if (!doit && didSomething) {
- return true;
- }
- }
-
- didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
- filterByClasses, userId, doit);
-
- return didSomething;
- }
-
- final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
- final int logIndex = r.nextReceiver - 1;
- if (logIndex >= 0 && logIndex < r.receivers.size()) {
- Object curReceiver = r.receivers.get(logIndex);
- if (curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter) curReceiver;
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
- bf.owningUserId, System.identityHashCode(r),
- r.intent.getAction(), logIndex, System.identityHashCode(bf));
- } else {
- ResolveInfo ri = (ResolveInfo) curReceiver;
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
- UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
- System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
- }
- } else {
- if (logIndex < 0) Slog.w(TAG,
- "Discarding broadcast before first receiver is invoked: " + r);
- EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
- -1, System.identityHashCode(r),
- r.intent.getAction(),
- r.nextReceiver,
- "NONE");
- }
- }
-
- private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
- return formatSimple("Broadcast %s from %s (%s) %s",
- state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
- record.callerPackage == null ? "" : record.callerPackage,
- record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
- record.intent == null ? "" : record.intent.getAction());
- }
-
- boolean isIdle() {
- return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle()
- && (mPendingBroadcast == null);
- }
-
- // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
- // be immediately deliverable.
- void cancelDeferrals() {
- synchronized (mService) {
- mDispatcher.cancelDeferralsLocked();
- scheduleBroadcastsLocked();
- }
- }
-
- String describeState() {
- synchronized (mService) {
- return mParallelBroadcasts.size() + " parallel; "
- + mDispatcher.describeStateLocked();
- }
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- long token = proto.start(fieldId);
- proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
- int N;
- N = mParallelBroadcasts.size();
- for (int i = N - 1; i >= 0; i--) {
- mParallelBroadcasts.get(i).dumpDebug(proto, BroadcastQueueProto.PARALLEL_BROADCASTS);
- }
- mDispatcher.dumpDebug(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
- if (mPendingBroadcast != null) {
- mPendingBroadcast.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCAST);
- }
-
- int lastIndex = mHistoryNext;
- int ringIndex = lastIndex;
- do {
- // increasing index = more recent entry, and we want to print the most
- // recent first and work backwards, so we roll through the ring backwards.
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r != null) {
- r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS);
- }
- } while (ringIndex != lastIndex);
-
- lastIndex = ringIndex = mSummaryHistoryNext;
- do {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- Intent intent = mBroadcastSummaryHistory[ringIndex];
- if (intent == null) {
- continue;
- }
- long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY);
- intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT,
- false, true, true, false);
- proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS,
- mSummaryHistoryEnqueueTime[ringIndex]);
- proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS,
- mSummaryHistoryDispatchTime[ringIndex]);
- proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS,
- mSummaryHistoryFinishTime[ringIndex]);
- proto.end(summaryToken);
- } while (ringIndex != lastIndex);
- proto.end(token);
- }
-
- final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- if (!mParallelBroadcasts.isEmpty() || !mDispatcher.isEmpty()
- || mPendingBroadcast != null) {
- boolean printed = false;
- for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
- BroadcastRecord br = mParallelBroadcasts.get(i);
- if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- printed = true;
- pw.println(" Active broadcasts [" + mQueueName + "]:");
- }
- pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
- br.dump(pw, " ", sdf);
- }
-
- mDispatcher.dumpLocked(pw, dumpPackage, mQueueName, sdf);
-
- if (dumpPackage == null || (mPendingBroadcast != null
- && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
- pw.println();
- pw.println(" Pending broadcast [" + mQueueName + "]:");
- if (mPendingBroadcast != null) {
- mPendingBroadcast.dump(pw, " ", sdf);
- } else {
- pw.println(" (null)");
- }
- needSep = true;
- }
- }
-
- mConstants.dump(pw);
-
- int i;
- boolean printed = false;
-
- i = -1;
- int lastIndex = mHistoryNext;
- int ringIndex = lastIndex;
- do {
- // increasing index = more recent entry, and we want to print the most
- // recent first and work backwards, so we roll through the ring backwards.
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r == null) {
- continue;
- }
-
- i++; // genuine record of some sort even if we're filtering it out
- if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- pw.println(" Historical broadcasts [" + mQueueName + "]:");
- printed = true;
- }
- if (dumpAll) {
- pw.print(" Historical Broadcast " + mQueueName + " #");
- pw.print(i); pw.println(":");
- r.dump(pw, " ", sdf);
- } else {
- pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
- pw.print(" ");
- pw.println(r.intent.toShortString(false, true, true, false));
- if (r.targetComp != null && r.targetComp != r.intent.getComponent()) {
- pw.print(" targetComp: "); pw.println(r.targetComp.toShortString());
- }
- Bundle bundle = r.intent.getExtras();
- if (bundle != null) {
- pw.print(" extras: "); pw.println(bundle.toString());
- }
- }
- } while (ringIndex != lastIndex);
-
- if (dumpPackage == null) {
- lastIndex = ringIndex = mSummaryHistoryNext;
- if (dumpAll) {
- printed = false;
- i = -1;
- } else {
- // roll over the 'i' full dumps that have already been issued
- for (int j = i;
- j > 0 && ringIndex != lastIndex;) {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- BroadcastRecord r = mBroadcastHistory[ringIndex];
- if (r == null) {
- continue;
- }
- j--;
- }
- }
- // done skipping; dump the remainder of the ring. 'i' is still the ordinal within
- // the overall broadcast history.
- do {
- ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
- Intent intent = mBroadcastSummaryHistory[ringIndex];
- if (intent == null) {
- continue;
- }
- if (!printed) {
- if (needSep) {
- pw.println();
- }
- needSep = true;
- pw.println(" Historical broadcasts summary [" + mQueueName + "]:");
- printed = true;
- }
- if (!dumpAll && i >= 50) {
- pw.println(" ...");
- break;
- }
- i++;
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(intent.toShortString(false, true, true, false));
- pw.print(" ");
- TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
- - mSummaryHistoryEnqueueTime[ringIndex], pw);
- pw.print(" dispatch ");
- TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
- - mSummaryHistoryDispatchTime[ringIndex], pw);
- pw.println(" finish");
- pw.print(" enq=");
- pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
- pw.print(" disp=");
- pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
- pw.print(" fin=");
- pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
- Bundle bundle = intent.getExtras();
- if (bundle != null) {
- pw.print(" extras: "); pw.println(bundle.toString());
- }
- } while (ringIndex != lastIndex);
- }
-
- return needSep;
- }
+ public abstract boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage, boolean needSep);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
new file mode 100644
index 0000000..73a413a
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -0,0 +1,2575 @@
+/*
+ * Copyright (C) 2012 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.server.am;
+
+import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+import static android.text.TextUtils.formatSimple;
+
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_START_RECEIVER;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
+import android.app.IApplicationThread;
+import android.app.PendingIntent;
+import android.app.RemoteServiceException.CannotDeliverBroadcastException;
+import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerExemptionManager.ReasonCode;
+import android.os.PowerExemptionManager.TempAllowListType;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.permission.IPermissionManager;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.os.TimeoutRecord;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * BROADCASTS
+ *
+ * We keep three broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts, and one to
+ * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
+ */
+public class BroadcastQueueImpl extends BroadcastQueue {
+ private static final String TAG = "BroadcastQueue";
+ private static final String TAG_MU = TAG + POSTFIX_MU;
+ private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
+
+ static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50;
+ static final int MAX_BROADCAST_SUMMARY_HISTORY
+ = ActivityManager.isLowRamDeviceStatic() ? 25 : 300;
+
+ /**
+ * If true, we can delay broadcasts while waiting services to finish in the previous
+ * receiver's process.
+ */
+ final boolean mDelayBehindServices;
+
+ /**
+ * Lists of all active broadcasts that are to be executed immediately
+ * (without waiting for another broadcast to finish). Currently this only
+ * contains broadcasts to registered receivers, to avoid spinning up
+ * a bunch of processes to execute IntentReceiver components. Background-
+ * and foreground-priority broadcasts are queued separately.
+ */
+ final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
+
+ /**
+ * Tracking of the ordered broadcast queue, including deferral policy and alarm
+ * prioritization.
+ */
+ final BroadcastDispatcher mDispatcher;
+
+ /**
+ * Refcounting for completion callbacks of split/deferred broadcasts. The key
+ * is an opaque integer token assigned lazily when a broadcast is first split
+ * into multiple BroadcastRecord objects.
+ */
+ final SparseIntArray mSplitRefcounts = new SparseIntArray();
+ private int mNextToken = 0;
+
+ /**
+ * Historical data of past broadcasts, for debugging. This is a ring buffer
+ * whose last element is at mHistoryNext.
+ */
+ final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY];
+ int mHistoryNext = 0;
+
+ /**
+ * Summary of historical data of past broadcasts, for debugging. This is a
+ * ring buffer whose last element is at mSummaryHistoryNext.
+ */
+ final Intent[] mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
+ int mSummaryHistoryNext = 0;
+
+ /**
+ * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring
+ * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall
+ * clock time, not elapsed.
+ */
+ final long[] mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+ final long[] mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+ final long[] mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY];
+
+ /**
+ * Set when we current have a BROADCAST_INTENT_MSG in flight.
+ */
+ boolean mBroadcastsScheduled = false;
+
+ /**
+ * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
+ */
+ boolean mPendingBroadcastTimeoutMessage;
+
+ /**
+ * Intent broadcasts that we have tried to start, but are
+ * waiting for the application's process to be created. We only
+ * need one per scheduling class (instead of a list) because we always
+ * process broadcasts one at a time, so no others can be started while
+ * waiting for this one.
+ */
+ BroadcastRecord mPendingBroadcast = null;
+
+ /**
+ * The receiver index that is pending, to restart the broadcast if needed.
+ */
+ int mPendingBroadcastRecvIndex;
+
+ static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
+ static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
+
+ // log latency metrics for ordered broadcasts during BOOT_COMPLETED processing
+ boolean mLogLatencyMetrics = true;
+
+ final BroadcastHandler mHandler;
+
+ private final class BroadcastHandler extends Handler {
+ public BroadcastHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case BROADCAST_INTENT_MSG: {
+ if (DEBUG_BROADCAST) Slog.v(
+ TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
+ + mQueueName + "]");
+ processNextBroadcast(true);
+ } break;
+ case BROADCAST_TIMEOUT_MSG: {
+ synchronized (mService) {
+ broadcastTimeoutLocked(true);
+ }
+ } break;
+ }
+ }
+ }
+
+ BroadcastQueueImpl(ActivityManagerService service, Handler handler,
+ String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
+ super(service, handler, name, constants);
+ mHandler = new BroadcastHandler(handler.getLooper());
+ mDelayBehindServices = allowDelayBehindServices;
+ mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
+ }
+
+ void start(ContentResolver resolver) {
+ mDispatcher.start();
+ mConstants.startObserving(mHandler, resolver);
+ }
+
+ @Override
+ public String toString() {
+ return mQueueName;
+ }
+
+ public boolean isDelayBehindServices() {
+ return mDelayBehindServices;
+ }
+
+ public boolean hasBroadcastsScheduled() {
+ return mBroadcastsScheduled;
+ }
+
+ public BroadcastRecord getPendingBroadcastLocked() {
+ return mPendingBroadcast;
+ }
+
+ public BroadcastRecord getActiveBroadcastLocked() {
+ return mDispatcher.getActiveBroadcastLocked();
+ }
+
+ public boolean isPendingBroadcastProcessLocked(int pid) {
+ return mPendingBroadcast != null && mPendingBroadcast.curApp.getPid() == pid;
+ }
+
+ public boolean isPendingBroadcastProcessLocked(ProcessRecord app) {
+ return mPendingBroadcast != null && mPendingBroadcast.curApp == app;
+ }
+
+ public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
+ r.enqueueClockTime = System.currentTimeMillis();
+ r.enqueueTime = SystemClock.uptimeMillis();
+ r.enqueueRealTime = SystemClock.elapsedRealtime();
+ mParallelBroadcasts.add(r);
+ enqueueBroadcastHelper(r);
+ }
+
+ public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+ r.enqueueClockTime = System.currentTimeMillis();
+ r.enqueueTime = SystemClock.uptimeMillis();
+ r.enqueueRealTime = SystemClock.elapsedRealtime();
+ mDispatcher.enqueueOrderedBroadcastLocked(r);
+ enqueueBroadcastHelper(r);
+ }
+
+ /**
+ * Don't call this method directly; call enqueueParallelBroadcastLocked or
+ * enqueueOrderedBroadcastLocked.
+ */
+ private void enqueueBroadcastHelper(BroadcastRecord r) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
+ System.identityHashCode(r));
+ }
+ }
+
+ /**
+ * Find the same intent from queued parallel broadcast, replace with a new one and return
+ * the old one.
+ */
+ public final BroadcastRecord replaceParallelBroadcastLocked(BroadcastRecord r) {
+ return replaceBroadcastLocked(mParallelBroadcasts, r, "PARALLEL");
+ }
+
+ /**
+ * Find the same intent from queued ordered broadcast, replace with a new one and return
+ * the old one.
+ */
+ public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) {
+ return mDispatcher.replaceBroadcastLocked(r, "ORDERED");
+ }
+
+ private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue,
+ BroadcastRecord r, String typeForLogging) {
+ final Intent intent = r.intent;
+ for (int i = queue.size() - 1; i > 0; i--) {
+ final BroadcastRecord old = queue.get(i);
+ if (old.userId == r.userId && intent.filterEquals(old.intent)) {
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG_BROADCAST, "***** DROPPING "
+ + typeForLogging + " [" + mQueueName + "]: " + intent);
+ }
+ queue.set(i, r);
+ return old;
+ }
+ }
+ return null;
+ }
+
+ private final void processCurBroadcastLocked(BroadcastRecord r,
+ ProcessRecord app) throws RemoteException {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Process cur broadcast " + r + " for app " + app);
+ final IApplicationThread thread = app.getThread();
+ if (thread == null) {
+ throw new RemoteException();
+ }
+ if (app.isInFullBackup()) {
+ skipReceiverLocked(r);
+ return;
+ }
+
+ r.receiver = thread.asBinder();
+ r.curApp = app;
+ final ProcessReceiverRecord prr = app.mReceivers;
+ prr.addCurReceiver(r);
+ app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
+ // Don't bump its LRU position if it's in the background restricted.
+ if (mService.mInternal.getRestrictionLevel(app.info.packageName, app.userId)
+ < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
+ mService.updateLruProcessLocked(app, false, null);
+ }
+ // Make sure the oom adj score is updated before delivering the broadcast.
+ // Force an update, even if there are other pending requests, overall it still saves time,
+ // because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
+ mService.enqueueOomAdjTargetLocked(app);
+ mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
+
+ // Tell the application to launch this receiver.
+ maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
+ r.intent.setComponent(r.curComponent);
+
+ boolean started = false;
+ try {
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
+ "Delivering to component " + r.curComponent
+ + ": " + r);
+ mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
+ PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
+ thread.scheduleReceiver(prepareReceiverIntent(r.intent, r.curFilteredExtras),
+ r.curReceiver, null /* compatInfo (unused but need to keep method signature) */,
+ r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
+ app.mState.getReportedProcState());
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Process cur broadcast " + r + " DELIVERED for app " + app);
+ started = true;
+ } finally {
+ if (!started) {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Process cur broadcast " + r + ": NOT STARTED!");
+ r.receiver = null;
+ r.curApp = null;
+ prr.removeCurReceiver(r);
+ }
+ }
+
+ // if something bad happens here, launch the app and try again
+ if (app.isKilled()) {
+ throw new RemoteException("app gets killed during broadcasting");
+ }
+ }
+
+ /**
+ * Called by ActivityManagerService to notify that the uid has process started, if there is any
+ * deferred BOOT_COMPLETED broadcast, the BroadcastDispatcher can dispatch the broadcast now.
+ * @param uid
+ */
+ public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
+ mDispatcher.updateUidReadyForBootCompletedBroadcastLocked(uid);
+ }
+
+ public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
+ boolean didSomething = false;
+ final BroadcastRecord br = mPendingBroadcast;
+ if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
+ if (br.curApp != app) {
+ Slog.e(TAG, "App mismatch when sending pending broadcast to "
+ + app.processName + ", intended target is " + br.curApp.processName);
+ return false;
+ }
+ try {
+ mPendingBroadcast = null;
+ br.mIsReceiverAppRunning = false;
+ processCurBroadcastLocked(br, app);
+ didSomething = true;
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception in new application when starting receiver "
+ + br.curComponent.flattenToShortString(), e);
+ logBroadcastReceiverDiscardLocked(br);
+ finishReceiverLocked(br, br.resultCode, br.resultData,
+ br.resultExtras, br.resultAbort, false);
+ scheduleBroadcastsLocked();
+ // We need to reset the state if we failed to start the receiver.
+ br.state = BroadcastRecord.IDLE;
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+ return didSomething;
+ }
+
+ public void skipPendingBroadcastLocked(int pid) {
+ final BroadcastRecord br = mPendingBroadcast;
+ if (br != null && br.curApp.getPid() == pid) {
+ br.state = BroadcastRecord.IDLE;
+ br.nextReceiver = mPendingBroadcastRecvIndex;
+ mPendingBroadcast = null;
+ scheduleBroadcastsLocked();
+ }
+ }
+
+ // Skip the current receiver, if any, that is in flight to the given process
+ public void skipCurrentReceiverLocked(ProcessRecord app) {
+ BroadcastRecord r = null;
+ final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
+ if (curActive != null && curActive.curApp == app) {
+ // confirmed: the current active broadcast is to the given app
+ r = curActive;
+ }
+
+ // If the current active broadcast isn't this BUT we're waiting for
+ // mPendingBroadcast to spin up the target app, that's what we use.
+ if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "[" + mQueueName + "] skip & discard pending app " + r);
+ r = mPendingBroadcast;
+ }
+
+ if (r != null) {
+ skipReceiverLocked(r);
+ }
+ }
+
+ private void skipReceiverLocked(BroadcastRecord r) {
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ }
+
+ public void scheduleBroadcastsLocked() {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ + mQueueName + "]: current="
+ + mBroadcastsScheduled);
+
+ if (mBroadcastsScheduled) {
+ return;
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
+ mBroadcastsScheduled = true;
+ }
+
+ public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) {
+ BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
+ if (br != null && br.receiver == receiver) {
+ return br;
+ }
+ return null;
+ }
+
+ // > 0 only, no worry about "eventual" recycling
+ private int nextSplitTokenLocked() {
+ int next = mNextToken + 1;
+ if (next <= 0) {
+ next = 1;
+ }
+ mNextToken = next;
+ return next;
+ }
+
+ private void postActivityStartTokenRemoval(ProcessRecord app, BroadcastRecord r) {
+ // the receiver had run for less than allowed bg activity start timeout,
+ // so allow the process to still start activities from bg for some more time
+ String msgToken = (app.toShortString() + r.toString()).intern();
+ // first, if there exists a past scheduled request to remove this token, drop
+ // that request - we don't want the token to be swept from under our feet...
+ mHandler.removeCallbacksAndMessages(msgToken);
+ // ...then schedule the removal of the token after the extended timeout
+ mHandler.postAtTime(() -> {
+ synchronized (mService) {
+ app.removeAllowBackgroundActivityStartsToken(r);
+ }
+ }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
+ }
+
+ public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
+ String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
+ final int state = r.state;
+ final ActivityInfo receiver = r.curReceiver;
+ final long finishTime = SystemClock.uptimeMillis();
+ final long elapsed = finishTime - r.receiverTime;
+ r.state = BroadcastRecord.IDLE;
+ final int curIndex = r.nextReceiver - 1;
+ if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
+ final Object curReceiver = r.receivers.get(curIndex);
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
+ r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
+ ActivityManagerService.getShortAction(r.intent.getAction()),
+ curReceiver instanceof BroadcastFilter
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
+ : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
+ r.mIsReceiverAppRunning
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM
+ : BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
+ r.dispatchTime - r.enqueueTime,
+ r.receiverTime - r.dispatchTime,
+ finishTime - r.receiverTime);
+ }
+ if (state == BroadcastRecord.IDLE) {
+ Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
+ }
+ if (r.allowBackgroundActivityStarts && r.curApp != null) {
+ if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
+ // if the receiver has run for more than allowed bg activity start timeout,
+ // just remove the token for this process now and we're done
+ r.curApp.removeAllowBackgroundActivityStartsToken(r);
+ } else {
+ // It gets more time; post the removal to happen at the appropriate moment
+ postActivityStartTokenRemoval(r.curApp, r);
+ }
+ }
+ // If we're abandoning this broadcast before any receivers were actually spun up,
+ // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
+ if (r.nextReceiver > 0) {
+ r.duration[r.nextReceiver - 1] = elapsed;
+ }
+
+ // if this receiver was slow, impose deferral policy on the app. This will kick in
+ // when processNextBroadcastLocked() next finds this uid as a receiver identity.
+ if (!r.timeoutExempt) {
+ // r.curApp can be null if finish has raced with process death - benign
+ // edge case, and we just ignore it because we're already cleaning up
+ // as expected.
+ if (r.curApp != null
+ && mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
+ // Core system packages are exempt from deferral policy
+ if (!UserHandle.isCore(r.curApp.uid)) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1)
+ + " was slow: " + receiver + " br=" + r);
+ }
+ mDispatcher.startDeferring(r.curApp.uid);
+ } else {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid
+ + " receiver was slow but not deferring: "
+ + receiver + " br=" + r);
+ }
+ }
+ }
+ } else {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Finished broadcast " + r.intent.getAction()
+ + " is exempt from deferral policy");
+ }
+ }
+
+ r.receiver = null;
+ r.intent.setComponent(null);
+ if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
+ r.curApp.mReceivers.removeCurReceiver(r);
+ mService.enqueueOomAdjTargetLocked(r.curApp);
+ }
+ if (r.curFilter != null) {
+ r.curFilter.receiverList.curBroadcast = null;
+ }
+ r.curFilter = null;
+ r.curReceiver = null;
+ r.curApp = null;
+ r.curFilteredExtras = null;
+ mPendingBroadcast = null;
+
+ r.resultCode = resultCode;
+ r.resultData = resultData;
+ r.resultExtras = resultExtras;
+ if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
+ r.resultAbort = resultAbort;
+ } else {
+ r.resultAbort = false;
+ }
+
+ // If we want to wait behind services *AND* we're finishing the head/
+ // active broadcast on its queue
+ if (waitForServices && r.curComponent != null && r.queue.isDelayBehindServices()
+ && r.queue.getActiveBroadcastLocked() == r) {
+ ActivityInfo nextReceiver;
+ if (r.nextReceiver < r.receivers.size()) {
+ Object obj = r.receivers.get(r.nextReceiver);
+ nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
+ } else {
+ nextReceiver = null;
+ }
+ // Don't do this if the next receive is in the same process as the current one.
+ if (receiver == null || nextReceiver == null
+ || receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
+ || !receiver.processName.equals(nextReceiver.processName)) {
+ // In this case, we are ready to process the next receiver for the current broadcast,
+ // but are on a queue that would like to wait for services to finish before moving
+ // on. If there are background services currently starting, then we will go into a
+ // special state where we hold off on continuing this broadcast until they are done.
+ if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
+ Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());
+ r.state = BroadcastRecord.WAITING_SERVICES;
+ return false;
+ }
+ }
+ }
+
+ r.curComponent = null;
+
+ // We will process the next receiver right now if this is finishing
+ // an app receiver (which is always asynchronous) or after we have
+ // come back from calling a receiver.
+ return state == BroadcastRecord.APP_RECEIVE
+ || state == BroadcastRecord.CALL_DONE_RECEIVE;
+ }
+
+ public void backgroundServicesFinishedLocked(int userId) {
+ BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
+ if (br != null) {
+ if (br.userId == userId && br.state == BroadcastRecord.WAITING_SERVICES) {
+ Slog.i(TAG, "Resuming delayed broadcast");
+ br.curComponent = null;
+ br.state = BroadcastRecord.IDLE;
+ processNextBroadcastLocked(false, false);
+ }
+ }
+ }
+
+ public void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
+ Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered, boolean sticky, int sendingUser,
+ int receiverUid, int callingUid, long dispatchDelay,
+ long receiveDelay) throws RemoteException {
+ // Send the intent to the receiver asynchronously using one-way binder calls.
+ if (app != null) {
+ final IApplicationThread thread = app.getThread();
+ if (thread != null) {
+ // If we have an app thread, do the call through that so it is
+ // correctly ordered with other one-way calls.
+ try {
+ thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
+ data, extras, ordered, sticky, sendingUser,
+ app.mState.getReportedProcState());
+ // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
+ // DeadObjectException when the process isn't actually dead.
+ //} catch (DeadObjectException ex) {
+ // Failed to call into the process. It's dying so just let it die and move on.
+ // throw ex;
+ } catch (RemoteException ex) {
+ // Failed to call into the process. It's either dying or wedged. Kill it gently.
+ synchronized (mService) {
+ Slog.w(TAG, "Can't deliver broadcast to " + app.processName
+ + " (pid " + app.getPid() + "). Crashing it.");
+ app.scheduleCrashLocked("can't deliver broadcast",
+ CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
+ }
+ throw ex;
+ }
+ } else {
+ // Application has died. Receiver doesn't exist.
+ throw new RemoteException("app.thread must not be null");
+ }
+ } else {
+ receiver.performReceive(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
+ }
+ if (!ordered) {
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
+ receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
+ callingUid == -1 ? Process.SYSTEM_UID : callingUid,
+ ActivityManagerService.getShortAction(intent.getAction()),
+ BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
+ BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
+ dispatchDelay, receiveDelay, 0 /* finish_delay */);
+ }
+ }
+
+ private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
+ BroadcastFilter filter, boolean ordered, int index) {
+ boolean skip = false;
+ if (r.options != null && !r.options.testRequireCompatChange(filter.owningUid)) {
+ Slog.w(TAG, "Compat change filtered: broadcasting " + r.intent.toString()
+ + " to uid " + filter.owningUid + " due to compat change "
+ + r.options.getRequireCompatChangeId());
+ skip = true;
+ }
+ if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
+ filter.packageName, filter.owningUid)) {
+ Slog.w(TAG, "Association not allowed: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
+ + filter);
+ skip = true;
+ }
+ if (!skip && !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+ r.callingPid, r.resolvedType, filter.receiverList.uid)) {
+ Slog.w(TAG, "Firewall blocked: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
+ + filter);
+ skip = true;
+ }
+ // Check that the sender has permission to send to this receiver
+ if (filter.requiredPermission != null) {
+ int perm = mService.checkComponentPermission(filter.requiredPermission,
+ r.callingPid, r.callingUid, -1, true);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid="
+ + r.callingPid + ", uid=" + r.callingUid + ")"
+ + " requires " + filter.requiredPermission
+ + " due to registered receiver " + filter);
+ skip = true;
+ } else {
+ final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
+ if (opCode != AppOpsManager.OP_NONE
+ && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
+ r.callerPackage, r.callerFeatureId, "Broadcast sent to protected receiver")
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: broadcasting "
+ + r.intent.toString()
+ + " from " + r.callerPackage + " (pid="
+ + r.callingPid + ", uid=" + r.callingUid + ")"
+ + " requires appop " + AppOpsManager.permissionToOp(
+ filter.requiredPermission)
+ + " due to registered receiver " + filter);
+ skip = true;
+ }
+ }
+ }
+
+ if (!skip && (filter.receiverList.app == null || filter.receiverList.app.isKilled()
+ || filter.receiverList.app.mErrorState.isCrashing())) {
+ Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
+ + " to " + filter.receiverList + ": process gone or crashing");
+ skip = true;
+ }
+
+ // Ensure that broadcasts are only sent to other Instant Apps if they are marked as
+ // visible to Instant Apps.
+ final boolean visibleToInstantApps =
+ (r.intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
+
+ if (!skip && !visibleToInstantApps && filter.instantApp
+ && filter.receiverList.uid != r.callingUid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")"
+ + " not specifying FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS");
+ skip = true;
+ }
+
+ if (!skip && !filter.visibleToInstantApp && r.callerInstantApp
+ && filter.receiverList.uid != r.callingUid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires receiver be visible to instant apps"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+
+ // Check that the receiver has the required permission(s) to receive this broadcast.
+ if (!skip && r.requiredPermissions != null && r.requiredPermissions.length > 0) {
+ for (int i = 0; i < r.requiredPermissions.length; i++) {
+ String requiredPermission = r.requiredPermissions[i];
+ int perm = mService.checkComponentPermission(requiredPermission,
+ filter.receiverList.pid, filter.receiverList.uid, -1, true);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires " + requiredPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+ if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
+ && mService.getAppOpsManager().noteOpNoThrow(appOp,
+ filter.receiverList.uid, filter.packageName, filter.featureId,
+ "Broadcast delivered to registered receiver " + filter.receiverId)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires appop " + AppOpsManager.permissionToOp(
+ requiredPermission)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ }
+ }
+ if (!skip && (r.requiredPermissions == null || r.requiredPermissions.length == 0)) {
+ int perm = mService.checkComponentPermission(null,
+ filter.receiverList.pid, filter.receiverList.uid, -1, true);
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: security check failed when receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+ }
+ // Check that the receiver does *not* have any excluded permissions
+ if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
+ for (int i = 0; i < r.excludedPermissions.length; i++) {
+ String excludedPermission = r.excludedPermissions[i];
+ final int perm = mService.checkComponentPermission(excludedPermission,
+ filter.receiverList.pid, filter.receiverList.uid, -1, true);
+
+ int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
+ if (appOp != AppOpsManager.OP_NONE) {
+ // When there is an app op associated with the permission,
+ // skip when both the permission and the app op are
+ // granted.
+ if ((perm == PackageManager.PERMISSION_GRANTED) && (
+ mService.getAppOpsManager().checkOpNoThrow(appOp,
+ filter.receiverList.uid,
+ filter.packageName)
+ == AppOpsManager.MODE_ALLOWED)) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " excludes appop " + AppOpsManager.permissionToOp(
+ excludedPermission)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ } else {
+ // When there is no app op associated with the permission,
+ // skip when permission is granted.
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " excludes " + excludedPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // Check that the receiver does *not* belong to any of the excluded packages
+ if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) {
+ if (ArrayUtils.contains(r.excludedPackages, filter.packageName)) {
+ Slog.w(TAG, "Skipping delivery of excluded package "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " excludes package " + filter.packageName
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+ }
+
+ // If the broadcast also requires an app op check that as well.
+ if (!skip && r.appOp != AppOpsManager.OP_NONE
+ && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
+ filter.receiverList.uid, filter.packageName, filter.featureId,
+ "Broadcast delivered to registered receiver " + filter.receiverId)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ", uid=" + filter.receiverList.uid + ")"
+ + " requires appop " + AppOpsManager.opToName(r.appOp)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+
+ // Ensure that broadcasts are only sent to other apps if they are explicitly marked as
+ // exported, or are System level broadcasts
+ if (!skip && !filter.exported && mService.checkComponentPermission(null, r.callingPid,
+ r.callingUid, filter.receiverList.uid, filter.exported)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Exported Denial: sending "
+ + r.intent.toString()
+ + ", action: " + r.intent.getAction()
+ + " from " + r.callerPackage
+ + " (uid=" + r.callingUid + ")"
+ + " due to receiver " + filter.receiverList.app
+ + " (uid " + filter.receiverList.uid + ")"
+ + " not specifying RECEIVER_EXPORTED");
+ skip = true;
+ }
+
+ // Filter packages in the intent extras, skipping delivery if none of the packages is
+ // visible to the receiver.
+ Bundle filteredExtras = null;
+ if (!skip && r.filterExtrasForReceiver != null) {
+ final Bundle extras = r.intent.getExtras();
+ if (extras != null) {
+ filteredExtras = r.filterExtrasForReceiver.apply(filter.receiverList.uid, extras);
+ if (filteredExtras == null) {
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG, "Skipping delivery to "
+ + filter.receiverList.app
+ + " : receiver is filtered by the package visibility");
+ }
+ skip = true;
+ }
+ }
+ }
+
+ if (skip) {
+ r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+ return;
+ }
+
+ // If permissions need a review before any of the app components can run, we drop
+ // the broadcast and if the calling app is in the foreground and the broadcast is
+ // explicit we launch the review UI passing it a pending intent to send the skipped
+ // broadcast.
+ if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
+ filter.owningUserId)) {
+ r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+ return;
+ }
+
+ r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
+
+ // If this is not being sent as an ordered broadcast, then we
+ // don't want to touch the fields that keep track of the current
+ // state of ordered broadcasts.
+ if (ordered) {
+ r.receiver = filter.receiverList.receiver.asBinder();
+ r.curFilter = filter;
+ filter.receiverList.curBroadcast = r;
+ r.state = BroadcastRecord.CALL_IN_RECEIVE;
+ if (filter.receiverList.app != null) {
+ // Bump hosting application to no longer be in background
+ // scheduling class. Note that we can't do that if there
+ // isn't an app... but we can only be in that case for
+ // things that directly call the IActivityManager API, which
+ // are already core system stuff so don't matter for this.
+ r.curApp = filter.receiverList.app;
+ filter.receiverList.app.mReceivers.addCurReceiver(r);
+ mService.enqueueOomAdjTargetLocked(r.curApp);
+ mService.updateOomAdjPendingTargetsLocked(
+ OOM_ADJ_REASON_START_RECEIVER);
+ }
+ } else if (filter.receiverList.app != null) {
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app,
+ OOM_ADJ_REASON_START_RECEIVER);
+ }
+
+ try {
+ if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
+ "Delivering to " + filter + " : " + r);
+ if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
+ // Skip delivery if full backup in progress
+ // If it's an ordered broadcast, we need to continue to the next receiver.
+ if (ordered) {
+ skipReceiverLocked(r);
+ }
+ } else {
+ r.receiverTime = SystemClock.uptimeMillis();
+ maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
+ maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
+ performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+ prepareReceiverIntent(r.intent, filteredExtras), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId,
+ filter.receiverList.uid, r.callingUid,
+ r.dispatchTime - r.enqueueTime,
+ r.receiverTime - r.dispatchTime);
+ // parallel broadcasts are fire-and-forget, not bookended by a call to
+ // finishReceiverLocked(), so we manage their activity-start token here
+ if (filter.receiverList.app != null
+ && r.allowBackgroundActivityStarts && !r.ordered) {
+ postActivityStartTokenRemoval(filter.receiverList.app, r);
+ }
+ }
+ if (ordered) {
+ r.state = BroadcastRecord.CALL_DONE_RECEIVE;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
+ // Clean up ProcessRecord state related to this broadcast attempt
+ if (filter.receiverList.app != null) {
+ filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r);
+ if (ordered) {
+ filter.receiverList.app.mReceivers.removeCurReceiver(r);
+ // Something wrong, its oom adj could be downgraded, but not in a hurry.
+ mService.enqueueOomAdjTargetLocked(r.curApp);
+ }
+ }
+ // And BroadcastRecord state related to ordered delivery, if appropriate
+ if (ordered) {
+ r.receiver = null;
+ r.curFilter = null;
+ filter.receiverList.curBroadcast = null;
+ }
+ }
+ }
+
+ private boolean requestStartTargetPermissionsReviewIfNeededLocked(
+ BroadcastRecord receiverRecord, String receivingPackageName,
+ final int receivingUserId) {
+ if (!mService.getPackageManagerInternal().isPermissionsReviewRequired(
+ receivingPackageName, receivingUserId)) {
+ return true;
+ }
+
+ final boolean callerForeground = receiverRecord.callerApp != null
+ ? receiverRecord.callerApp.mState.getSetSchedGroup()
+ != ProcessList.SCHED_GROUP_BACKGROUND : true;
+
+ // Show a permission review UI only for explicit broadcast from a foreground app
+ if (callerForeground && receiverRecord.intent.getComponent() != null) {
+ IIntentSender target = mService.mPendingIntentController.getIntentSender(
+ ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
+ receiverRecord.callerFeatureId, receiverRecord.callingUid,
+ receiverRecord.userId, null, null, 0,
+ new Intent[]{receiverRecord.intent},
+ new String[]{receiverRecord.intent.resolveType(mService.mContext
+ .getContentResolver())},
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, null);
+
+ final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, receivingPackageName);
+ intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
+
+ if (DEBUG_PERMISSIONS_REVIEW) {
+ Slog.i(TAG, "u" + receivingUserId + " Launching permission review for package "
+ + receivingPackageName);
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mService.mContext.startActivityAsUser(intent, new UserHandle(receivingUserId));
+ }
+ });
+ } else {
+ Slog.w(TAG, "u" + receivingUserId + " Receiving a broadcast in package"
+ + receivingPackageName + " requires a permissions review");
+ }
+
+ return false;
+ }
+
+ void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
+ @Nullable BroadcastOptions brOptions) {
+ if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
+ return;
+ }
+ long duration = brOptions.getTemporaryAppAllowlistDuration();
+ final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
+ final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
+ final String reason = brOptions.getTemporaryAppAllowlistReason();
+
+ if (duration > Integer.MAX_VALUE) {
+ duration = Integer.MAX_VALUE;
+ }
+ // XXX ideally we should pause the broadcast until everything behind this is done,
+ // or else we will likely start dispatching the broadcast before we have opened
+ // access to the app (there is a lot of asynchronicity behind this). It is probably
+ // not that big a deal, however, because the main purpose here is to allow apps
+ // to hold wake locks, and they will be able to acquire their wake lock immediately
+ // it just won't be enabled until we get through this work.
+ StringBuilder b = new StringBuilder();
+ b.append("broadcast:");
+ UserHandle.formatUid(b, r.callingUid);
+ b.append(":");
+ if (r.intent.getAction() != null) {
+ b.append(r.intent.getAction());
+ } else if (r.intent.getComponent() != null) {
+ r.intent.getComponent().appendShortString(b);
+ } else if (r.intent.getData() != null) {
+ b.append(r.intent.getData());
+ }
+ b.append(",reason:");
+ b.append(reason);
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
+ + " type=" + type + " : " + b.toString());
+ }
+ mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
+ r.callingUid);
+ }
+
+ /**
+ * Return true if all given permissions are signature-only perms.
+ */
+ final boolean isSignaturePerm(String[] perms) {
+ if (perms == null) {
+ return false;
+ }
+ IPermissionManager pm = AppGlobals.getPermissionManager();
+ for (int i = perms.length-1; i >= 0; i--) {
+ try {
+ PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0);
+ if (pi == null) {
+ // a required permission that no package has actually
+ // defined cannot be signature-required.
+ return false;
+ }
+ if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE
+ | PermissionInfo.PROTECTION_FLAG_PRIVILEGED))
+ != PermissionInfo.PROTECTION_SIGNATURE) {
+ // If this a signature permission and NOT allowed for privileged apps, it
+ // is okay... otherwise, nope!
+ return false;
+ }
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void processNextBroadcast(boolean fromMsg) {
+ synchronized (mService) {
+ processNextBroadcastLocked(fromMsg, false);
+ }
+ }
+
+ static String broadcastDescription(BroadcastRecord r, ComponentName component) {
+ return r.intent.toString()
+ + " from " + r.callerPackage + " (pid=" + r.callingPid
+ + ", uid=" + r.callingUid + ") to " + component.flattenToShortString();
+ }
+
+ private static Intent prepareReceiverIntent(@NonNull Intent originalIntent,
+ @Nullable Bundle filteredExtras) {
+ final Intent intent = new Intent(originalIntent);
+ if (filteredExtras != null) {
+ intent.replaceExtras(filteredExtras);
+ }
+ return intent;
+ }
+
+ public void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
+ BroadcastRecord r;
+
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
+ + mQueueName + "]: "
+ + mParallelBroadcasts.size() + " parallel broadcasts; "
+ + mDispatcher.describeStateLocked());
+
+ mService.updateCpuStats();
+
+ if (fromMsg) {
+ mBroadcastsScheduled = false;
+ }
+
+ // First, deliver any non-serialized broadcasts right away.
+ while (mParallelBroadcasts.size() > 0) {
+ r = mParallelBroadcasts.remove(0);
+ r.dispatchTime = SystemClock.uptimeMillis();
+ r.dispatchRealTime = SystemClock.elapsedRealtime();
+ r.dispatchClockTime = System.currentTimeMillis();
+ r.mIsReceiverAppRunning = true;
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
+ System.identityHashCode(r));
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
+ System.identityHashCode(r));
+ }
+
+ final int N = r.receivers.size();
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ + mQueueName + "] " + r);
+ for (int i=0; i<N; i++) {
+ Object target = r.receivers.get(i);
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Delivering non-ordered on [" + mQueueName + "] to registered "
+ + target + ": " + r);
+ deliverToRegisteredReceiverLocked(r,
+ (BroadcastFilter) target, false, i);
+ }
+ addBroadcastToHistoryLocked(r);
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ + mQueueName + "] " + r);
+ }
+
+ // Now take care of the next serialized one...
+
+ // If we are waiting for a process to come up to handle the next
+ // broadcast, then do nothing at this point. Just in case, we
+ // check that the process we're waiting for still exists.
+ if (mPendingBroadcast != null) {
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
+ "processNextBroadcast [" + mQueueName + "]: waiting for "
+ + mPendingBroadcast.curApp);
+
+ boolean isDead;
+ if (mPendingBroadcast.curApp.getPid() > 0) {
+ synchronized (mService.mPidsSelfLocked) {
+ ProcessRecord proc = mService.mPidsSelfLocked.get(
+ mPendingBroadcast.curApp.getPid());
+ isDead = proc == null || proc.mErrorState.isCrashing();
+ }
+ } else {
+ final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
+ mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
+ isDead = proc == null || !proc.isPendingStart();
+ }
+ if (!isDead) {
+ // It's still alive, so keep waiting
+ return;
+ } else {
+ Slog.w(TAG, "pending app ["
+ + mQueueName + "]" + mPendingBroadcast.curApp
+ + " died before responding to broadcast");
+ mPendingBroadcast.state = BroadcastRecord.IDLE;
+ mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
+ mPendingBroadcast = null;
+ }
+ }
+
+ boolean looped = false;
+
+ do {
+ final long now = SystemClock.uptimeMillis();
+ r = mDispatcher.getNextBroadcastLocked(now);
+
+ if (r == null) {
+ // No more broadcasts are deliverable right now, so all done!
+ mDispatcher.scheduleDeferralCheckLocked(false);
+ synchronized (mService.mAppProfiler.mProfilerLock) {
+ mService.mAppProfiler.scheduleAppGcsLPf();
+ }
+ if (looped && !skipOomAdj) {
+ // If we had finished the last ordered broadcast, then
+ // make sure all processes have correct oom and sched
+ // adjustments.
+ mService.updateOomAdjPendingTargetsLocked(
+ OOM_ADJ_REASON_START_RECEIVER);
+ }
+
+ // when we have no more ordered broadcast on this queue, stop logging
+ if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
+ mLogLatencyMetrics = false;
+ }
+
+ return;
+ }
+
+ boolean forceReceive = false;
+
+ // Ensure that even if something goes awry with the timeout
+ // detection, we catch "hung" broadcasts here, discard them,
+ // and continue to make progress.
+ //
+ // This is only done if the system is ready so that early-stage receivers
+ // don't get executed with timeouts; and of course other timeout-
+ // exempt broadcasts are ignored.
+ int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+ if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
+ if ((numReceivers > 0) &&
+ (now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
+ Slog.w(TAG, "Hung broadcast ["
+ + mQueueName + "] discarded after timeout failure:"
+ + " now=" + now
+ + " dispatchTime=" + r.dispatchTime
+ + " startTime=" + r.receiverTime
+ + " intent=" + r.intent
+ + " numReceivers=" + numReceivers
+ + " nextReceiver=" + r.nextReceiver
+ + " state=" + r.state);
+ broadcastTimeoutLocked(false); // forcibly finish this broadcast
+ forceReceive = true;
+ r.state = BroadcastRecord.IDLE;
+ }
+ }
+
+ if (r.state != BroadcastRecord.IDLE) {
+ if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
+ "processNextBroadcast("
+ + mQueueName + ") called when not idle (state="
+ + r.state + ")");
+ return;
+ }
+
+ // Is the current broadcast is done for any reason?
+ if (r.receivers == null || r.nextReceiver >= numReceivers
+ || r.resultAbort || forceReceive) {
+ // Send the final result if requested
+ if (r.resultTo != null) {
+ boolean sendResult = true;
+
+ // if this was part of a split/deferral complex, update the refcount and only
+ // send the completion when we clear all of them
+ if (r.splitToken != 0) {
+ int newCount = mSplitRefcounts.get(r.splitToken) - 1;
+ if (newCount == 0) {
+ // done! clear out this record's bookkeeping and deliver
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST,
+ "Sending broadcast completion for split token "
+ + r.splitToken + " : " + r.intent.getAction());
+ }
+ mSplitRefcounts.delete(r.splitToken);
+ } else {
+ // still have some split broadcast records in flight; update refcount
+ // and hold off on the callback
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST,
+ "Result refcount now " + newCount + " for split token "
+ + r.splitToken + " : " + r.intent.getAction()
+ + " - not sending completion yet");
+ }
+ sendResult = false;
+ mSplitRefcounts.put(r.splitToken, newCount);
+ }
+ }
+ if (sendResult) {
+ if (r.callerApp != null) {
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+ r.callerApp, OOM_ADJ_REASON_FINISH_RECEIVER);
+ }
+ try {
+ if (DEBUG_BROADCAST) {
+ Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
+ + r.intent.getAction() + " app=" + r.callerApp);
+ }
+ if (r.dispatchTime == 0) {
+ // The dispatch time here could be 0, in case it's a parallel
+ // broadcast but it has a result receiver. Set it to now.
+ r.dispatchTime = now;
+ }
+ r.mIsReceiverAppRunning = true;
+ performReceiveLocked(r.callerApp, r.resultTo,
+ new Intent(r.intent), r.resultCode,
+ r.resultData, r.resultExtras, false, false, r.userId,
+ r.callingUid, r.callingUid,
+ r.dispatchTime - r.enqueueTime,
+ now - r.dispatchTime);
+ logBootCompletedBroadcastCompletionLatencyIfPossible(r);
+ // Set this to null so that the reference
+ // (local and remote) isn't kept in the mBroadcastHistory.
+ r.resultTo = null;
+ } catch (RemoteException e) {
+ r.resultTo = null;
+ Slog.w(TAG, "Failure ["
+ + mQueueName + "] sending broadcast result of "
+ + r.intent, e);
+ }
+ }
+ }
+
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
+ cancelBroadcastTimeoutLocked();
+
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
+ "Finished with ordered broadcast " + r);
+
+ // ... and on to the next...
+ addBroadcastToHistoryLocked(r);
+ if (r.intent.getComponent() == null && r.intent.getPackage() == null
+ && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+ // This was an implicit broadcast... let's record it for posterity.
+ mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
+ r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
+ }
+ mDispatcher.retireBroadcastLocked(r);
+ r = null;
+ looped = true;
+ continue;
+ }
+
+ // Check whether the next receiver is under deferral policy, and handle that
+ // accordingly. If the current broadcast was already part of deferred-delivery
+ // tracking, we know that it must now be deliverable as-is without re-deferral.
+ if (!r.deferred) {
+ final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
+ if (mDispatcher.isDeferringLocked(receiverUid)) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid
+ + " at " + r.nextReceiver + " is under deferral");
+ }
+ // If this is the only (remaining) receiver in the broadcast, "splitting"
+ // doesn't make sense -- just defer it as-is and retire it as the
+ // currently active outgoing broadcast.
+ BroadcastRecord defer;
+ if (r.nextReceiver + 1 == numReceivers) {
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Sole receiver of " + r
+ + " is under deferral; setting aside and proceeding");
+ }
+ defer = r;
+ mDispatcher.retireBroadcastLocked(r);
+ } else {
+ // Nontrivial case; split out 'uid's receivers to a new broadcast record
+ // and defer that, then loop and pick up continuing delivery of the current
+ // record (now absent those receivers).
+
+ // The split operation is guaranteed to match at least at 'nextReceiver'
+ defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "Post split:");
+ Slog.i(TAG_BROADCAST, "Original broadcast receivers:");
+ for (int i = 0; i < r.receivers.size(); i++) {
+ Slog.i(TAG_BROADCAST, " " + r.receivers.get(i));
+ }
+ Slog.i(TAG_BROADCAST, "Split receivers:");
+ for (int i = 0; i < defer.receivers.size(); i++) {
+ Slog.i(TAG_BROADCAST, " " + defer.receivers.get(i));
+ }
+ }
+ // Track completion refcount as well if relevant
+ if (r.resultTo != null) {
+ int token = r.splitToken;
+ if (token == 0) {
+ // first split of this record; refcount for 'r' and 'deferred'
+ r.splitToken = defer.splitToken = nextSplitTokenLocked();
+ mSplitRefcounts.put(r.splitToken, 2);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST,
+ "Broadcast needs split refcount; using new token "
+ + r.splitToken);
+ }
+ } else {
+ // new split from an already-refcounted situation; increment count
+ final int curCount = mSplitRefcounts.get(token);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ if (curCount == 0) {
+ Slog.wtf(TAG_BROADCAST,
+ "Split refcount is zero with token for " + r);
+ }
+ }
+ mSplitRefcounts.put(token, curCount + 1);
+ if (DEBUG_BROADCAST_DEFERRAL) {
+ Slog.i(TAG_BROADCAST, "New split count for token " + token
+ + " is " + (curCount + 1));
+ }
+ }
+ }
+ }
+ mDispatcher.addDeferredBroadcast(receiverUid, defer);
+ r = null;
+ looped = true;
+ continue;
+ }
+ }
+ } while (r == null);
+
+ // Get the next receiver...
+ int recIdx = r.nextReceiver++;
+
+ // Keep track of when this receiver started, and make sure there
+ // is a timeout message pending to kill it if need be.
+ r.receiverTime = SystemClock.uptimeMillis();
+ if (recIdx == 0) {
+ r.dispatchTime = r.receiverTime;
+ r.dispatchRealTime = SystemClock.elapsedRealtime();
+ r.dispatchClockTime = System.currentTimeMillis();
+
+ if (mLogLatencyMetrics) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
+ r.dispatchClockTime - r.enqueueClockTime);
+ }
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
+ System.identityHashCode(r));
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
+ System.identityHashCode(r));
+ }
+ if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
+ + mQueueName + "] " + r);
+ }
+ if (! mPendingBroadcastTimeoutMessage) {
+ long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Submitting BROADCAST_TIMEOUT_MSG ["
+ + mQueueName + "] for " + r + " at " + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
+ }
+
+ final BroadcastOptions brOptions = r.options;
+ final Object nextReceiver = r.receivers.get(recIdx);
+
+ if (nextReceiver instanceof BroadcastFilter) {
+ // Simple case: this is a registered receiver who gets
+ // a direct call.
+ BroadcastFilter filter = (BroadcastFilter)nextReceiver;
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Delivering ordered ["
+ + mQueueName + "] to registered "
+ + filter + ": " + r);
+ r.mIsReceiverAppRunning = true;
+ deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
+ if (r.receiver == null || !r.ordered) {
+ // The receiver has already finished, so schedule to
+ // process the next one.
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
+ + mQueueName + "]: ordered="
+ + r.ordered + " receiver=" + r.receiver);
+ r.state = BroadcastRecord.IDLE;
+ scheduleBroadcastsLocked();
+ } else {
+ if (filter.receiverList != null) {
+ maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+ // r is guaranteed ordered at this point, so we know finishReceiverLocked()
+ // will get a callback and handle the activity start token lifecycle.
+ }
+ }
+ return;
+ }
+
+ // Hard case: need to instantiate the receiver, possibly
+ // starting its application process to host it.
+
+ ResolveInfo info =
+ (ResolveInfo)nextReceiver;
+ ComponentName component = new ComponentName(
+ info.activityInfo.applicationInfo.packageName,
+ info.activityInfo.name);
+
+ boolean skip = false;
+ if (brOptions != null &&
+ (info.activityInfo.applicationInfo.targetSdkVersion
+ < brOptions.getMinManifestReceiverApiLevel() ||
+ info.activityInfo.applicationInfo.targetSdkVersion
+ > brOptions.getMaxManifestReceiverApiLevel())) {
+ Slog.w(TAG, "Target SDK mismatch: receiver " + info.activityInfo
+ + " targets " + info.activityInfo.applicationInfo.targetSdkVersion
+ + " but delivery restricted to ["
+ + brOptions.getMinManifestReceiverApiLevel() + ", "
+ + brOptions.getMaxManifestReceiverApiLevel()
+ + "] broadcasting " + broadcastDescription(r, component));
+ skip = true;
+ }
+ if (brOptions != null &&
+ !brOptions.testRequireCompatChange(info.activityInfo.applicationInfo.uid)) {
+ Slog.w(TAG, "Compat change filtered: broadcasting " + broadcastDescription(r, component)
+ + " to uid " + info.activityInfo.applicationInfo.uid + " due to compat change "
+ + r.options.getRequireCompatChangeId());
+ skip = true;
+ }
+ if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
+ component.getPackageName(), info.activityInfo.applicationInfo.uid)) {
+ Slog.w(TAG, "Association not allowed: broadcasting "
+ + broadcastDescription(r, component));
+ skip = true;
+ }
+ if (!skip) {
+ skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+ r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
+ if (skip) {
+ Slog.w(TAG, "Firewall blocked: broadcasting "
+ + broadcastDescription(r, component));
+ }
+ }
+ int perm = mService.checkComponentPermission(info.activityInfo.permission,
+ r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
+ info.activityInfo.exported);
+ if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
+ if (!info.activityInfo.exported) {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + broadcastDescription(r, component)
+ + " is not exported from uid " + info.activityInfo.applicationInfo.uid);
+ } else {
+ Slog.w(TAG, "Permission Denial: broadcasting "
+ + broadcastDescription(r, component)
+ + " requires " + info.activityInfo.permission);
+ }
+ skip = true;
+ } else if (!skip && info.activityInfo.permission != null) {
+ final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
+ if (opCode != AppOpsManager.OP_NONE && mService.getAppOpsManager().noteOpNoThrow(opCode,
+ r.callingUid, r.callerPackage, r.callerFeatureId,
+ "Broadcast delivered to " + info.activityInfo.name)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: broadcasting "
+ + broadcastDescription(r, component)
+ + " requires appop " + AppOpsManager.permissionToOp(
+ info.activityInfo.permission));
+ skip = true;
+ }
+ }
+
+ boolean isSingleton = false;
+ try {
+ isSingleton = mService.isSingleton(info.activityInfo.processName,
+ info.activityInfo.applicationInfo,
+ info.activityInfo.name, info.activityInfo.flags);
+ } catch (SecurityException e) {
+ Slog.w(TAG, e.getMessage());
+ skip = true;
+ }
+ if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ if (ActivityManager.checkUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ info.activityInfo.applicationInfo.uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: Receiver " + component.flattenToShortString()
+ + " requests FLAG_SINGLE_USER, but app does not hold "
+ + android.Manifest.permission.INTERACT_ACROSS_USERS);
+ skip = true;
+ }
+ }
+ if (!skip && info.activityInfo.applicationInfo.isInstantApp()
+ && r.callingUid != info.activityInfo.applicationInfo.uid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent
+ + " to " + component.flattenToShortString()
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")"
+ + " Instant Apps do not support manifest receivers");
+ skip = true;
+ }
+ if (!skip && r.callerInstantApp
+ && (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0
+ && r.callingUid != info.activityInfo.applicationInfo.uid) {
+ Slog.w(TAG, "Instant App Denial: receiving "
+ + r.intent
+ + " to " + component.flattenToShortString()
+ + " requires receiver have visibleToInstantApps set"
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+ if (r.curApp != null && r.curApp.mErrorState.isCrashing()) {
+ // If the target process is crashing, just skip it.
+ Slog.w(TAG, "Skipping deliver ordered [" + mQueueName + "] " + r
+ + " to " + r.curApp + ": process crashing");
+ skip = true;
+ }
+ if (!skip) {
+ boolean isAvailable = false;
+ try {
+ isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
+ info.activityInfo.packageName,
+ UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (Exception e) {
+ // all such failures mean we skip this receiver
+ Slog.w(TAG, "Exception getting recipient info for "
+ + info.activityInfo.packageName, e);
+ }
+ if (!isAvailable) {
+ Slog.w(TAG_BROADCAST,
+ "Skipping delivery to " + info.activityInfo.packageName + " / "
+ + info.activityInfo.applicationInfo.uid
+ + " : package no longer available");
+ skip = true;
+ }
+ }
+
+ // If permissions need a review before any of the app components can run, we drop
+ // the broadcast and if the calling app is in the foreground and the broadcast is
+ // explicit we launch the review UI passing it a pending intent to send the skipped
+ // broadcast.
+ if (!skip) {
+ if (!requestStartTargetPermissionsReviewIfNeededLocked(r,
+ info.activityInfo.packageName, UserHandle.getUserId(
+ info.activityInfo.applicationInfo.uid))) {
+ Slog.w(TAG_BROADCAST,
+ "Skipping delivery: permission review required for "
+ + broadcastDescription(r, component));
+ skip = true;
+ }
+ }
+
+ // This is safe to do even if we are skipping the broadcast, and we need
+ // this information now to evaluate whether it is going to be allowed to run.
+ final int receiverUid = info.activityInfo.applicationInfo.uid;
+ // If it's a singleton, it needs to be the same app or a special app
+ if (r.callingUid != Process.SYSTEM_UID && isSingleton
+ && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
+ info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
+ }
+ String targetProcess = info.activityInfo.processName;
+ ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
+ info.activityInfo.applicationInfo.uid);
+
+ if (!skip) {
+ final int allowed = mService.getAppStartModeLOSP(
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);
+ if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
+ // We won't allow this receiver to be launched if the app has been
+ // completely disabled from launches, or it was not explicitly sent
+ // to it and the app is in a state that should not receive it
+ // (depending on how getAppStartModeLOSP has determined that).
+ if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
+ Slog.w(TAG, "Background execution disabled: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ skip = true;
+ } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
+ || (r.intent.getComponent() == null
+ && r.intent.getPackage() == null
+ && ((r.intent.getFlags()
+ & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
+ && !isSignaturePerm(r.requiredPermissions))) {
+ mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
+ component.getPackageName());
+ Slog.w(TAG, "Background execution not allowed: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString());
+ skip = true;
+ }
+ }
+ }
+
+ if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
+ && !mService.mUserController
+ .isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
+ 0 /* flags */)) {
+ skip = true;
+ Slog.w(TAG,
+ "Skipping delivery to " + info.activityInfo.packageName + " / "
+ + info.activityInfo.applicationInfo.uid + " : user is not running");
+ }
+
+ if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) {
+ for (int i = 0; i < r.excludedPermissions.length; i++) {
+ String excludedPermission = r.excludedPermissions[i];
+ try {
+ perm = AppGlobals.getPackageManager()
+ .checkPermission(excludedPermission,
+ info.activityInfo.applicationInfo.packageName,
+ UserHandle
+ .getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (RemoteException e) {
+ perm = PackageManager.PERMISSION_DENIED;
+ }
+
+ int appOp = AppOpsManager.permissionToOpCode(excludedPermission);
+ if (appOp != AppOpsManager.OP_NONE) {
+ // When there is an app op associated with the permission,
+ // skip when both the permission and the app op are
+ // granted.
+ if ((perm == PackageManager.PERMISSION_GRANTED) && (
+ mService.getAppOpsManager().checkOpNoThrow(appOp,
+ info.activityInfo.applicationInfo.uid,
+ info.activityInfo.packageName)
+ == AppOpsManager.MODE_ALLOWED)) {
+ skip = true;
+ break;
+ }
+ } else {
+ // When there is no app op associated with the permission,
+ // skip when permission is granted.
+ if (perm == PackageManager.PERMISSION_GRANTED) {
+ skip = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // Check that the receiver does *not* belong to any of the excluded packages
+ if (!skip && r.excludedPackages != null && r.excludedPackages.length > 0) {
+ if (ArrayUtils.contains(r.excludedPackages, component.getPackageName())) {
+ Slog.w(TAG, "Skipping delivery of excluded package "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " excludes package " + component.getPackageName()
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ }
+ }
+
+ if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
+ r.requiredPermissions != null && r.requiredPermissions.length > 0) {
+ for (int i = 0; i < r.requiredPermissions.length; i++) {
+ String requiredPermission = r.requiredPermissions[i];
+ try {
+ perm = AppGlobals.getPackageManager().
+ checkPermission(requiredPermission,
+ info.activityInfo.applicationInfo.packageName,
+ UserHandle
+ .getUserId(info.activityInfo.applicationInfo.uid));
+ } catch (RemoteException e) {
+ perm = PackageManager.PERMISSION_DENIED;
+ }
+ if (perm != PackageManager.PERMISSION_GRANTED) {
+ Slog.w(TAG, "Permission Denial: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " requires " + requiredPermission
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ skip = true;
+ break;
+ }
+ int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+ if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
+ if (!noteOpForManifestReceiver(appOp, r, info, component)) {
+ skip = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!skip && r.appOp != AppOpsManager.OP_NONE) {
+ if (!noteOpForManifestReceiver(r.appOp, r, info, component)) {
+ skip = true;
+ }
+ }
+
+ // Filter packages in the intent extras, skipping delivery if none of the packages is
+ // visible to the receiver.
+ Bundle filteredExtras = null;
+ if (!skip && r.filterExtrasForReceiver != null) {
+ final Bundle extras = r.intent.getExtras();
+ if (extras != null) {
+ filteredExtras = r.filterExtrasForReceiver.apply(receiverUid, extras);
+ if (filteredExtras == null) {
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG, "Skipping delivery to "
+ + info.activityInfo.packageName + " / " + receiverUid
+ + " : receiver is filtered by the package visibility");
+ }
+ skip = true;
+ }
+ }
+ }
+
+ if (skip) {
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Skipping delivery of ordered [" + mQueueName + "] "
+ + r + " for reason described above");
+ r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
+ r.receiver = null;
+ r.curFilter = null;
+ r.state = BroadcastRecord.IDLE;
+ r.manifestSkipCount++;
+ scheduleBroadcastsLocked();
+ return;
+ }
+ r.manifestCount++;
+
+ r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
+ r.state = BroadcastRecord.APP_RECEIVE;
+ r.curComponent = component;
+ r.curReceiver = info.activityInfo;
+ r.curFilteredExtras = filteredExtras;
+ if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
+ Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ + receiverUid);
+ }
+ final boolean isActivityCapable =
+ (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
+ maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
+
+ // Report that a component is used for explicit broadcasts.
+ if (r.intent.getComponent() != null && r.curComponent != null
+ && !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
+ mService.mUsageStatsService.reportEvent(
+ r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
+ }
+
+ // Broadcast is being executed, its package can't be stopped.
+ try {
+ AppGlobals.getPackageManager().setPackageStoppedState(
+ r.curComponent.getPackageName(), false, r.userId);
+ } catch (RemoteException e) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + r.curComponent.getPackageName() + ": " + e);
+ }
+
+ // Is this receiver's application already running?
+ if (app != null && app.getThread() != null && !app.isKilled()) {
+ try {
+ app.addPackage(info.activityInfo.packageName,
+ info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
+ maybeAddAllowBackgroundActivityStartsToken(app, r);
+ r.mIsReceiverAppRunning = true;
+ processCurBroadcastLocked(r, app);
+ return;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception when sending broadcast to "
+ + r.curComponent, e);
+ } catch (RuntimeException e) {
+ Slog.wtf(TAG, "Failed sending broadcast to "
+ + r.curComponent + " with " + r.intent, e);
+ // If some unexpected exception happened, just skip
+ // this broadcast. At this point we are not in the call
+ // from a client, so throwing an exception out from here
+ // will crash the entire system instead of just whoever
+ // sent the broadcast.
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ // We need to reset the state if we failed to start the receiver.
+ r.state = BroadcastRecord.IDLE;
+ return;
+ }
+
+ // If a dead object exception was thrown -- fall through to
+ // restart the application.
+ }
+
+ // Not running -- get it started, to be executed when the app comes up.
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Need to start app ["
+ + mQueueName + "] " + targetProcess + " for broadcast " + r);
+ r.curApp = mService.startProcessLocked(targetProcess,
+ info.activityInfo.applicationInfo, true,
+ r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
+ new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
+ r.intent.getAction(), getHostingRecordTriggerType(r)),
+ isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
+ (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
+ if (r.curApp == null) {
+ // Ah, this recipient is unavailable. Finish it if necessary,
+ // and mark the broadcast record as ready for the next.
+ Slog.w(TAG, "Unable to launch app "
+ + info.activityInfo.applicationInfo.packageName + "/"
+ + receiverUid + " for broadcast "
+ + r.intent + ": process is bad");
+ logBroadcastReceiverDiscardLocked(r);
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+ r.state = BroadcastRecord.IDLE;
+ return;
+ }
+
+ maybeAddAllowBackgroundActivityStartsToken(r.curApp, r);
+ mPendingBroadcast = r;
+ mPendingBroadcastRecvIndex = recIdx;
+ }
+
+ private String getHostingRecordTriggerType(BroadcastRecord r) {
+ if (r.alarm) {
+ return HostingRecord.TRIGGER_TYPE_ALARM;
+ } else if (r.pushMessage) {
+ return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE;
+ } else if (r.pushMessageOverQuota) {
+ return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
+ }
+ return HostingRecord.TRIGGER_TYPE_UNKNOWN;
+ }
+
+ @Nullable
+ private String getTargetPackage(BroadcastRecord r) {
+ if (r.intent == null) {
+ return null;
+ }
+ if (r.intent.getPackage() != null) {
+ return r.intent.getPackage();
+ } else if (r.intent.getComponent() != null) {
+ return r.intent.getComponent().getPackageName();
+ }
+ return null;
+ }
+
+ private void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
+ // Only log after last receiver.
+ // In case of split BOOT_COMPLETED broadcast, make sure only call this method on the
+ // last BroadcastRecord of the split broadcast which has non-null resultTo.
+ final int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
+ if (r.nextReceiver < numReceivers) {
+ return;
+ }
+ final String action = r.intent.getAction();
+ int event = 0;
+ if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
+ event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
+ } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+ event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
+ }
+ if (event != 0) {
+ final int dispatchLatency = (int)(r.dispatchTime - r.enqueueTime);
+ final int completeLatency = (int)
+ (SystemClock.uptimeMillis() - r.enqueueTime);
+ final int dispatchRealLatency = (int)(r.dispatchRealTime - r.enqueueRealTime);
+ final int completeRealLatency = (int)
+ (SystemClock.elapsedRealtime() - r.enqueueRealTime);
+ int userType = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
+ // This method is called very infrequently, no performance issue we call
+ // LocalServices.getService() here.
+ final UserManagerInternal umInternal = LocalServices.getService(
+ UserManagerInternal.class);
+ final UserInfo userInfo = umInternal.getUserInfo(r.userId);
+ if (userInfo != null) {
+ userType = UserManager.getUserTypeForStatsd(userInfo.userType);
+ }
+ Slog.i(TAG_BROADCAST,
+ "BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:"
+ + action
+ + " dispatchLatency:" + dispatchLatency
+ + " completeLatency:" + completeLatency
+ + " dispatchRealLatency:" + dispatchRealLatency
+ + " completeRealLatency:" + completeRealLatency
+ + " receiversSize:" + numReceivers
+ + " userId:" + r.userId
+ + " userType:" + (userInfo != null? userInfo.userType : null));
+ FrameworkStatsLog.write(
+ BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED,
+ event,
+ dispatchLatency,
+ completeLatency,
+ dispatchRealLatency,
+ completeRealLatency,
+ r.userId,
+ userType);
+ }
+ }
+
+ private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
+ if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
+ return;
+ }
+ final String targetPackage = getTargetPackage(r);
+ // Ignore non-explicit broadcasts
+ if (targetPackage == null) {
+ return;
+ }
+ getUsageStatsManagerInternal().reportBroadcastDispatched(
+ r.callingUid, targetPackage, UserHandle.of(r.userId),
+ r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
+ mService.getUidStateLocked(targetUid));
+ }
+
+ @NonNull
+ private UsageStatsManagerInternal getUsageStatsManagerInternal() {
+ final UsageStatsManagerInternal usageStatsManagerInternal =
+ LocalServices.getService(UsageStatsManagerInternal.class);
+ return usageStatsManagerInternal;
+ }
+
+ private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
+ ComponentName component) {
+ if (ArrayUtils.isEmpty(info.activityInfo.attributionTags)) {
+ return noteOpForManifestReceiverInner(appOp, r, info, component, null);
+ } else {
+ // Attribution tags provided, noteOp each tag
+ for (String tag : info.activityInfo.attributionTags) {
+ if (!noteOpForManifestReceiverInner(appOp, r, info, component, tag)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private boolean noteOpForManifestReceiverInner(int appOp, BroadcastRecord r, ResolveInfo info,
+ ComponentName component, String tag) {
+ if (mService.getAppOpsManager().noteOpNoThrow(appOp,
+ info.activityInfo.applicationInfo.uid,
+ info.activityInfo.packageName,
+ tag,
+ "Broadcast delivered to " + info.activityInfo.name)
+ != AppOpsManager.MODE_ALLOWED) {
+ Slog.w(TAG, "Appop Denial: receiving "
+ + r.intent + " to "
+ + component.flattenToShortString()
+ + " requires appop " + AppOpsManager.opToName(appOp)
+ + " due to sender " + r.callerPackage
+ + " (uid " + r.callingUid + ")");
+ return false;
+ }
+ return true;
+ }
+
+ private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
+ if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
+ return;
+ }
+ String msgToken = (proc.toShortString() + r.toString()).intern();
+ // first, if there exists a past scheduled request to remove this token, drop
+ // that request - we don't want the token to be swept from under our feet...
+ mHandler.removeCallbacksAndMessages(msgToken);
+ // ...then add the token
+ proc.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
+ }
+
+ final void setBroadcastTimeoutLocked(long timeoutTime) {
+ if (! mPendingBroadcastTimeoutMessage) {
+ Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
+ mHandler.sendMessageAtTime(msg, timeoutTime);
+ mPendingBroadcastTimeoutMessage = true;
+ }
+ }
+
+ final void cancelBroadcastTimeoutLocked() {
+ if (mPendingBroadcastTimeoutMessage) {
+ mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
+ mPendingBroadcastTimeoutMessage = false;
+ }
+ }
+
+ final void broadcastTimeoutLocked(boolean fromMsg) {
+ if (fromMsg) {
+ mPendingBroadcastTimeoutMessage = false;
+ }
+
+ if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
+ if (fromMsg) {
+ if (!mService.mProcessesReady) {
+ // Only process broadcast timeouts if the system is ready; some early
+ // broadcasts do heavy work setting up system facilities
+ return;
+ }
+
+ // If the broadcast is generally exempt from timeout tracking, we're done
+ if (r.timeoutExempt) {
+ if (DEBUG_BROADCAST) {
+ Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
+ + r.intent.getAction());
+ }
+ return;
+ }
+
+ long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
+ if (timeoutTime > now) {
+ // We can observe premature timeouts because we do not cancel and reset the
+ // broadcast timeout message after each receiver finishes. Instead, we set up
+ // an initial timeout then kick it down the road a little further as needed
+ // when it expires.
+ if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
+ "Premature timeout ["
+ + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+ + timeoutTime);
+ setBroadcastTimeoutLocked(timeoutTime);
+ return;
+ }
+ }
+
+ if (r.state == BroadcastRecord.WAITING_SERVICES) {
+ // In this case the broadcast had already finished, but we had decided to wait
+ // for started services to finish as well before going on. So if we have actually
+ // waited long enough time timeout the broadcast, let's give up on the whole thing
+ // and just move on to the next.
+ Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
+ ? r.curComponent.flattenToShortString() : "(null)"));
+ r.curComponent = null;
+ r.state = BroadcastRecord.IDLE;
+ processNextBroadcastLocked(false, false);
+ return;
+ }
+
+ // If the receiver app is being debugged we quietly ignore unresponsiveness, just
+ // tidying up and moving on to the next broadcast without crashing or ANRing this
+ // app just because it's stopped at a breakpoint.
+ final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
+
+ long timeoutDurationMs = now - r.receiverTime;
+ Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+ + ", started " + timeoutDurationMs + "ms ago");
+ r.receiverTime = now;
+ if (!debugging) {
+ r.anrCount++;
+ }
+
+ ProcessRecord app = null;
+ TimeoutRecord timeoutRecord = null;
+
+ Object curReceiver;
+ if (r.nextReceiver > 0) {
+ curReceiver = r.receivers.get(r.nextReceiver-1);
+ r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
+ } else {
+ curReceiver = r.curReceiver;
+ }
+ Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
+ logBroadcastReceiverDiscardLocked(r);
+ if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
+ BroadcastFilter bf = (BroadcastFilter)curReceiver;
+ if (bf.receiverList.pid != 0
+ && bf.receiverList.pid != ActivityManagerService.MY_PID) {
+ synchronized (mService.mPidsSelfLocked) {
+ app = mService.mPidsSelfLocked.get(
+ bf.receiverList.pid);
+ }
+ }
+ } else {
+ app = r.curApp;
+ }
+
+ if (app != null) {
+ String anrMessage =
+ "Broadcast of " + r.intent.toString() + ", waited " + timeoutDurationMs
+ + "ms";
+ timeoutRecord = TimeoutRecord.forBroadcastReceiver(anrMessage);
+ }
+
+ if (mPendingBroadcast == r) {
+ mPendingBroadcast = null;
+ }
+
+ // Move on to the next receiver.
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
+
+ if (!debugging && timeoutRecord != null) {
+ mService.mAnrHelper.appNotResponding(app, timeoutRecord);
+ }
+ }
+
+ private final int ringAdvance(int x, final int increment, final int ringSize) {
+ x += increment;
+ if (x < 0) return (ringSize - 1);
+ else if (x >= ringSize) return 0;
+ else return x;
+ }
+
+ private final void addBroadcastToHistoryLocked(BroadcastRecord original) {
+ if (original.callingUid < 0) {
+ // This was from a registerReceiver() call; ignore it.
+ return;
+ }
+ original.finishTime = SystemClock.uptimeMillis();
+
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ createBroadcastTraceTitle(original, BroadcastRecord.DELIVERY_DELIVERED),
+ System.identityHashCode(original));
+ }
+
+ final ApplicationInfo info = original.callerApp != null ? original.callerApp.info : null;
+ final String callerPackage = info != null ? info.packageName : original.callerPackage;
+ if (callerPackage != null) {
+ mService.mHandler.obtainMessage(ActivityManagerService.DISPATCH_SENDING_BROADCAST_EVENT,
+ original.callingUid, 0, callerPackage).sendToTarget();
+ }
+
+ // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords,
+ // So don't change the incoming record directly.
+ final BroadcastRecord historyRecord = original.maybeStripForHistory();
+
+ mBroadcastHistory[mHistoryNext] = historyRecord;
+ mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY);
+
+ mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent;
+ mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime;
+ mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime;
+ mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis();
+ mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY);
+ }
+
+ public boolean cleanupDisabledPackageReceiversLocked(
+ String packageName, Set<String> filterByClasses, int userId, boolean doit) {
+ boolean didSomething = false;
+ for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
+ didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
+ packageName, filterByClasses, userId, doit);
+ if (!doit && didSomething) {
+ return true;
+ }
+ }
+
+ didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
+ filterByClasses, userId, doit);
+
+ return didSomething;
+ }
+
+ final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
+ final int logIndex = r.nextReceiver - 1;
+ if (logIndex >= 0 && logIndex < r.receivers.size()) {
+ Object curReceiver = r.receivers.get(logIndex);
+ if (curReceiver instanceof BroadcastFilter) {
+ BroadcastFilter bf = (BroadcastFilter) curReceiver;
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
+ bf.owningUserId, System.identityHashCode(r),
+ r.intent.getAction(), logIndex, System.identityHashCode(bf));
+ } else {
+ ResolveInfo ri = (ResolveInfo) curReceiver;
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+ UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
+ System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
+ }
+ } else {
+ if (logIndex < 0) Slog.w(TAG,
+ "Discarding broadcast before first receiver is invoked: " + r);
+ EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
+ -1, System.identityHashCode(r),
+ r.intent.getAction(),
+ r.nextReceiver,
+ "NONE");
+ }
+ }
+
+ private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
+ return formatSimple("Broadcast %s from %s (%s) %s",
+ state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
+ record.callerPackage == null ? "" : record.callerPackage,
+ record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
+ record.intent == null ? "" : record.intent.getAction());
+ }
+
+ public boolean isIdle() {
+ return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle()
+ && (mPendingBroadcast == null);
+ }
+
+ // Used by wait-for-broadcast-idle : fast-forward all current deferrals to
+ // be immediately deliverable.
+ public void cancelDeferrals() {
+ synchronized (mService) {
+ mDispatcher.cancelDeferralsLocked();
+ scheduleBroadcastsLocked();
+ }
+ }
+
+ public String describeState() {
+ synchronized (mService) {
+ return mParallelBroadcasts.size() + " parallel; "
+ + mDispatcher.describeStateLocked();
+ }
+ }
+
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
+ int N;
+ N = mParallelBroadcasts.size();
+ for (int i = N - 1; i >= 0; i--) {
+ mParallelBroadcasts.get(i).dumpDebug(proto, BroadcastQueueProto.PARALLEL_BROADCASTS);
+ }
+ mDispatcher.dumpDebug(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
+ if (mPendingBroadcast != null) {
+ mPendingBroadcast.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCAST);
+ }
+
+ int lastIndex = mHistoryNext;
+ int ringIndex = lastIndex;
+ do {
+ // increasing index = more recent entry, and we want to print the most
+ // recent first and work backwards, so we roll through the ring backwards.
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r != null) {
+ r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS);
+ }
+ } while (ringIndex != lastIndex);
+
+ lastIndex = ringIndex = mSummaryHistoryNext;
+ do {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ Intent intent = mBroadcastSummaryHistory[ringIndex];
+ if (intent == null) {
+ continue;
+ }
+ long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY);
+ intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT,
+ false, true, true, false);
+ proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS,
+ mSummaryHistoryEnqueueTime[ringIndex]);
+ proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS,
+ mSummaryHistoryDispatchTime[ringIndex]);
+ proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS,
+ mSummaryHistoryFinishTime[ringIndex]);
+ proto.end(summaryToken);
+ } while (ringIndex != lastIndex);
+ proto.end(token);
+ }
+
+ public boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+ int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ if (!mParallelBroadcasts.isEmpty() || !mDispatcher.isEmpty()
+ || mPendingBroadcast != null) {
+ boolean printed = false;
+ for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
+ BroadcastRecord br = mParallelBroadcasts.get(i);
+ if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ printed = true;
+ pw.println(" Active broadcasts [" + mQueueName + "]:");
+ }
+ pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
+ br.dump(pw, " ", sdf);
+ }
+
+ mDispatcher.dumpLocked(pw, dumpPackage, mQueueName, sdf);
+
+ if (dumpPackage == null || (mPendingBroadcast != null
+ && dumpPackage.equals(mPendingBroadcast.callerPackage))) {
+ pw.println();
+ pw.println(" Pending broadcast [" + mQueueName + "]:");
+ if (mPendingBroadcast != null) {
+ mPendingBroadcast.dump(pw, " ", sdf);
+ } else {
+ pw.println(" (null)");
+ }
+ needSep = true;
+ }
+ }
+
+ mConstants.dump(pw);
+
+ int i;
+ boolean printed = false;
+
+ i = -1;
+ int lastIndex = mHistoryNext;
+ int ringIndex = lastIndex;
+ do {
+ // increasing index = more recent entry, and we want to print the most
+ // recent first and work backwards, so we roll through the ring backwards.
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r == null) {
+ continue;
+ }
+
+ i++; // genuine record of some sort even if we're filtering it out
+ if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.println(" Historical broadcasts [" + mQueueName + "]:");
+ printed = true;
+ }
+ if (dumpAll) {
+ pw.print(" Historical Broadcast " + mQueueName + " #");
+ pw.print(i); pw.println(":");
+ r.dump(pw, " ", sdf);
+ } else {
+ pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
+ pw.print(" ");
+ pw.println(r.intent.toShortString(false, true, true, false));
+ if (r.targetComp != null && r.targetComp != r.intent.getComponent()) {
+ pw.print(" targetComp: "); pw.println(r.targetComp.toShortString());
+ }
+ Bundle bundle = r.intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: "); pw.println(bundle.toString());
+ }
+ }
+ } while (ringIndex != lastIndex);
+
+ if (dumpPackage == null) {
+ lastIndex = ringIndex = mSummaryHistoryNext;
+ if (dumpAll) {
+ printed = false;
+ i = -1;
+ } else {
+ // roll over the 'i' full dumps that have already been issued
+ for (int j = i;
+ j > 0 && ringIndex != lastIndex;) {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ BroadcastRecord r = mBroadcastHistory[ringIndex];
+ if (r == null) {
+ continue;
+ }
+ j--;
+ }
+ }
+ // done skipping; dump the remainder of the ring. 'i' is still the ordinal within
+ // the overall broadcast history.
+ do {
+ ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
+ Intent intent = mBroadcastSummaryHistory[ringIndex];
+ if (intent == null) {
+ continue;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.println(" Historical broadcasts summary [" + mQueueName + "]:");
+ printed = true;
+ }
+ if (!dumpAll && i >= 50) {
+ pw.println(" ...");
+ break;
+ }
+ i++;
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ pw.println(intent.toShortString(false, true, true, false));
+ pw.print(" ");
+ TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
+ - mSummaryHistoryEnqueueTime[ringIndex], pw);
+ pw.print(" dispatch ");
+ TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
+ - mSummaryHistoryDispatchTime[ringIndex], pw);
+ pw.println(" finish");
+ pw.print(" enq=");
+ pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
+ pw.print(" disp=");
+ pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
+ pw.print(" fin=");
+ pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: "); pw.println(bundle.toString());
+ }
+ } while (ringIndex != lastIndex);
+ }
+
+ return needSep;
+ }
+}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 66ef5e7..11eb782 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -582,13 +582,18 @@
}
@Override
- public boolean isCameraDisabled() {
+ public boolean isCameraDisabled(int userId) {
DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
if (dpm == null) {
Slog.e(TAG, "Failed to get the device policy manager service");
return false;
}
- return dpm.getCameraDisabled(null);
+ try {
+ return dpm.getCameraDisabled(null, userId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
}
};
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
new file mode 100644
index 0000000..e46bb74
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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.server.location.contexthub;
+
+import android.hardware.location.NanoAppMessage;
+
+/**
+ * A class to log events and useful metrics within the Context Hub service.
+ *
+ * The class holds a queue of the last NUM_EVENTS_TO_STORE events for each
+ * event category: nanoapp load, nanoapp unload, message from a nanoapp,
+ * message to a nanoapp, and context hub restarts. The dump() function
+ * will be called during debug dumps, giving access to the event information
+ * and aggregate data since the instantiation of this class.
+ *
+ * @hide
+ */
+public class ContextHubEventLogger {
+ private static final String TAG = "ContextHubEventLogger";
+
+ ContextHubEventLogger() {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs a nanoapp load event
+ *
+ * @param contextHubId the ID of the context hub
+ * @param nanoAppId the ID of the nanoapp
+ * @param nanoAppVersion the version of the nanoapp
+ * @param nanoAppSize the size in bytes of the nanoapp
+ * @param success whether the load was successful
+ */
+ public void logNanoAppLoad(int contextHubId, long nanoAppId, int nanoAppVersion,
+ long nanoAppSize, boolean success) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs a nanoapp unload event
+ *
+ * @param contextHubId the ID of the context hub
+ * @param nanoAppId the ID of the nanoapp
+ * @param success whether the unload was successful
+ */
+ public void logNanoAppUnload(int contextHubId, long nanoAppId, boolean success) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs the event where a nanoapp sends a message to a client
+ *
+ * @param contextHubId the ID of the context hub
+ * @param message the message that was sent
+ */
+ public void logMessageFromNanoApp(int contextHubId, NanoAppMessage message) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs the event where a client sends a message to a nanoapp
+ *
+ * @param contextHubId the ID of the context hub
+ * @param message the message that was sent
+ * @param success whether the message was sent successfully
+ */
+ public void logMessageToNanoApp(int contextHubId, NanoAppMessage message, boolean success) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Logs a context hub restart event
+ *
+ * @param contextHubId the ID of the context hub
+ */
+ public void logContextHubRestarts(int contextHubId) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ /**
+ * Creates a string representation of the logged events
+ *
+ * @return the dumped events
+ */
+ public String dump() {
+ throw new RuntimeException("Not implemented");
+ }
+
+ @Override
+ public String toString() {
+ return dump();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e423ea9..672c13c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -911,7 +911,7 @@
private @NonNull List<UserInfo> getUsersInternal(boolean excludePartial, boolean excludeDying,
boolean excludePreCreated) {
synchronized (mUsersLock) {
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ ArrayList<UserInfo> users = new ArrayList<>(mUsers.size());
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i).info;
@@ -1767,6 +1767,32 @@
}
@Override
+ public List<UserHandle> getVisibleUsers() {
+ if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+ throw new SecurityException("Caller needs MANAGE_USERS or INTERACT_ACROSS_USERS "
+ + "permission to get list of visible users");
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ // TODO(b/2399825580): refactor into UserDisplayAssigner
+ synchronized (mUsersLock) {
+ int usersSize = mUsers.size();
+ ArrayList<UserHandle> visibleUsers = new ArrayList<>(usersSize);
+ for (int i = 0; i < usersSize; i++) {
+ UserInfo ui = mUsers.valueAt(i).info;
+ if (!ui.partial && !ui.preCreated && !mRemovingUserIds.get(ui.id)
+ && isUserVisibleUnchecked(ui.id)) {
+ visibleUsers.add(UserHandle.of(ui.id));
+ }
+ }
+ return visibleUsers;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public @NonNull String getUserName() {
final int callingUid = Binder.getCallingUid();
if (!hasQueryOrCreateUsersPermission()
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4f67f1d..fbaf1ce 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8144,7 +8144,8 @@
}
final CallerIdentity caller = getCallerIdentity(who);
- Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)
+ || isCameraServerUid(caller));
if (parent) {
Preconditions.checkCallAuthorization(
@@ -9666,6 +9667,10 @@
return UserHandle.isSameApp(caller.getUid(), Process.SHELL_UID);
}
+ private boolean isCameraServerUid(CallerIdentity caller) {
+ return UserHandle.isSameApp(caller.getUid(), Process.CAMERASERVER_UID);
+ }
+
private @UserIdInt int getCurrentForegroundUserId() {
try {
UserInfo currentUser = mInjector.getIActivityManager().getCurrentUser();
diff --git a/tools/lint/OWNERS b/tools/lint/OWNERS
index 7c04519..2c526a1 100644
--- a/tools/lint/OWNERS
+++ b/tools/lint/OWNERS
@@ -2,4 +2,5 @@
jsharkey@google.com
per-file *CallingSettingsNonUserGetterMethods* = file:/packages/SettingsProvider/OWNERS
+per-file *RegisterReceiverFlagDetector* = jacobhobbie@google.com