blob: 60fddf0c7f22730ea23ad66eb194e98f2d126664 [file] [log] [blame]
/*
* 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.am;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.am.ActivityManagerService.checkComponentPermission;
import static com.android.server.am.BroadcastQueue.TAG;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.permission.IPermissionManager;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
/**
* Policy logic that decides if delivery of a particular {@link BroadcastRecord}
* should be skipped for a given {@link ResolveInfo} or {@link BroadcastFilter}.
* <p>
* This policy should be consulted as close as possible to the actual dispatch.
*/
public class BroadcastSkipPolicy {
private final ActivityManagerService mService;
public BroadcastSkipPolicy(ActivityManagerService service) {
mService = service;
}
/**
* Determine if the given {@link BroadcastRecord} is eligible to be sent to
* the given {@link BroadcastFilter} or {@link ResolveInfo}.
*/
public boolean shouldSkip(@NonNull BroadcastRecord r, @NonNull Object target) {
if (target instanceof BroadcastFilter) {
return shouldSkip(r, (BroadcastFilter) target);
} else {
return shouldSkip(r, (ResolveInfo) target);
}
}
/**
* Determine if the given {@link BroadcastRecord} is eligible to be sent to
* the given {@link ResolveInfo}.
*/
public boolean shouldSkip(@NonNull BroadcastRecord r, @NonNull ResolveInfo info) {
final BroadcastOptions brOptions = r.options;
final ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
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));
return 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());
return true;
}
if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
component.getPackageName(), info.activityInfo.applicationInfo.uid)) {
Slog.w(TAG, "Association not allowed: broadcasting "
+ broadcastDescription(r, component));
return true;
}
if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid)) {
Slog.w(TAG, "Firewall blocked: broadcasting "
+ broadcastDescription(r, component));
return true;
}
int perm = checkComponentPermission(info.activityInfo.permission,
r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
info.activityInfo.exported);
if (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);
}
return true;
} else if (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));
return 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);
return true;
}
}
if (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");
return true;
}
if (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 + ")");
return true;
}
if (r.curApp != null && r.curApp.mErrorState.isCrashing()) {
// If the target process is crashing, just skip it.
Slog.w(TAG, "Skipping deliver ordered [" + r.queue.toString() + "] " + r
+ " to " + r.curApp + ": process crashing");
return true;
}
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,
"Skipping delivery to " + info.activityInfo.packageName + " / "
+ info.activityInfo.applicationInfo.uid
+ " : package no longer available");
return 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 (!requestStartTargetPermissionsReviewIfNeededLocked(r,
info.activityInfo.packageName, UserHandle.getUserId(
info.activityInfo.applicationInfo.uid))) {
Slog.w(TAG,
"Skipping delivery: permission review required for "
+ broadcastDescription(r, component));
return true;
}
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());
return 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());
return true;
}
}
if (!Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())
&& !mService.mUserController
.isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),
0 /* flags */)) {
Slog.w(TAG,
"Skipping delivery to " + info.activityInfo.packageName + " / "
+ info.activityInfo.applicationInfo.uid + " : user is not running");
return true;
}
if (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)) {
return true;
}
} else {
// When there is no app op associated with the permission,
// skip when permission is granted.
if (perm == PackageManager.PERMISSION_GRANTED) {
return true;
}
}
}
}
// Check that the receiver does *not* belong to any of the excluded packages
if (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 + ")");
return true;
}
}
if (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 + ")");
return true;
}
int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
if (!noteOpForManifestReceiver(appOp, r, info, component)) {
return true;
}
}
}
}
if (r.appOp != AppOpsManager.OP_NONE) {
if (!noteOpForManifestReceiver(r.appOp, r, info, component)) {
return true;
}
}
return false;
}
/**
* Determine if the given {@link BroadcastRecord} is eligible to be sent to
* the given {@link BroadcastFilter}.
*/
public boolean shouldSkip(@NonNull BroadcastRecord r, @NonNull BroadcastFilter filter) {
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());
return 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);
return true;
}
if (!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);
return true;
}
// Check that the sender has permission to send to this receiver
if (filter.requiredPermission != null) {
int perm = 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);
return 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);
return true;
}
}
}
if ((filter.receiverList.app == null || filter.receiverList.app.isKilled()
|| filter.receiverList.app.mErrorState.isCrashing())) {
Slog.w(TAG, "Skipping deliver [" + r.queue.toString() + "] " + r
+ " to " + filter.receiverList + ": process gone or crashing");
return 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 (!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");
return true;
}
if (!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 + ")");
return true;
}
// Check that the receiver has the required permission(s) to receive this broadcast.
if (r.requiredPermissions != null && r.requiredPermissions.length > 0) {
for (int i = 0; i < r.requiredPermissions.length; i++) {
String requiredPermission = r.requiredPermissions[i];
int perm = 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 + ")");
return true;
}
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 + ")");
return true;
}
}
}
if ((r.requiredPermissions == null || r.requiredPermissions.length == 0)) {
int perm = 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 + ")");
return true;
}
}
// Check that the receiver does *not* have any excluded permissions
if (r.excludedPermissions != null && r.excludedPermissions.length > 0) {
for (int i = 0; i < r.excludedPermissions.length; i++) {
String excludedPermission = r.excludedPermissions[i];
final int perm = 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 + ")");
return true;
}
} 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 + ")");
return true;
}
}
}
}
// Check that the receiver does *not* belong to any of the excluded packages
if (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 + ")");
return true;
}
}
// If the broadcast also requires an app op check that as well.
if (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 + ")");
return true;
}
// Ensure that broadcasts are only sent to other apps if they are explicitly marked as
// exported, or are System level broadcasts
if (!filter.exported && 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");
return 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 (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
filter.owningUserId)) {
return true;
}
return false;
}
private static String broadcastDescription(BroadcastRecord r, ComponentName component) {
return r.intent.toString()
+ " from " + r.callerPackage + " (pid=" + r.callingPid
+ ", uid=" + r.callingUid + ") to " + component.flattenToShortString();
}
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;
}
/**
* Return true if all given permissions are signature-only perms.
*/
private 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 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);
}
mService.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;
}
}