use of android.app.BroadcastOptions in project platform_frameworks_base by android.
the class BroadcastQueue method processNextBroadcast.
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
BroadcastRecord r;
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "processNextBroadcast [" + mQueueName + "]: " + mParallelBroadcasts.size() + " broadcasts, " + mOrderedBroadcasts.size() + " ordered broadcasts");
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.dispatchClockTime = System.currentTimeMillis();
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);
}
// 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;
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
}
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 {
if (mOrderedBroadcasts.size() == 0) {
// No more broadcasts pending, so all done!
mService.scheduleAppGcsLocked();
if (looped) {
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
// adjustments.
mService.updateOomAdjLocked();
}
return;
}
r = mOrderedBroadcasts.get(0);
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 PRE_BOOT_COMPLETED
// receivers don't get executed with timeouts. They're intended for
// one time heavy lifting after system upgrades and can take
// significant amounts of time.
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) && (now > r.dispatchTime + (2 * mTimeoutPeriod * 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);
// forcibly finish this broadcast
broadcastTimeoutLocked(false);
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;
}
if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) {
// result if requested...
if (r.resultTo != null) {
try {
if (DEBUG_BROADCAST)
Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] " + r.intent.getAction() + " app=" + r.callerApp);
performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false, r.userId);
// 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);
}
mOrderedBroadcasts.remove(0);
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.dispatchClockTime = System.currentTimeMillis();
if (DEBUG_BROADCAST_LIGHT)
Slog.v(TAG_BROADCAST, "Processing ordered broadcast [" + mQueueName + "] " + r);
}
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
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);
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
// 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 (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(filter.owningUid, brOptions.getTemporaryAppWhitelistDuration(), r);
}
}
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())) {
skip = true;
}
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 " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" + " is not exported from uid " + info.activityInfo.applicationInfo.uid + " due to receiver " + component.flattenToShortString());
} else {
Slog.w(TAG, "Permission Denial: broadcasting " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" + " requires " + info.activityInfo.permission + " due to receiver " + component.flattenToShortString());
}
skip = true;
} else if (!skip && info.activityInfo.permission != null) {
final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
if (opCode != AppOpsManager.OP_NONE && mService.mAppOpsService.noteOperation(opCode, r.callingUid, r.callerPackage) != 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(info.activityInfo.permission) + " due to registered receiver " + component.flattenToShortString());
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 && mService.mAppOpsService.noteOperation(appOp, info.activityInfo.applicationInfo.uid, info.activityInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving " + r.intent + " to " + component.flattenToShortString() + " requires appop " + AppOpsManager.permissionToOp(requiredPermission) + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")");
skip = true;
break;
}
}
}
if (!skip && r.appOp != AppOpsManager.OP_NONE && mService.mAppOpsService.noteOperation(r.appOp, info.activityInfo.applicationInfo.uid, info.activityInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving " + r.intent + " to " + component.flattenToShortString() + " requires appop " + AppOpsManager.opToName(r.appOp) + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")");
skip = true;
}
if (!skip) {
skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid, r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
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) {
r.manifestCount++;
} else {
r.manifestSkipCount++;
}
if (r.curApp != null && r.curApp.crashing) {
// 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) {
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "Skipping delivery to " + info.activityInfo.packageName + " / " + info.activityInfo.applicationInfo.uid + " : package no longer available");
skip = true;
}
}
// broadcast.
if ((mService.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) && !skip) {
if (!requestStartTargetPermissionsReviewIfNeededLocked(r, info.activityInfo.packageName, UserHandle.getUserId(info.activityInfo.applicationInfo.uid))) {
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, false);
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
// (depending on how checkAllowBackgroundLocked 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))) {
Slog.w(TAG, "Background execution not allowed: receiving " + r.intent + " to " + component.flattenToShortString());
skip = true;
}
}
}
if (skip) {
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "Skipping delivery of ordered [" + mQueueName + "] " + r + " for whatever reason");
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
return;
}
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
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 = " + info.activityInfo.applicationInfo.uid);
}
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(receiverUid, brOptions.getTemporaryAppWhitelistDuration(), r);
}
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} 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.thread != null) {
try {
app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
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);
if ((r.curApp = mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false)) == 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 + "/" + info.activityInfo.applicationInfo.uid + " 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;
}
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
}
use of android.app.BroadcastOptions in project android_frameworks_base by DirtyUnicorns.
the class ConnectivityService method sendStickyBroadcast.
private void sendStickyBroadcast(Intent intent) {
synchronized (this) {
if (!mSystemReady) {
mInitialBroadcast = new Intent(intent);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (VDBG) {
log("sendStickyBroadcast: action=" + intent.getAction());
}
Bundle options = null;
final long ident = Binder.clearCallingIdentity();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
final NetworkInfo ni = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
} else {
BroadcastOptions opts = BroadcastOptions.makeBasic();
opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
options = opts.toBundle();
}
final IBatteryStats bs = BatteryStatsService.getService();
try {
bs.noteConnectivityChanged(intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE), ni != null ? ni.getState().toString() : "?");
} catch (RemoteException e) {
}
}
try {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
use of android.app.BroadcastOptions in project android_frameworks_base by AOSPA.
the class BroadcastQueue method processNextBroadcast.
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
BroadcastRecord r;
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "processNextBroadcast [" + mQueueName + "]: " + mParallelBroadcasts.size() + " broadcasts, " + mOrderedBroadcasts.size() + " ordered broadcasts");
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.dispatchClockTime = System.currentTimeMillis();
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);
}
// 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;
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
}
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 {
if (mOrderedBroadcasts.size() == 0) {
// No more broadcasts pending, so all done!
mService.scheduleAppGcsLocked();
if (looped) {
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
// adjustments.
mService.updateOomAdjLocked();
}
return;
}
r = mOrderedBroadcasts.get(0);
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 PRE_BOOT_COMPLETED
// receivers don't get executed with timeouts. They're intended for
// one time heavy lifting after system upgrades and can take
// significant amounts of time.
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) && (now > r.dispatchTime + (2 * mTimeoutPeriod * 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);
// forcibly finish this broadcast
broadcastTimeoutLocked(false);
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;
}
if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) {
// result if requested...
if (r.resultTo != null) {
try {
if (DEBUG_BROADCAST)
Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] " + r.intent.getAction() + " app=" + r.callerApp);
performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false, r.userId);
// 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);
}
mOrderedBroadcasts.remove(0);
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.dispatchClockTime = System.currentTimeMillis();
if (DEBUG_BROADCAST_LIGHT)
Slog.v(TAG_BROADCAST, "Processing ordered broadcast [" + mQueueName + "] " + r);
}
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
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);
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
// 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 (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(filter.owningUid, brOptions.getTemporaryAppWhitelistDuration(), r);
}
}
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())) {
skip = true;
}
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 " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" + " is not exported from uid " + info.activityInfo.applicationInfo.uid + " due to receiver " + component.flattenToShortString());
} else {
Slog.w(TAG, "Permission Denial: broadcasting " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" + " requires " + info.activityInfo.permission + " due to receiver " + component.flattenToShortString());
}
skip = true;
} else if (!skip && info.activityInfo.permission != null) {
final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
if (opCode != AppOpsManager.OP_NONE && mService.mAppOpsService.noteOperation(opCode, r.callingUid, r.callerPackage) != 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(info.activityInfo.permission) + " due to registered receiver " + component.flattenToShortString());
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 && mService.mAppOpsService.noteOperation(appOp, info.activityInfo.applicationInfo.uid, info.activityInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving " + r.intent + " to " + component.flattenToShortString() + " requires appop " + AppOpsManager.permissionToOp(requiredPermission) + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")");
skip = true;
break;
}
}
}
if (!skip && r.appOp != AppOpsManager.OP_NONE && mService.mAppOpsService.noteOperation(r.appOp, info.activityInfo.applicationInfo.uid, info.activityInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving " + r.intent + " to " + component.flattenToShortString() + " requires appop " + AppOpsManager.opToName(r.appOp) + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")");
skip = true;
}
if (!skip) {
skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid, r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
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) {
r.manifestCount++;
} else {
r.manifestSkipCount++;
}
if (r.curApp != null && r.curApp.crashing) {
// 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) {
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "Skipping delivery to " + info.activityInfo.packageName + " / " + info.activityInfo.applicationInfo.uid + " : package no longer available");
skip = true;
}
}
// broadcast.
if ((mService.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) && !skip) {
if (!requestStartTargetPermissionsReviewIfNeededLocked(r, info.activityInfo.packageName, UserHandle.getUserId(info.activityInfo.applicationInfo.uid))) {
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, false);
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
// (depending on how checkAllowBackgroundLocked 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))) {
Slog.w(TAG, "Background execution not allowed: receiving " + r.intent + " to " + component.flattenToShortString());
skip = true;
}
}
}
if (skip) {
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "Skipping delivery of ordered [" + mQueueName + "] " + r + " for whatever reason");
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
return;
}
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
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 = " + info.activityInfo.applicationInfo.uid);
}
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(receiverUid, brOptions.getTemporaryAppWhitelistDuration(), r);
}
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} 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.thread != null) {
try {
app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
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.
resetBroadcastStateAndContinueDelivery(r);
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);
// skip send broadcast to the receiver whoes user has not started.
if (!isUserOfReceiverStarted(info)) {
resetBroadcastStateAndContinueDelivery(r);
return;
}
if ((r.curApp = mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false)) == 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 + "/" + info.activityInfo.applicationInfo.uid + " for broadcast " + r.intent + ": process is bad");
resetBroadcastStateAndContinueDelivery(r);
return;
}
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
}
use of android.app.BroadcastOptions in project android_frameworks_base by crdroidandroid.
the class ConnectivityService method sendStickyBroadcast.
private void sendStickyBroadcast(Intent intent) {
synchronized (this) {
if (!mSystemReady) {
mInitialBroadcast = new Intent(intent);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (VDBG) {
log("sendStickyBroadcast: action=" + intent.getAction());
}
Bundle options = null;
final long ident = Binder.clearCallingIdentity();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
final NetworkInfo ni = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
} else {
BroadcastOptions opts = BroadcastOptions.makeBasic();
opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
options = opts.toBundle();
}
final IBatteryStats bs = BatteryStatsService.getService();
try {
bs.noteConnectivityChanged(intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE), ni != null ? ni.getState().toString() : "?");
} catch (RemoteException e) {
}
}
try {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
use of android.app.BroadcastOptions in project android_frameworks_base by crdroidandroid.
the class BroadcastQueue method processNextBroadcast.
final void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
BroadcastRecord r;
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "processNextBroadcast [" + mQueueName + "]: " + mParallelBroadcasts.size() + " broadcasts, " + mOrderedBroadcasts.size() + " ordered broadcasts");
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.dispatchClockTime = System.currentTimeMillis();
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);
}
// 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;
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
}
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 {
if (mOrderedBroadcasts.size() == 0) {
// No more broadcasts pending, so all done!
mService.scheduleAppGcsLocked();
if (looped) {
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
// adjustments.
mService.updateOomAdjLocked();
}
return;
}
r = mOrderedBroadcasts.get(0);
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 PRE_BOOT_COMPLETED
// receivers don't get executed with timeouts. They're intended for
// one time heavy lifting after system upgrades and can take
// significant amounts of time.
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) && (now > r.dispatchTime + (2 * mTimeoutPeriod * 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);
// forcibly finish this broadcast
broadcastTimeoutLocked(false);
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;
}
if (r.receivers == null || r.nextReceiver >= numReceivers || r.resultAbort || forceReceive) {
// result if requested...
if (r.resultTo != null) {
try {
if (DEBUG_BROADCAST)
Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] " + r.intent.getAction() + " app=" + r.callerApp);
performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false, r.userId);
// 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);
}
mOrderedBroadcasts.remove(0);
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.dispatchClockTime = System.currentTimeMillis();
if (DEBUG_BROADCAST_LIGHT)
Slog.v(TAG_BROADCAST, "Processing ordered broadcast [" + mQueueName + "] " + r);
}
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
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);
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if (r.receiver == null || !r.ordered) {
// 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 (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(filter.owningUid, brOptions.getTemporaryAppWhitelistDuration(), r);
}
}
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())) {
skip = true;
}
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 " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" + " is not exported from uid " + info.activityInfo.applicationInfo.uid + " due to receiver " + component.flattenToShortString());
} else {
Slog.w(TAG, "Permission Denial: broadcasting " + r.intent.toString() + " from " + r.callerPackage + " (pid=" + r.callingPid + ", uid=" + r.callingUid + ")" + " requires " + info.activityInfo.permission + " due to receiver " + component.flattenToShortString());
}
skip = true;
} else if (!skip && info.activityInfo.permission != null) {
final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
if (opCode != AppOpsManager.OP_NONE && mService.mAppOpsService.noteOperation(opCode, r.callingUid, r.callerPackage) != 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(info.activityInfo.permission) + " due to registered receiver " + component.flattenToShortString());
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 && mService.mAppOpsService.noteOperation(appOp, info.activityInfo.applicationInfo.uid, info.activityInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving " + r.intent + " to " + component.flattenToShortString() + " requires appop " + AppOpsManager.permissionToOp(requiredPermission) + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")");
skip = true;
break;
}
}
}
if (!skip && r.appOp != AppOpsManager.OP_NONE && mService.mAppOpsService.noteOperation(r.appOp, info.activityInfo.applicationInfo.uid, info.activityInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: receiving " + r.intent + " to " + component.flattenToShortString() + " requires appop " + AppOpsManager.opToName(r.appOp) + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")");
skip = true;
}
if (!skip) {
skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid, r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
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) {
r.manifestCount++;
} else {
r.manifestSkipCount++;
}
if (r.curApp != null && r.curApp.crashing) {
// 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 && !mService.mUserController.isUserRunningLocked(UserHandle.getUserId(info.activityInfo.applicationInfo.uid), 0)) {
Slog.w(TAG, "Skipping delivery to " + info.activityInfo.packageName + " / " + info.activityInfo.applicationInfo.uid + " : user is stopped");
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) {
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "Skipping delivery to " + info.activityInfo.packageName + " / " + info.activityInfo.applicationInfo.uid + " : package no longer available");
skip = true;
}
}
// broadcast.
if ((mService.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) && !skip) {
if (!requestStartTargetPermissionsReviewIfNeededLocked(r, info.activityInfo.packageName, UserHandle.getUserId(info.activityInfo.applicationInfo.uid))) {
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, false);
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
// (depending on how checkAllowBackgroundLocked 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))) {
Slog.w(TAG, "Background execution not allowed: receiving " + r.intent + " to " + component.flattenToShortString());
skip = true;
}
}
}
if (skip) {
if (DEBUG_BROADCAST)
Slog.v(TAG_BROADCAST, "Skipping delivery of ordered [" + mQueueName + "] " + r + " for whatever reason");
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
return;
}
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
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 = " + info.activityInfo.applicationInfo.uid);
}
if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
scheduleTempWhitelistLocked(receiverUid, brOptions.getTemporaryAppWhitelistDuration(), r);
}
// Broadcast is being executed, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} 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.thread != null) {
try {
app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
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.
resetBroadcastStateAndContinueDelivery(r);
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);
// skip send broadcast to the receiver whoes user has not started.
if (!isUserOfReceiverStarted(info)) {
resetBroadcastStateAndContinueDelivery(r);
return;
}
if ((r.curApp = mService.startProcessLocked(targetProcess, info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, "broadcast", r.curComponent, (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false)) == 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 + "/" + info.activityInfo.applicationInfo.uid + " for broadcast " + r.intent + ": process is bad");
resetBroadcastStateAndContinueDelivery(r);
return;
}
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
}
Aggregations