Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +102 −6 Original line number Diff line number Diff line Loading @@ -382,6 +382,16 @@ public class JobSchedulerService extends com.android.server.SystemService * A mapping of which uids are currently in the foreground to their effective bias. */ final SparseIntArray mUidBiasOverride = new SparseIntArray(); /** * A cached mapping of uids to their current capabilities. */ @GuardedBy("mLock") private final SparseIntArray mUidCapabilities = new SparseIntArray(); /** * A cached mapping of uids to their proc states. */ @GuardedBy("mLock") private final SparseIntArray mUidProcStates = new SparseIntArray(); /** * Which uids are currently performing backups, so we shouldn't allow their jobs to run. Loading Loading @@ -1158,6 +1168,14 @@ public class JobSchedulerService extends com.android.server.SystemService mDebuggableApps.remove(pkgName); mConcurrencyManager.onAppRemovedLocked(pkgName, pkgUid); } } else if (Intent.ACTION_UID_REMOVED.equals(action)) { if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { synchronized (mLock) { mUidBiasOverride.delete(pkgUid); mUidCapabilities.delete(pkgUid); mUidProcStates.delete(pkgUid); } } } else if (Intent.ACTION_USER_ADDED.equals(action)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); synchronized (mLock) { Loading Loading @@ -1236,7 +1254,11 @@ public class JobSchedulerService extends com.android.server.SystemService final private IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget(); final SomeArgs args = SomeArgs.obtain(); args.argi1 = uid; args.argi2 = procState; args.argi3 = capability; mHandler.obtainMessage(MSG_UID_STATE_CHANGED, args).sendToTarget(); } @Override public void onUidGone(int uid, boolean disabled) { Loading Loading @@ -1978,8 +2000,14 @@ public class JobSchedulerService extends com.android.server.SystemService } } void updateUidState(int uid, int procState) { void updateUidState(int uid, int procState, int capabilities) { if (DEBUG) { Slog.d(TAG, "UID " + uid + " proc state changed to " + ActivityManager.procStateToString(procState) + " with capabilities=" + ActivityManager.getCapabilitiesSummary(capabilities)); } synchronized (mLock) { mUidProcStates.put(uid, procState); final int prevBias = mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT); if (procState == ActivityManager.PROCESS_STATE_TOP) { // Only use this if we are exactly the top app. All others can live Loading @@ -1993,6 +2021,12 @@ public class JobSchedulerService extends com.android.server.SystemService } else { mUidBiasOverride.delete(uid); } if (capabilities == ActivityManager.PROCESS_CAPABILITY_NONE || procState == ActivityManager.PROCESS_STATE_NONEXISTENT) { mUidCapabilities.delete(uid); } else { mUidCapabilities.put(uid, capabilities); } final int newBias = mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT); if (prevBias != newBias) { if (DEBUG) { Loading @@ -2013,6 +2047,23 @@ public class JobSchedulerService extends com.android.server.SystemService } } /** * Return the current {@link ActivityManager#PROCESS_CAPABILITY_ALL capabilities} * of the given UID. */ public int getUidCapabilities(int uid) { synchronized (mLock) { return mUidCapabilities.get(uid, ActivityManager.PROCESS_CAPABILITY_NONE); } } /** Return the current proc state of the given UID. */ public int getUidProcState(int uid) { synchronized (mLock) { return mUidProcStates.get(uid, ActivityManager.PROCESS_STATE_UNKNOWN); } } @Override public void onDeviceIdleStateChanged(boolean deviceIdle) { synchronized (mLock) { Loading Loading @@ -2276,6 +2327,9 @@ public class JobSchedulerService extends com.android.server.SystemService filter.addDataScheme("package"); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, filter, null, null); final IntentFilter uidFilter = new IntentFilter(Intent.ACTION_UID_REMOVED); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, uidFilter, null, null); final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); userFilter.addAction(Intent.ACTION_USER_ADDED); getContext().registerReceiverAsUser( Loading Loading @@ -2807,15 +2861,19 @@ public class JobSchedulerService extends com.android.server.SystemService break; case MSG_UID_STATE_CHANGED: { final int uid = message.arg1; final int procState = message.arg2; updateUidState(uid, procState); final SomeArgs args = (SomeArgs) message.obj; final int uid = args.argi1; final int procState = args.argi2; final int capabilities = args.argi3; updateUidState(uid, procState, capabilities); args.recycle(); break; } case MSG_UID_GONE: { final int uid = message.arg1; final boolean disabled = message.arg2 != 0; updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY, ActivityManager.PROCESS_CAPABILITY_NONE); if (disabled) { cancelJobsForUid(uid, /* includeSourceApp */ true, Loading Loading @@ -4891,6 +4949,25 @@ public class JobSchedulerService extends com.android.server.SystemService pw.decreaseIndent(); } boolean procStatePrinted = false; for (int i = 0; i < mUidProcStates.size(); i++) { int uid = mUidProcStates.keyAt(i); if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { if (!procStatePrinted) { procStatePrinted = true; pw.println(); pw.println("Uid proc states:"); pw.increaseIndent(); } pw.print(UserHandle.formatUid(uid)); pw.print(": "); pw.println(ActivityManager.procStateToString(mUidProcStates.valueAt(i))); } } if (procStatePrinted) { pw.decreaseIndent(); } boolean overridePrinted = false; for (int i = 0; i < mUidBiasOverride.size(); i++) { int uid = mUidBiasOverride.keyAt(i); Loading @@ -4909,6 +4986,25 @@ public class JobSchedulerService extends com.android.server.SystemService pw.decreaseIndent(); } boolean capabilitiesPrinted = false; for (int i = 0; i < mUidCapabilities.size(); i++) { int uid = mUidCapabilities.keyAt(i); if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { if (!capabilitiesPrinted) { capabilitiesPrinted = true; pw.println(); pw.println("Uid capabilities:"); pw.increaseIndent(); } pw.print(UserHandle.formatUid(uid)); pw.print(": "); pw.println(ActivityManager.getCapabilitiesSummary(mUidCapabilities.valueAt(i))); } } if (capabilitiesPrinted) { pw.decreaseIndent(); } boolean uidMapPrinted = false; for (int i = 0; i < mUidToPackageCache.size(); ++i) { final int uid = mUidToPackageCache.keyAt(i); Loading apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +18 −14 Original line number Diff line number Diff line Loading @@ -393,23 +393,27 @@ public final class JobServiceContext implements ServiceConnection { .setFlags(Intent.FLAG_FROM_BACKGROUND); boolean binding = false; try { final int bindFlags; final Context.BindServiceFlags bindFlags; if (job.shouldTreatAsUserInitiatedJob()) { // TODO (191785864, 261999509): add an appropriate flag so user-initiated jobs // can bypass data saver bindFlags = Context.BIND_AUTO_CREATE bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE; | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE); } else if (job.shouldTreatAsExpeditedJob()) { bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE; | Context.BIND_NOT_APP_COMPONENT_USAGE); } else { bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_PERCEPTIBLE | Context.BIND_NOT_APP_COMPONENT_USAGE; | Context.BIND_NOT_APP_COMPONENT_USAGE); } binding = mContext.bindServiceAsUser(intent, this, bindFlags, UserHandle.of(job.getUserId())); Loading apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +154 −6 Original line number Diff line number Diff line Loading @@ -18,15 +18,18 @@ package com.android.server.job.controllers; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.job.JobInfo; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.INetworkPolicyListener; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicyManager; Loading @@ -47,6 +50,7 @@ import android.util.Log; import android.util.Pools; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; Loading Loading @@ -98,13 +102,12 @@ public final class ConnectivityController extends RestrictingController implemen ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER | ConnectivityManager.BLOCKED_REASON_DOZE); // TODO(261999509): allow bypassing data saver & user-restricted. However, when we allow a UI // job to run while data saver restricts the app, we must ensure that we don't run regular // jobs when we put a hole in the data saver wall for the UI job private static final int UNBYPASSABLE_UI_BLOCKED_REASONS = ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER | ConnectivityManager.BLOCKED_REASON_DOZE); | ConnectivityManager.BLOCKED_REASON_DOZE | ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER | ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED); private static final int UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS = ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER Loading @@ -113,6 +116,7 @@ public final class ConnectivityController extends RestrictingController implemen | ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED); private final ConnectivityManager mConnManager; private final NetworkPolicyManager mNetPolicyManager; private final NetworkPolicyManagerInternal mNetPolicyManagerInternal; private final FlexibilityController mFlexibilityController; Loading Loading @@ -241,6 +245,8 @@ public final class ConnectivityController extends RestrictingController implemen */ private final List<UidStats> mSortedStats = new ArrayList<>(); @GuardedBy("mLock") private final SparseBooleanArray mBackgroundMeteredAllowed = new SparseBooleanArray(); @GuardedBy("mLock") private long mLastCallbackAdjustmentTimeElapsed; @GuardedBy("mLock") private final SparseArray<CellSignalStrengthCallback> mSignalStrengths = new SparseArray<>(); Loading @@ -250,6 +256,8 @@ public final class ConnectivityController extends RestrictingController implemen private static final int MSG_ADJUST_CALLBACKS = 0; private static final int MSG_UPDATE_ALL_TRACKED_JOBS = 1; private static final int MSG_DATA_SAVER_TOGGLED = 2; private static final int MSG_UID_POLICIES_CHANGED = 3; private final Handler mHandler; Loading @@ -259,6 +267,7 @@ public final class ConnectivityController extends RestrictingController implemen mHandler = new CcHandler(AppSchedulingModuleThread.get().getLooper()); mConnManager = mContext.getSystemService(ConnectivityManager.class); mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); mNetPolicyManagerInternal = LocalServices.getService(NetworkPolicyManagerInternal.class); mFlexibilityController = flexibilityController; Loading @@ -266,6 +275,8 @@ public final class ConnectivityController extends RestrictingController implemen // network changes against the active network for each UID with jobs. final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); mConnManager.registerNetworkCallback(request, mNetworkCallback); mNetPolicyManager.registerListener(mNetPolicyListener); } @GuardedBy("mLock") Loading Loading @@ -530,6 +541,7 @@ public final class ConnectivityController extends RestrictingController implemen // All packages in the UID have been removed. It's safe to remove things based on // UID alone. mTrackedJobs.delete(uid); mBackgroundMeteredAllowed.delete(uid); UidStats uidStats = mUidStats.removeReturnOld(uid); unregisterDefaultNetworkCallbackLocked(uid, sElapsedRealtimeClock.millis()); mSortedStats.remove(uidStats); Loading @@ -549,6 +561,12 @@ public final class ConnectivityController extends RestrictingController implemen mUidStats.removeAt(u); } } for (int u = mBackgroundMeteredAllowed.size() - 1; u >= 0; --u) { final int uid = mBackgroundMeteredAllowed.keyAt(u); if (UserHandle.getUserId(uid) == userId) { mBackgroundMeteredAllowed.removeAt(u); } } postAdjustCallbacks(); } Loading Loading @@ -666,6 +684,88 @@ public final class ConnectivityController extends RestrictingController implemen return false; } private boolean isMeteredAllowed(@NonNull JobStatus jobStatus, @NonNull NetworkCapabilities networkCapabilities) { // Network isn't metered. Usage is allowed. The rest of this method doesn't apply. if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) || networkCapabilities.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)) { return true; } final int uid = jobStatus.getSourceUid(); final int procState = mService.getUidProcState(uid); final int capabilities = mService.getUidCapabilities(uid); // Jobs don't raise the proc state to anything better than IMPORTANT_FOREGROUND. // If the app is in a better state, see if it has the capability to use the metered network. final boolean currentStateAllows = procState != ActivityManager.PROCESS_STATE_UNKNOWN && procState < ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND && NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground( procState, capabilities); if (DEBUG) { Slog.d(TAG, "UID " + uid + " current state allows metered network=" + currentStateAllows + " procState=" + ActivityManager.procStateToString(procState) + " capabilities=" + ActivityManager.getCapabilitiesSummary(capabilities)); } if (currentStateAllows) { return true; } if ((jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { final int expectedProcState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; final int mergedCapabilities = capabilities | NetworkPolicyManager.getDefaultProcessNetworkCapabilities(expectedProcState); final boolean wouldBeAllowed = NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground( expectedProcState, mergedCapabilities); if (DEBUG) { Slog.d(TAG, "UID " + uid + " willBeForeground flag allows metered network=" + wouldBeAllowed + " capabilities=" + ActivityManager.getCapabilitiesSummary(mergedCapabilities)); } if (wouldBeAllowed) { return true; } } if (jobStatus.shouldTreatAsUserInitiatedJob()) { // Since the job is initiated by the user and will be visible to the user, it // should be able to run on metered networks, similar to FGS. // With user-initiated jobs, JobScheduler will request that the process // run at IMPORTANT_FOREGROUND process state // and get the USER_RESTRICTED_NETWORK process capability. final int expectedProcState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; final int mergedCapabilities = capabilities | ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK | NetworkPolicyManager.getDefaultProcessNetworkCapabilities(expectedProcState); final boolean wouldBeAllowed = NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground( expectedProcState, mergedCapabilities); if (DEBUG) { Slog.d(TAG, "UID " + uid + " UI job state allows metered network=" + wouldBeAllowed + " capabilities=" + mergedCapabilities); } if (wouldBeAllowed) { return true; } } if (mBackgroundMeteredAllowed.indexOfKey(uid) >= 0) { return mBackgroundMeteredAllowed.get(uid); } final boolean allowed = mNetPolicyManager.getRestrictBackgroundStatus(uid) != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; if (DEBUG) { Slog.d(TAG, "UID " + uid + " allowed in data saver=" + allowed); } mBackgroundMeteredAllowed.put(uid, allowed); return allowed; } /** * Return the estimated amount of time this job will be transferring data, * based on the current network speed. Loading Loading @@ -859,6 +959,12 @@ public final class ConnectivityController extends RestrictingController implemen // First, are we insane? if (isInsane(jobStatus, network, capabilities, constants)) return false; // User-initiated jobs might make NetworkPolicyManager open up network access for // the whole UID. If network access is opened up just because of UI jobs, we want // to make sure that non-UI jobs don't run during that time, // so make sure the job can make use of the metered network at this time. if (!isMeteredAllowed(jobStatus, capabilities)) return false; // Second, is the network congested? if (isCongestionDelayed(jobStatus, network, capabilities, constants)) return false; Loading Loading @@ -1138,9 +1244,10 @@ public final class ConnectivityController extends RestrictingController implemen // but it doesn't yet satisfy the requested constraints and the old network // is still available and satisfies the constraints. Don't change the network // given to the job for now and let it keep running. We will re-evaluate when // the capabilities or connection state of the either network change. // the capabilities or connection state of either network change. if (DEBUG) { Slog.i(TAG, "Not reassigning network for running job " + jobStatus); Slog.i(TAG, "Not reassigning network from " + jobStatus.network + " to " + network + " for running job " + jobStatus); } return false; } Loading Loading @@ -1389,6 +1496,26 @@ public final class ConnectivityController extends RestrictingController implemen } }; private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() { @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { if (DEBUG) { Slog.v(TAG, "onRestrictBackgroundChanged: " + restrictBackground); } mHandler.obtainMessage(MSG_DATA_SAVER_TOGGLED).sendToTarget(); } @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { if (DEBUG) { Slog.v(TAG, "onUidPoliciesChanged: " + uid); } mHandler.obtainMessage(MSG_UID_POLICIES_CHANGED, uid, mNetPolicyManager.getRestrictBackgroundStatus(uid)) .sendToTarget(); } }; private class CcHandler extends Handler { CcHandler(Looper looper) { super(looper); Loading @@ -1410,6 +1537,27 @@ public final class ConnectivityController extends RestrictingController implemen updateAllTrackedJobsLocked(allowThrottle); } break; case MSG_DATA_SAVER_TOGGLED: removeMessages(MSG_DATA_SAVER_TOGGLED); synchronized (mLock) { mBackgroundMeteredAllowed.clear(); updateTrackedJobsLocked(-1, null); } break; case MSG_UID_POLICIES_CHANGED: final int uid = msg.arg1; final boolean newAllowed = msg.arg2 != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; synchronized (mLock) { final boolean oldAllowed = mBackgroundMeteredAllowed.get(uid); if (oldAllowed != newAllowed) { mBackgroundMeteredAllowed.put(uid, newAllowed); updateTrackedJobsLocked(uid, null); } } break; } } } Loading core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -151,6 +151,7 @@ package android.app { field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6 field public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 8; // 0x8 field public static final int PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK = 32; // 0x20 field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4 field public static final int PROCESS_STATE_TOP = 2; // 0x2 field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff Loading core/java/android/app/ActivityManager.java +12 −1 Original line number Diff line number Diff line Loading @@ -794,6 +794,7 @@ public class ActivityManager { PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK, PROCESS_CAPABILITY_BFSL, PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK, }) @Retention(RetentionPolicy.SOURCE) public @interface ProcessCapability {} Loading Loading @@ -935,6 +936,13 @@ public class ActivityManager { */ public static final int PROCESS_CAPABILITY_BFSL = 1 << 4; /** * @hide * Process can access network at a high enough proc state despite any user restrictions. */ @TestApi public static final int PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK = 1 << 5; /** * @hide all capabilities, the ORing of all flags in {@link ProcessCapability}. * Loading @@ -945,7 +953,8 @@ public class ActivityManager { | PROCESS_CAPABILITY_FOREGROUND_CAMERA | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE | PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK | PROCESS_CAPABILITY_BFSL; | PROCESS_CAPABILITY_BFSL | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; /** * All implicit capabilities. There are capabilities that process automatically have. Loading @@ -965,6 +974,7 @@ public class ActivityManager { pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-'); pw.print((caps & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0 ? 'N' : '-'); pw.print((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-'); pw.print((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-'); } /** @hide */ Loading @@ -974,6 +984,7 @@ public class ActivityManager { sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-'); sb.append((caps & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0 ? 'N' : '-'); sb.append((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-'); sb.append((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-'); } /** Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +102 −6 Original line number Diff line number Diff line Loading @@ -382,6 +382,16 @@ public class JobSchedulerService extends com.android.server.SystemService * A mapping of which uids are currently in the foreground to their effective bias. */ final SparseIntArray mUidBiasOverride = new SparseIntArray(); /** * A cached mapping of uids to their current capabilities. */ @GuardedBy("mLock") private final SparseIntArray mUidCapabilities = new SparseIntArray(); /** * A cached mapping of uids to their proc states. */ @GuardedBy("mLock") private final SparseIntArray mUidProcStates = new SparseIntArray(); /** * Which uids are currently performing backups, so we shouldn't allow their jobs to run. Loading Loading @@ -1158,6 +1168,14 @@ public class JobSchedulerService extends com.android.server.SystemService mDebuggableApps.remove(pkgName); mConcurrencyManager.onAppRemovedLocked(pkgName, pkgUid); } } else if (Intent.ACTION_UID_REMOVED.equals(action)) { if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { synchronized (mLock) { mUidBiasOverride.delete(pkgUid); mUidCapabilities.delete(pkgUid); mUidProcStates.delete(pkgUid); } } } else if (Intent.ACTION_USER_ADDED.equals(action)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); synchronized (mLock) { Loading Loading @@ -1236,7 +1254,11 @@ public class JobSchedulerService extends com.android.server.SystemService final private IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) { mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget(); final SomeArgs args = SomeArgs.obtain(); args.argi1 = uid; args.argi2 = procState; args.argi3 = capability; mHandler.obtainMessage(MSG_UID_STATE_CHANGED, args).sendToTarget(); } @Override public void onUidGone(int uid, boolean disabled) { Loading Loading @@ -1978,8 +2000,14 @@ public class JobSchedulerService extends com.android.server.SystemService } } void updateUidState(int uid, int procState) { void updateUidState(int uid, int procState, int capabilities) { if (DEBUG) { Slog.d(TAG, "UID " + uid + " proc state changed to " + ActivityManager.procStateToString(procState) + " with capabilities=" + ActivityManager.getCapabilitiesSummary(capabilities)); } synchronized (mLock) { mUidProcStates.put(uid, procState); final int prevBias = mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT); if (procState == ActivityManager.PROCESS_STATE_TOP) { // Only use this if we are exactly the top app. All others can live Loading @@ -1993,6 +2021,12 @@ public class JobSchedulerService extends com.android.server.SystemService } else { mUidBiasOverride.delete(uid); } if (capabilities == ActivityManager.PROCESS_CAPABILITY_NONE || procState == ActivityManager.PROCESS_STATE_NONEXISTENT) { mUidCapabilities.delete(uid); } else { mUidCapabilities.put(uid, capabilities); } final int newBias = mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT); if (prevBias != newBias) { if (DEBUG) { Loading @@ -2013,6 +2047,23 @@ public class JobSchedulerService extends com.android.server.SystemService } } /** * Return the current {@link ActivityManager#PROCESS_CAPABILITY_ALL capabilities} * of the given UID. */ public int getUidCapabilities(int uid) { synchronized (mLock) { return mUidCapabilities.get(uid, ActivityManager.PROCESS_CAPABILITY_NONE); } } /** Return the current proc state of the given UID. */ public int getUidProcState(int uid) { synchronized (mLock) { return mUidProcStates.get(uid, ActivityManager.PROCESS_STATE_UNKNOWN); } } @Override public void onDeviceIdleStateChanged(boolean deviceIdle) { synchronized (mLock) { Loading Loading @@ -2276,6 +2327,9 @@ public class JobSchedulerService extends com.android.server.SystemService filter.addDataScheme("package"); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, filter, null, null); final IntentFilter uidFilter = new IntentFilter(Intent.ACTION_UID_REMOVED); getContext().registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, uidFilter, null, null); final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); userFilter.addAction(Intent.ACTION_USER_ADDED); getContext().registerReceiverAsUser( Loading Loading @@ -2807,15 +2861,19 @@ public class JobSchedulerService extends com.android.server.SystemService break; case MSG_UID_STATE_CHANGED: { final int uid = message.arg1; final int procState = message.arg2; updateUidState(uid, procState); final SomeArgs args = (SomeArgs) message.obj; final int uid = args.argi1; final int procState = args.argi2; final int capabilities = args.argi3; updateUidState(uid, procState, capabilities); args.recycle(); break; } case MSG_UID_GONE: { final int uid = message.arg1; final boolean disabled = message.arg2 != 0; updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY, ActivityManager.PROCESS_CAPABILITY_NONE); if (disabled) { cancelJobsForUid(uid, /* includeSourceApp */ true, Loading Loading @@ -4891,6 +4949,25 @@ public class JobSchedulerService extends com.android.server.SystemService pw.decreaseIndent(); } boolean procStatePrinted = false; for (int i = 0; i < mUidProcStates.size(); i++) { int uid = mUidProcStates.keyAt(i); if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { if (!procStatePrinted) { procStatePrinted = true; pw.println(); pw.println("Uid proc states:"); pw.increaseIndent(); } pw.print(UserHandle.formatUid(uid)); pw.print(": "); pw.println(ActivityManager.procStateToString(mUidProcStates.valueAt(i))); } } if (procStatePrinted) { pw.decreaseIndent(); } boolean overridePrinted = false; for (int i = 0; i < mUidBiasOverride.size(); i++) { int uid = mUidBiasOverride.keyAt(i); Loading @@ -4909,6 +4986,25 @@ public class JobSchedulerService extends com.android.server.SystemService pw.decreaseIndent(); } boolean capabilitiesPrinted = false; for (int i = 0; i < mUidCapabilities.size(); i++) { int uid = mUidCapabilities.keyAt(i); if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) { if (!capabilitiesPrinted) { capabilitiesPrinted = true; pw.println(); pw.println("Uid capabilities:"); pw.increaseIndent(); } pw.print(UserHandle.formatUid(uid)); pw.print(": "); pw.println(ActivityManager.getCapabilitiesSummary(mUidCapabilities.valueAt(i))); } } if (capabilitiesPrinted) { pw.decreaseIndent(); } boolean uidMapPrinted = false; for (int i = 0; i < mUidToPackageCache.size(); ++i) { final int uid = mUidToPackageCache.keyAt(i); Loading
apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +18 −14 Original line number Diff line number Diff line Loading @@ -393,23 +393,27 @@ public final class JobServiceContext implements ServiceConnection { .setFlags(Intent.FLAG_FROM_BACKGROUND); boolean binding = false; try { final int bindFlags; final Context.BindServiceFlags bindFlags; if (job.shouldTreatAsUserInitiatedJob()) { // TODO (191785864, 261999509): add an appropriate flag so user-initiated jobs // can bypass data saver bindFlags = Context.BIND_AUTO_CREATE bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE; | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE); } else if (job.shouldTreatAsExpeditedJob()) { bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE; | Context.BIND_NOT_APP_COMPONENT_USAGE); } else { bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_PERCEPTIBLE | Context.BIND_NOT_APP_COMPONENT_USAGE; | Context.BIND_NOT_APP_COMPONENT_USAGE); } binding = mContext.bindServiceAsUser(intent, this, bindFlags, UserHandle.of(job.getUserId())); Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +154 −6 Original line number Diff line number Diff line Loading @@ -18,15 +18,18 @@ package com.android.server.job.controllers; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.job.JobInfo; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.INetworkPolicyListener; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicyManager; Loading @@ -47,6 +50,7 @@ import android.util.Log; import android.util.Pools; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; Loading Loading @@ -98,13 +102,12 @@ public final class ConnectivityController extends RestrictingController implemen ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER | ConnectivityManager.BLOCKED_REASON_DOZE); // TODO(261999509): allow bypassing data saver & user-restricted. However, when we allow a UI // job to run while data saver restricts the app, we must ensure that we don't run regular // jobs when we put a hole in the data saver wall for the UI job private static final int UNBYPASSABLE_UI_BLOCKED_REASONS = ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER | ConnectivityManager.BLOCKED_REASON_DOZE); | ConnectivityManager.BLOCKED_REASON_DOZE | ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER | ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED); private static final int UNBYPASSABLE_FOREGROUND_BLOCKED_REASONS = ~(ConnectivityManager.BLOCKED_REASON_APP_STANDBY | ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER Loading @@ -113,6 +116,7 @@ public final class ConnectivityController extends RestrictingController implemen | ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED); private final ConnectivityManager mConnManager; private final NetworkPolicyManager mNetPolicyManager; private final NetworkPolicyManagerInternal mNetPolicyManagerInternal; private final FlexibilityController mFlexibilityController; Loading Loading @@ -241,6 +245,8 @@ public final class ConnectivityController extends RestrictingController implemen */ private final List<UidStats> mSortedStats = new ArrayList<>(); @GuardedBy("mLock") private final SparseBooleanArray mBackgroundMeteredAllowed = new SparseBooleanArray(); @GuardedBy("mLock") private long mLastCallbackAdjustmentTimeElapsed; @GuardedBy("mLock") private final SparseArray<CellSignalStrengthCallback> mSignalStrengths = new SparseArray<>(); Loading @@ -250,6 +256,8 @@ public final class ConnectivityController extends RestrictingController implemen private static final int MSG_ADJUST_CALLBACKS = 0; private static final int MSG_UPDATE_ALL_TRACKED_JOBS = 1; private static final int MSG_DATA_SAVER_TOGGLED = 2; private static final int MSG_UID_POLICIES_CHANGED = 3; private final Handler mHandler; Loading @@ -259,6 +267,7 @@ public final class ConnectivityController extends RestrictingController implemen mHandler = new CcHandler(AppSchedulingModuleThread.get().getLooper()); mConnManager = mContext.getSystemService(ConnectivityManager.class); mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); mNetPolicyManagerInternal = LocalServices.getService(NetworkPolicyManagerInternal.class); mFlexibilityController = flexibilityController; Loading @@ -266,6 +275,8 @@ public final class ConnectivityController extends RestrictingController implemen // network changes against the active network for each UID with jobs. final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); mConnManager.registerNetworkCallback(request, mNetworkCallback); mNetPolicyManager.registerListener(mNetPolicyListener); } @GuardedBy("mLock") Loading Loading @@ -530,6 +541,7 @@ public final class ConnectivityController extends RestrictingController implemen // All packages in the UID have been removed. It's safe to remove things based on // UID alone. mTrackedJobs.delete(uid); mBackgroundMeteredAllowed.delete(uid); UidStats uidStats = mUidStats.removeReturnOld(uid); unregisterDefaultNetworkCallbackLocked(uid, sElapsedRealtimeClock.millis()); mSortedStats.remove(uidStats); Loading @@ -549,6 +561,12 @@ public final class ConnectivityController extends RestrictingController implemen mUidStats.removeAt(u); } } for (int u = mBackgroundMeteredAllowed.size() - 1; u >= 0; --u) { final int uid = mBackgroundMeteredAllowed.keyAt(u); if (UserHandle.getUserId(uid) == userId) { mBackgroundMeteredAllowed.removeAt(u); } } postAdjustCallbacks(); } Loading Loading @@ -666,6 +684,88 @@ public final class ConnectivityController extends RestrictingController implemen return false; } private boolean isMeteredAllowed(@NonNull JobStatus jobStatus, @NonNull NetworkCapabilities networkCapabilities) { // Network isn't metered. Usage is allowed. The rest of this method doesn't apply. if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) || networkCapabilities.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)) { return true; } final int uid = jobStatus.getSourceUid(); final int procState = mService.getUidProcState(uid); final int capabilities = mService.getUidCapabilities(uid); // Jobs don't raise the proc state to anything better than IMPORTANT_FOREGROUND. // If the app is in a better state, see if it has the capability to use the metered network. final boolean currentStateAllows = procState != ActivityManager.PROCESS_STATE_UNKNOWN && procState < ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND && NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground( procState, capabilities); if (DEBUG) { Slog.d(TAG, "UID " + uid + " current state allows metered network=" + currentStateAllows + " procState=" + ActivityManager.procStateToString(procState) + " capabilities=" + ActivityManager.getCapabilitiesSummary(capabilities)); } if (currentStateAllows) { return true; } if ((jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) { final int expectedProcState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; final int mergedCapabilities = capabilities | NetworkPolicyManager.getDefaultProcessNetworkCapabilities(expectedProcState); final boolean wouldBeAllowed = NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground( expectedProcState, mergedCapabilities); if (DEBUG) { Slog.d(TAG, "UID " + uid + " willBeForeground flag allows metered network=" + wouldBeAllowed + " capabilities=" + ActivityManager.getCapabilitiesSummary(mergedCapabilities)); } if (wouldBeAllowed) { return true; } } if (jobStatus.shouldTreatAsUserInitiatedJob()) { // Since the job is initiated by the user and will be visible to the user, it // should be able to run on metered networks, similar to FGS. // With user-initiated jobs, JobScheduler will request that the process // run at IMPORTANT_FOREGROUND process state // and get the USER_RESTRICTED_NETWORK process capability. final int expectedProcState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; final int mergedCapabilities = capabilities | ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK | NetworkPolicyManager.getDefaultProcessNetworkCapabilities(expectedProcState); final boolean wouldBeAllowed = NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground( expectedProcState, mergedCapabilities); if (DEBUG) { Slog.d(TAG, "UID " + uid + " UI job state allows metered network=" + wouldBeAllowed + " capabilities=" + mergedCapabilities); } if (wouldBeAllowed) { return true; } } if (mBackgroundMeteredAllowed.indexOfKey(uid) >= 0) { return mBackgroundMeteredAllowed.get(uid); } final boolean allowed = mNetPolicyManager.getRestrictBackgroundStatus(uid) != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; if (DEBUG) { Slog.d(TAG, "UID " + uid + " allowed in data saver=" + allowed); } mBackgroundMeteredAllowed.put(uid, allowed); return allowed; } /** * Return the estimated amount of time this job will be transferring data, * based on the current network speed. Loading Loading @@ -859,6 +959,12 @@ public final class ConnectivityController extends RestrictingController implemen // First, are we insane? if (isInsane(jobStatus, network, capabilities, constants)) return false; // User-initiated jobs might make NetworkPolicyManager open up network access for // the whole UID. If network access is opened up just because of UI jobs, we want // to make sure that non-UI jobs don't run during that time, // so make sure the job can make use of the metered network at this time. if (!isMeteredAllowed(jobStatus, capabilities)) return false; // Second, is the network congested? if (isCongestionDelayed(jobStatus, network, capabilities, constants)) return false; Loading Loading @@ -1138,9 +1244,10 @@ public final class ConnectivityController extends RestrictingController implemen // but it doesn't yet satisfy the requested constraints and the old network // is still available and satisfies the constraints. Don't change the network // given to the job for now and let it keep running. We will re-evaluate when // the capabilities or connection state of the either network change. // the capabilities or connection state of either network change. if (DEBUG) { Slog.i(TAG, "Not reassigning network for running job " + jobStatus); Slog.i(TAG, "Not reassigning network from " + jobStatus.network + " to " + network + " for running job " + jobStatus); } return false; } Loading Loading @@ -1389,6 +1496,26 @@ public final class ConnectivityController extends RestrictingController implemen } }; private final INetworkPolicyListener mNetPolicyListener = new NetworkPolicyManager.Listener() { @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { if (DEBUG) { Slog.v(TAG, "onRestrictBackgroundChanged: " + restrictBackground); } mHandler.obtainMessage(MSG_DATA_SAVER_TOGGLED).sendToTarget(); } @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { if (DEBUG) { Slog.v(TAG, "onUidPoliciesChanged: " + uid); } mHandler.obtainMessage(MSG_UID_POLICIES_CHANGED, uid, mNetPolicyManager.getRestrictBackgroundStatus(uid)) .sendToTarget(); } }; private class CcHandler extends Handler { CcHandler(Looper looper) { super(looper); Loading @@ -1410,6 +1537,27 @@ public final class ConnectivityController extends RestrictingController implemen updateAllTrackedJobsLocked(allowThrottle); } break; case MSG_DATA_SAVER_TOGGLED: removeMessages(MSG_DATA_SAVER_TOGGLED); synchronized (mLock) { mBackgroundMeteredAllowed.clear(); updateTrackedJobsLocked(-1, null); } break; case MSG_UID_POLICIES_CHANGED: final int uid = msg.arg1; final boolean newAllowed = msg.arg2 != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; synchronized (mLock) { final boolean oldAllowed = mBackgroundMeteredAllowed.get(uid); if (oldAllowed != newAllowed) { mBackgroundMeteredAllowed.put(uid, newAllowed); updateTrackedJobsLocked(uid, null); } } break; } } } Loading
core/api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -151,6 +151,7 @@ package android.app { field public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L; // 0xa692aadL field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6 field public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 8; // 0x8 field public static final int PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK = 32; // 0x20 field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4 field public static final int PROCESS_STATE_TOP = 2; // 0x2 field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff Loading
core/java/android/app/ActivityManager.java +12 −1 Original line number Diff line number Diff line Loading @@ -794,6 +794,7 @@ public class ActivityManager { PROCESS_CAPABILITY_FOREGROUND_MICROPHONE, PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK, PROCESS_CAPABILITY_BFSL, PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK, }) @Retention(RetentionPolicy.SOURCE) public @interface ProcessCapability {} Loading Loading @@ -935,6 +936,13 @@ public class ActivityManager { */ public static final int PROCESS_CAPABILITY_BFSL = 1 << 4; /** * @hide * Process can access network at a high enough proc state despite any user restrictions. */ @TestApi public static final int PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK = 1 << 5; /** * @hide all capabilities, the ORing of all flags in {@link ProcessCapability}. * Loading @@ -945,7 +953,8 @@ public class ActivityManager { | PROCESS_CAPABILITY_FOREGROUND_CAMERA | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE | PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK | PROCESS_CAPABILITY_BFSL; | PROCESS_CAPABILITY_BFSL | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; /** * All implicit capabilities. There are capabilities that process automatically have. Loading @@ -965,6 +974,7 @@ public class ActivityManager { pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-'); pw.print((caps & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0 ? 'N' : '-'); pw.print((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-'); pw.print((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-'); } /** @hide */ Loading @@ -974,6 +984,7 @@ public class ActivityManager { sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0 ? 'M' : '-'); sb.append((caps & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0 ? 'N' : '-'); sb.append((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-'); sb.append((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-'); } /** Loading