mBackgroundLaunchBroadcasts;
/**
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
* NOTE: This object is protected by its own lock, NOT the global
* activity manager lock!
*/
final SparseArray mPidsSelfLocked = new SparseArray();
/**
* All of the processes that have been forced to be important. The key
* is the pid of the caller who requested it (we hold a death
* link on it).
*/
abstract class ImportanceToken implements IBinder.DeathRecipient {
final int pid;
final IBinder token;
final String reason;
ImportanceToken(int _pid, IBinder _token, String _reason) {
pid = _pid;
token = _token;
reason = _reason;
}
@Override
public String toString() {
return "ImportanceToken { " + Integer.toHexString(System.identityHashCode(this))
+ " " + reason + " " + pid + " " + token + " }";
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long pToken = proto.start(fieldId);
proto.write(ImportanceTokenProto.PID, pid);
if (token != null) {
proto.write(ImportanceTokenProto.TOKEN, token.toString());
}
proto.write(ImportanceTokenProto.REASON, reason);
proto.end(pToken);
}
}
final SparseArray mImportantProcesses = new SparseArray();
/**
* List of records for processes that someone had tried to start before the
* system was ready. We don't start them at that point, but ensure they
* are started by the time booting is complete.
*/
final ArrayList mProcessesOnHold = new ArrayList();
/**
* List of persistent applications that are in the process
* of being started.
*/
final ArrayList mPersistentStartingProcesses = new ArrayList();
/**
* Processes that are being forcibly torn down.
*/
final ArrayList mRemovedProcesses = new ArrayList();
/**
* List of running applications, sorted by recent usage.
* The first entry in the list is the least recently used.
*/
final ArrayList mLruProcesses = new ArrayList();
/**
* Where in mLruProcesses that the processes hosting activities start.
*/
int mLruProcessActivityStart = 0;
/**
* Where in mLruProcesses that the processes hosting services start.
* This is after (lower index) than mLruProcessesActivityStart.
*/
int mLruProcessServiceStart = 0;
/**
* List of processes that should gc as soon as things are idle.
*/
final ArrayList mProcessesToGc = new ArrayList();
/**
* Processes we want to collect PSS data from.
*/
final ArrayList mPendingPssProcesses = new ArrayList();
private boolean mBinderTransactionTrackingEnabled = false;
/**
* Last time we requested PSS data of all processes.
*/
long mLastFullPssTime = SystemClock.uptimeMillis();
/**
* If set, the next time we collect PSS data we should do a full collection
* with data from native processes and the kernel.
*/
boolean mFullPssPending = false;
/**
* This is the process holding what we currently consider to be
* the "home" activity.
*/
ProcessRecord mHomeProcess;
/**
* This is the process holding the activity the user last visited that
* is in a different process from the one they are currently in.
*/
ProcessRecord mPreviousProcess;
/**
* The time at which the previous process was last visible.
*/
long mPreviousProcessVisibleTime;
/**
* Track all uids that have actively running processes.
*/
final SparseArray mActiveUids = new SparseArray<>();
/**
* This is for verifying the UID report flow.
*/
static final boolean VALIDATE_UID_STATES = true;
final SparseArray mValidateUids = new SparseArray<>();
/**
* Packages that the user has asked to have run in screen size
* compatibility mode instead of filling the screen.
*/
final CompatModePackages mCompatModePackages;
/**
* Set of IntentSenderRecord objects that are currently active.
*/
final HashMap> mIntentSenderRecords
= new HashMap>();
/**
* Fingerprints (hashCode()) of stack traces that we've
* already logged DropBox entries for. Guarded by itself. If
* something (rogue user app) forces this over
* MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared.
*/
private final HashSet mAlreadyLoggedViolatedStacks = new HashSet();
private static final int MAX_DUP_SUPPRESSED_STACKS = 5000;
/**
* Keeps track of all IIntentReceivers that have been registered for broadcasts.
* Hash keys are the receiver IBinder, hash value is a ReceiverList.
*/
final HashMap mRegisteredReceivers = new HashMap<>();
/**
* Resolver for broadcast intents to registered receivers.
* Holds BroadcastFilter (subclass of IntentFilter).
*/
final IntentResolver mReceiverResolver
= new IntentResolver() {
@Override
protected boolean allowFilterResult(
BroadcastFilter filter, List dest) {
IBinder target = filter.receiverList.receiver.asBinder();
for (int i = dest.size() - 1; i >= 0; i--) {
if (dest.get(i).receiverList.receiver.asBinder() == target) {
return false;
}
}
return true;
}
@Override
protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
|| userId == filter.owningUserId) {
return super.newResult(filter, match, userId);
}
return null;
}
@Override
protected BroadcastFilter[] newArray(int size) {
return new BroadcastFilter[size];
}
@Override
protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
return packageName.equals(filter.packageName);
}
};
/**
* State of all active sticky broadcasts per user. Keys are the action of the
* sticky Intent, values are an ArrayList of all broadcasted intents with
* that action (which should usually be one). The SparseArray is keyed
* by the user ID the sticky is for, and can include UserHandle.USER_ALL
* for stickies that are sent to all users.
*/
final SparseArray>> mStickyBroadcasts =
new SparseArray>>();
final ActiveServices mServices;
final static class Association {
final int mSourceUid;
final String mSourceProcess;
final int mTargetUid;
final ComponentName mTargetComponent;
final String mTargetProcess;
int mCount;
long mTime;
int mNesting;
long mStartTime;
// states of the source process when the bind occurred.
int mLastState = ActivityManager.MAX_PROCESS_STATE + 1;
long mLastStateUptime;
long[] mStateTimes = new long[ActivityManager.MAX_PROCESS_STATE
- ActivityManager.MIN_PROCESS_STATE+1];
Association(int sourceUid, String sourceProcess, int targetUid,
ComponentName targetComponent, String targetProcess) {
mSourceUid = sourceUid;
mSourceProcess = sourceProcess;
mTargetUid = targetUid;
mTargetComponent = targetComponent;
mTargetProcess = targetProcess;
}
}
/**
* When service association tracking is enabled, this is all of the associations we
* have seen. Mapping is target uid -> target component -> source uid -> source process name
* -> association data.
*/
final SparseArray>>>
mAssociations = new SparseArray<>();
boolean mTrackingAssociations;
/**
* Backup/restore process management
*/
String mBackupAppName = null;
BackupRecord mBackupTarget = null;
final ProviderMap mProviderMap;
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
* removed from this list once it is published.
*/
final ArrayList mLaunchingProviders
= new ArrayList();
/**
* File storing persisted {@link #mGrantedUriPermissions}.
*/
private final AtomicFile mGrantFile;
/** XML constants used in {@link #mGrantFile} */
private static final String TAG_URI_GRANTS = "uri-grants";
private static final String TAG_URI_GRANT = "uri-grant";
private static final String ATTR_USER_HANDLE = "userHandle";
private static final String ATTR_SOURCE_USER_ID = "sourceUserId";
private static final String ATTR_TARGET_USER_ID = "targetUserId";
private static final String ATTR_SOURCE_PKG = "sourcePkg";
private static final String ATTR_TARGET_PKG = "targetPkg";
private static final String ATTR_URI = "uri";
private static final String ATTR_MODE_FLAGS = "modeFlags";
private static final String ATTR_CREATED_TIME = "createdTime";
private static final String ATTR_PREFIX = "prefix";
/**
* Global set of specific {@link Uri} permissions that have been granted.
* This optimized lookup structure maps from {@link UriPermission#targetUid}
* to {@link UriPermission#uri} to {@link UriPermission}.
*/
@GuardedBy("this")
private final SparseArray>
mGrantedUriPermissions = new SparseArray>();
public static class GrantUri {
public final int sourceUserId;
public final Uri uri;
public boolean prefix;
public GrantUri(int sourceUserId, Uri uri, boolean prefix) {
this.sourceUserId = sourceUserId;
this.uri = uri;
this.prefix = prefix;
}
@Override
public int hashCode() {
int hashCode = 1;
hashCode = 31 * hashCode + sourceUserId;
hashCode = 31 * hashCode + uri.hashCode();
hashCode = 31 * hashCode + (prefix ? 1231 : 1237);
return hashCode;
}
@Override
public boolean equals(Object o) {
if (o instanceof GrantUri) {
GrantUri other = (GrantUri) o;
return uri.equals(other.uri) && (sourceUserId == other.sourceUserId)
&& prefix == other.prefix;
}
return false;
}
@Override
public String toString() {
String result = uri.toString() + " [user " + sourceUserId + "]";
if (prefix) result += " [prefix]";
return result;
}
public String toSafeString() {
String result = uri.toSafeString() + " [user " + sourceUserId + "]";
if (prefix) result += " [prefix]";
return result;
}
public void writeToProto(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(GrantUriProto.URI, uri.toString());
proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
proto.end(token);
}
public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
ContentProvider.getUriWithoutUserId(uri), false);
} else {
return new GrantUri(defaultSourceUserHandle, uri, false);
}
}
}
boolean mSystemProvidersInstalled;
CoreSettingsObserver mCoreSettingsObserver;
FontScaleSettingObserver mFontScaleSettingObserver;
private final class FontScaleSettingObserver extends ContentObserver {
private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
public FontScaleSettingObserver() {
super(mHandler);
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
UserHandle.USER_ALL);
}
@Override
public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
if (mFontScaleUri.equals(uri)) {
updateFontScaleIfNeeded(userId);
} else if (mHideErrorDialogsUri.equals(uri)) {
synchronized (ActivityManagerService.this) {
updateShouldShowDialogsLocked(getGlobalConfiguration());
}
}
}
}
DevelopmentSettingsObserver mDevelopmentSettingsObserver;
private final class DevelopmentSettingsObserver extends ContentObserver {
private final Uri mUri = Settings.Global
.getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
private final ComponentName mBugreportStorageProvider = new ComponentName(
"com.android.shell", "com.android.shell.BugreportStorageProvider");
public DevelopmentSettingsObserver() {
super(mHandler);
mContext.getContentResolver().registerContentObserver(mUri, false, this,
UserHandle.USER_ALL);
// Always kick once to ensure that we match current state
onChange();
}
@Override
public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
if (mUri.equals(uri)) {
onChange();
}
}
public void onChange() {
final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, Build.IS_ENG ? 1 : 0) != 0;
mContext.getPackageManager().setComponentEnabledSetting(mBugreportStorageProvider,
enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
0);
}
}
/**
* Thread-local storage used to carry caller permissions over through
* indirect content-provider access.
*/
private class Identity {
public final IBinder token;
public final int pid;
public final int uid;
Identity(IBinder _token, int _pid, int _uid) {
token = _token;
pid = _pid;
uid = _uid;
}
}
private static final ThreadLocal sCallerIdentity = new ThreadLocal();
/**
* All information we have collected about the runtime performance of
* any user id that can impact battery performance.
*/
final BatteryStatsService mBatteryStatsService;
/**
* Information about component usage
*/
UsageStatsManagerInternal mUsageStatsService;
/**
* Access to DeviceIdleController service.
*/
DeviceIdleController.LocalService mLocalDeviceIdleController;
/**
* Power-save whitelisted app-ids (not including except-idle-whitelisted ones).
*/
int[] mDeviceIdleWhitelist = new int[0];
/**
* Power-save whitelisted app-ids (including except-idle-whitelisted ones).
*/
int[] mDeviceIdleExceptIdleWhitelist = new int[0];
/**
* Set of app ids that are temporarily allowed to escape bg check due to high-pri message
*/
int[] mDeviceIdleTempWhitelist = new int[0];
static final class PendingTempWhitelist {
final int targetUid;
final long duration;
final String tag;
PendingTempWhitelist(int _targetUid, long _duration, String _tag) {
targetUid = _targetUid;
duration = _duration;
tag = _tag;
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag);
proto.end(token);
}
}
final SparseArray mPendingTempWhitelist = new SparseArray<>();
/**
* Information about and control over application operations
*/
final AppOpsService mAppOpsService;
/** Current sequencing integer of the configuration, for skipping old configurations. */
private int mConfigurationSeq;
/**
* Temp object used when global and/or display override configuration is updated. It is also
* sent to outer world instead of {@link #getGlobalConfiguration} because we don't trust
* anyone...
*/
private Configuration mTempConfig = new Configuration();
private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
new UpdateConfigurationResult();
private static final class UpdateConfigurationResult {
// Configuration changes that were updated.
int changes;
// If the activity was relaunched to match the new configuration.
boolean activityRelaunched;
void reset() {
changes = 0;
activityRelaunched = false;
}
}
boolean mSuppressResizeConfigChanges;
/**
* Hardware-reported OpenGLES version.
*/
final int GL_ES_VERSION;
/**
* List of initialization arguments to pass to all processes when binding applications to them.
* For example, references to the commonly used services.
*/
ArrayMap mAppBindArgs;
ArrayMap mIsolatedAppBindArgs;
/**
* Temporary to avoid allocations. Protected by main lock.
*/
final StringBuilder mStringBuilder = new StringBuilder(256);
/**
* Used to control how we initialize the service.
*/
ComponentName mTopComponent;
String mTopAction = Intent.ACTION_MAIN;
String mTopData;
volatile boolean mProcessesReady = false;
volatile boolean mSystemReady = false;
volatile boolean mOnBattery = false;
volatile int mFactoryTest;
@GuardedBy("this") boolean mBooting = false;
@GuardedBy("this") boolean mCallFinishBooting = false;
@GuardedBy("this") boolean mBootAnimationComplete = false;
@GuardedBy("this") boolean mLaunchWarningShown = false;
private @GuardedBy("this") boolean mCheckedForSetup = false;
final Context mContext;
/**
* This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
* change at runtime. Use mContext for non-UI purposes.
*/
final Context mUiContext;
/**
* The time at which we will allow normal application switches again,
* after a call to {@link #stopAppSwitches()}.
*/
long mAppSwitchesAllowedTime;
/**
* This is set to true after the first switch after mAppSwitchesAllowedTime
* is set; any switches after that will clear the time.
*/
boolean mDidAppSwitch;
/**
* Last time (in uptime) at which we checked for power usage.
*/
long mLastPowerCheckUptime;
/**
* Set while we are wanting to sleep, to prevent any
* activities from being started/resumed.
*
* TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
*
* Currently mSleeping is set to true when transitioning into the sleep state, and remains true
* while in the sleep state until there is a pending transition out of sleep, in which case
* mSleeping is set to false, and remains false while awake.
*
* Whether mSleeping can quickly toggled between true/false without the device actually
* display changing states is undefined.
*/
private boolean mSleeping = false;
/**
* The process state used for processes that are running the top activities.
* This changes between TOP and TOP_SLEEPING to following mSleeping.
*/
int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
/**
* Set while we are running a voice interaction. This overrides
* sleeping while it is active.
*/
IVoiceInteractionSession mRunningVoice;
/**
* For some direct access we need to power manager.
*/
PowerManagerInternal mLocalPowerManager;
/**
* We want to hold a wake lock while running a voice interaction session, since
* this may happen with the screen off and we need to keep the CPU running to
* be able to continue to interact with the user.
*/
PowerManager.WakeLock mVoiceWakeLock;
/**
* State of external calls telling us if the device is awake or asleep.
*/
private int mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
/**
* State of external calls telling us if the device is awake or asleep.
*/
private boolean mKeyguardShown = false;
/**
* Set if we are shutting down the system, similar to sleeping.
*/
boolean mShuttingDown = false;
/**
* Current sequence id for oom_adj computation traversal.
*/
int mAdjSeq = 0;
/**
* Current sequence id for process LRU updating.
*/
int mLruSeq = 0;
/**
* Keep track of the non-cached/empty process we last found, to help
* determine how to distribute cached/empty processes next time.
*/
int mNumNonCachedProcs = 0;
/**
* Keep track of the number of cached hidden procs, to balance oom adj
* distribution between those and empty procs.
*/
int mNumCachedHiddenProcs = 0;
/**
* Keep track of the number of service processes we last found, to
* determine on the next iteration which should be B services.
*/
int mNumServiceProcs = 0;
int mNewNumAServiceProcs = 0;
int mNewNumServiceProcs = 0;
/**
* Allow the current computed overall memory level of the system to go down?
* This is set to false when we are killing processes for reasons other than
* memory management, so that the now smaller process list will not be taken as
* an indication that memory is tighter.
*/
boolean mAllowLowerMemLevel = false;
/**
* The last computed memory level, for holding when we are in a state that
* processes are going away for other reasons.
*/
int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
/**
* The last total number of process we have, to determine if changes actually look
* like a shrinking number of process due to lower RAM.
*/
int mLastNumProcesses;
/**
* The uptime of the last time we performed idle maintenance.
*/
long mLastIdleTime = SystemClock.uptimeMillis();
/**
* Total time spent with RAM that has been added in the past since the last idle time.
*/
long mLowRamTimeSinceLastIdle = 0;
/**
* If RAM is currently low, when that horrible situation started.
*/
long mLowRamStartTime = 0;
/**
* For reporting to battery stats the current top application.
*/
private String mCurResumedPackage = null;
private int mCurResumedUid = -1;
/**
* For reporting to battery stats the apps currently running foreground
* service. The ProcessMap is package/uid tuples; each of these contain
* an array of the currently foreground processes.
*/
final ProcessMap> mForegroundPackages
= new ProcessMap>();
/**
* Set if the systemServer made a call to enterSafeMode.
*/
boolean mSafeMode;
/**
* If true, we are running under a test environment so will sample PSS from processes
* much more rapidly to try to collect better data when the tests are rapidly
* running through apps.
*/
boolean mTestPssMode = false;
String mDebugApp = null;
boolean mWaitForDebugger = false;
boolean mDebugTransient = false;
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
boolean mForceResizableActivities;
/**
* Flag that indicates if multi-window is enabled.
*
* For any particular form of multi-window to be enabled, generic multi-window must be enabled
* in {@link com.android.internal.R.bool#config_supportsMultiWindow} config or
* {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
* At least one of the forms of multi-window must be enabled in order for this flag to be
* initialized to 'true'.
*
* @see #mSupportsSplitScreenMultiWindow
* @see #mSupportsFreeformWindowManagement
* @see #mSupportsPictureInPicture
* @see #mSupportsMultiDisplay
*/
boolean mSupportsMultiWindow;
boolean mSupportsSplitScreenMultiWindow;
boolean mSupportsFreeformWindowManagement;
boolean mSupportsPictureInPicture;
boolean mSupportsMultiDisplay;
boolean mSupportsLeanbackOnly;
IActivityController mController = null;
boolean mControllerIsAMonkey = false;
String mProfileApp = null;
ProcessRecord mProfileProc = null;
ProfilerInfo mProfilerInfo = null;
/**
* Stores a map of process name -> agent string. When a process is started and mAgentAppMap
* is not null, this map is checked and the mapped agent installed during bind-time. Note:
* A non-null agent in mProfileInfo overrides this.
*/
private @Nullable Map mAppAgentMap = null;
int mProfileType = 0;
final ProcessMap> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
String mMemWatchDumpFile;
int mMemWatchDumpPid;
int mMemWatchDumpUid;
String mTrackAllocationApp = null;
String mNativeDebuggingApp = null;
final long[] mTmpLong = new long[3];
private final ArraySet mTmpBroadcastQueue = new ArraySet();
/**
* A global counter for generating sequence numbers.
* This value will be used when incrementing sequence numbers in individual uidRecords.
*
* Having a global counter ensures that seq numbers are monotonically increasing for a
* particular uid even when the uidRecord is re-created.
*/
@GuardedBy("this")
@VisibleForTesting
long mProcStateSeqCounter = 0;
/**
* A global counter for generating sequence numbers to uniquely identify pending process starts.
*/
@GuardedBy("this")
private long mProcStartSeqCounter = 0;
/**
* Contains {@link ProcessRecord} objects for pending process starts.
*
* Mapping: {@link #mProcStartSeqCounter} -> {@link ProcessRecord}
*/
@GuardedBy("this")
private final LongSparseArray mPendingStarts = new LongSparseArray<>();
private final Injector mInjector;
static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
int changes;
int uid;
int pid;
int processState;
boolean foregroundActivities;
}
static final class UidObserverRegistration {
final int uid;
final String pkg;
final int which;
final int cutpoint;
/**
* Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}.
* We show it in dumpsys.
*/
int mSlowDispatchCount;
/** Max time it took for each dispatch. */
int mMaxDispatchTime;
final SparseIntArray lastProcStates;
// Please keep the enum lists in sync
private static int[] ORIG_ENUMS = new int[]{
ActivityManager.UID_OBSERVER_IDLE,
ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.UID_OBSERVER_GONE,
ActivityManager.UID_OBSERVER_PROCSTATE,
};
private static int[] PROTO_ENUMS = new int[]{
ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
};
UidObserverRegistration(int _uid, String _pkg, int _which, int _cutpoint) {
uid = _uid;
pkg = _pkg;
which = _which;
cutpoint = _cutpoint;
if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
lastProcStates = new SparseIntArray();
} else {
lastProcStates = null;
}
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(UidObserverRegistrationProto.UID, uid);
proto.write(UidObserverRegistrationProto.PACKAGE, pkg);
ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, UidObserverRegistrationProto.FLAGS,
which, ORIG_ENUMS, PROTO_ENUMS);
proto.write(UidObserverRegistrationProto.CUT_POINT, cutpoint);
if (lastProcStates != null) {
final int NI = lastProcStates.size();
for (int i=0; i mScreenObservers = new ArrayList<>();
final RemoteCallbackList mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
final ArrayList mPendingProcessChanges = new ArrayList<>();
final ArrayList mAvailProcessChanges = new ArrayList<>();
final RemoteCallbackList mUidObservers = new RemoteCallbackList<>();
UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5];
final ArrayList mPendingUidChanges = new ArrayList<>();
final ArrayList mAvailUidChanges = new ArrayList<>();
OomAdjObserver mCurOomAdjObserver;
int mCurOomAdjUid;
interface OomAdjObserver {
void onOomAdjMessage(String msg);
}
/**
* Runtime CPU use collection thread. This object's lock is used to
* perform synchronization with the thread (notifying it to run).
*/
final Thread mProcessCpuThread;
/**
* Used to collect per-process CPU use for ANRs, battery stats, etc.
* Must acquire this object's lock when accessing it.
* NOTE: this lock will be held while doing long operations (trawling
* through all processes in /proc), so it should never be acquired by
* any critical paths such as when holding the main activity manager lock.
*/
final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
MONITOR_THREAD_CPU_USAGE);
final AtomicLong mLastCpuTime = new AtomicLong(0);
final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
final CountDownLatch mProcessCpuInitLatch = new CountDownLatch(1);
long mLastWriteTime = 0;
/**
* Used to retain an update lock when the foreground activity is in
* immersive mode.
*/
final UpdateLock mUpdateLock = new UpdateLock("immersive");
/**
* Set to true after the system has finished booting.
*/
boolean mBooted = false;
/**
* Current boot phase.
*/
int mBootPhase;
WindowManagerService mWindowManager;
final ActivityThread mSystemThread;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
if (DEBUG_ALL) Slog.v(
TAG, "New death recipient " + this
+ " for thread " + thread.asBinder());
mApp = app;
mPid = pid;
mAppThread = thread;
}
@Override
public void binderDied() {
if (DEBUG_ALL) Slog.v(
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true);
}
}
}
static final int SHOW_ERROR_UI_MSG = 1;
static final int SHOW_NOT_RESPONDING_UI_MSG = 2;
static final int SHOW_FACTORY_ERROR_UI_MSG = 3;
static final int UPDATE_CONFIGURATION_MSG = 4;
static final int GC_BACKGROUND_PROCESSES_MSG = 5;
static final int WAIT_FOR_DEBUGGER_UI_MSG = 6;
static final int SERVICE_TIMEOUT_MSG = 12;
static final int UPDATE_TIME_ZONE = 13;
static final int SHOW_UID_ERROR_UI_MSG = 14;
static final int SHOW_FINGERPRINT_ERROR_UI_MSG = 15;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
static final int POST_HEAVY_NOTIFICATION_MSG = 24;
static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
static final int SHOW_STRICT_MODE_VIOLATION_UI_MSG = 26;
static final int CHECK_EXCESSIVE_POWER_USE_MSG = 27;
static final int CLEAR_DNS_CACHE_MSG = 28;
static final int UPDATE_HTTP_PROXY_MSG = 29;
static final int SHOW_COMPAT_MODE_DIALOG_UI_MSG = 30;
static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int PERSIST_URI_GRANTS_MSG = 38;
static final int UPDATE_TIME_PREFERENCE_MSG = 41;
static final int ENTER_ANIMATION_COMPLETE_MSG = 44;
static final int FINISH_BOOTING_MSG = 45;
static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
static final int DISMISS_DIALOG_UI_MSG = 48;
static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49;
static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50;
static final int DELETE_DUMPHEAP_MSG = 51;
static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53;
static final int REPORT_TIME_TRACKER_MSG = 54;
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56;
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57;
static final int IDLE_UIDS_MSG = 58;
static final int LOG_STACK_STATE = 60;
static final int VR_MODE_CHANGE_MSG = 61;
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
static final int DISPATCH_SCREEN_AWAKE_MSG = 64;
static final int DISPATCH_SCREEN_KEYGUARD_MSG = 65;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
static final int FIRST_COMPAT_MODE_MSG = 300;
static final int FIRST_SUPERVISOR_STACK_MSG = 100;
static final String SERVICE_RECORD_KEY = "servicerecord";
static ServiceThread sKillThread = null;
static KillHandler sKillHandler = null;
CompatModeDialog mCompatModeDialog;
long mLastMemUsageReportTime = 0;
/**
* Flag whether the current user is a "monkey", i.e. whether
* the UI is driven by a UI automation tool.
*/
private boolean mUserIsMonkey;
/** The dimensions of the thumbnails in the Recents UI. */
int mThumbnailWidth;
int mThumbnailHeight;
float mFullscreenThumbnailScale;
final ServiceThread mHandlerThread;
final MainHandler mHandler;
final Handler mUiHandler;
final ServiceThread mProcStartHandlerThread;
final Handler mProcStartHandler;
final ActivityManagerConstants mConstants;
// Encapsulates the global setting "hidden_api_blacklist_exemptions"
final HiddenApiSettings mHiddenApiBlacklist;
PackageManagerInternal mPackageManagerInt;
// VoiceInteraction session ID that changes for each new request except when
// being called for multiwindow assist in a single session.
private int mViSessionId = 1000;
final boolean mPermissionReviewRequired;
boolean mHasHeavyWeightFeature;
/**
* Whether to force background check on all apps (for battery saver) or not.
*/
boolean mForceBackgroundCheck;
private static String sTheRealBuildSerial = Build.UNKNOWN;
/**
* Current global configuration information. Contains general settings for the entire system,
* also corresponds to the merged configuration of the default display.
*/
Configuration getGlobalConfiguration() {
return mStackSupervisor.getConfiguration();
}
final class KillHandler extends Handler {
static final int KILL_PROCESS_GROUP_MSG = 4000;
public KillHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case KILL_PROCESS_GROUP_MSG:
{
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup");
Process.killProcessGroup(msg.arg1 /* uid */, msg.arg2 /* pid */);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
break;
default:
super.handleMessage(msg);
}
}
}
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ERROR_UI_MSG: {
mAppErrors.handleShowAppErrorUi(msg);
ensureBootCompleted();
} break;
case SHOW_NOT_RESPONDING_UI_MSG: {
mAppErrors.handleShowAnrUi(msg);
ensureBootCompleted();
} break;
case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
HashMap data = (HashMap) msg.obj;
synchronized (ActivityManagerService.this) {
ProcessRecord proc = (ProcessRecord) data.get("app");
if (proc == null) {
Slog.e(TAG, "App not found when showing strict mode dialog.");
break;
}
if (proc.crashDialog != null) {
Slog.e(TAG, "App already has strict mode dialog: " + proc);
return;
}
AppErrorResult res = (AppErrorResult) data.get("result");
if (mShowDialogs && !mSleeping && !mShuttingDown) {
Dialog d = new StrictModeViolationDialog(mUiContext,
ActivityManagerService.this, res, proc);
d.show();
proc.crashDialog = d;
} else {
// The device is asleep, so just pretend that the user
// saw a crash dialog and hit "force quit".
res.set(0);
}
}
ensureBootCompleted();
} break;
case SHOW_FACTORY_ERROR_UI_MSG: {
Dialog d = new FactoryErrorDialog(
mUiContext, msg.getData().getCharSequence("msg"));
d.show();
ensureBootCompleted();
} break;
case WAIT_FOR_DEBUGGER_UI_MSG: {
synchronized (ActivityManagerService.this) {
ProcessRecord app = (ProcessRecord)msg.obj;
if (msg.arg1 != 0) {
if (!app.waitedForDebugger) {
Dialog d = new AppWaitingForDebuggerDialog(
ActivityManagerService.this,
mUiContext, app);
app.waitDialog = d;
app.waitedForDebugger = true;
d.show();
}
} else {
if (app.waitDialog != null) {
app.waitDialog.dismiss();
app.waitDialog = null;
}
}
}
} break;
case SHOW_UID_ERROR_UI_MSG: {
if (mShowDialogs) {
AlertDialog d = new BaseErrorDialog(mUiContext);
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
d.setCancelable(false);
d.setTitle(mUiContext.getText(R.string.android_system_label));
d.setMessage(mUiContext.getText(R.string.system_error_wipe_data));
d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok),
obtainMessage(DISMISS_DIALOG_UI_MSG, d));
d.show();
}
} break;
case SHOW_FINGERPRINT_ERROR_UI_MSG: {
if (mShowDialogs) {
AlertDialog d = new BaseErrorDialog(mUiContext);
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
d.setCancelable(false);
d.setTitle(mUiContext.getText(R.string.android_system_label));
d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.ok),
obtainMessage(DISMISS_DIALOG_UI_MSG, d));
d.show();
}
} break;
case SHOW_COMPAT_MODE_DIALOG_UI_MSG: {
synchronized (ActivityManagerService.this) {
ActivityRecord ar = (ActivityRecord) msg.obj;
if (mCompatModeDialog != null) {
if (mCompatModeDialog.mAppInfo.packageName.equals(
ar.info.applicationInfo.packageName)) {
return;
}
mCompatModeDialog.dismiss();
mCompatModeDialog = null;
}
if (ar != null && false) {
if (mCompatModePackages.getPackageAskCompatModeLocked(
ar.packageName)) {
int mode = mCompatModePackages.computeCompatModeLocked(
ar.info.applicationInfo);
if (mode == ActivityManager.COMPAT_MODE_DISABLED
|| mode == ActivityManager.COMPAT_MODE_ENABLED) {
mCompatModeDialog = new CompatModeDialog(
ActivityManagerService.this, mUiContext,
ar.info.applicationInfo);
mCompatModeDialog.show();
}
}
}
}
break;
}
case DISMISS_DIALOG_UI_MSG: {
final Dialog d = (Dialog) msg.obj;
d.dismiss();
break;
}
case DISPATCH_PROCESSES_CHANGED_UI_MSG: {
dispatchProcessesChanged();
break;
}
case DISPATCH_PROCESS_DIED_UI_MSG: {
final int pid = msg.arg1;
final int uid = msg.arg2;
dispatchProcessDied(pid, uid);
break;
}
case DISPATCH_UIDS_CHANGED_UI_MSG: {
dispatchUidsChanged();
} break;
case DISPATCH_OOM_ADJ_OBSERVER_MSG: {
dispatchOomAdjObserver((String)msg.obj);
} break;
case PUSH_TEMP_WHITELIST_UI_MSG: {
pushTempWhitelist();
} break;
}
}
}
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_CONFIGURATION_MSG: {
final ContentResolver resolver = mContext.getContentResolver();
Settings.System.putConfigurationForUser(resolver, (Configuration) msg.obj,
msg.arg1);
} break;
case GC_BACKGROUND_PROCESSES_MSG: {
synchronized (ActivityManagerService.this) {
performAppGcsIfAppropriateLocked();
}
} break;
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
case SERVICE_FOREGROUND_TIMEOUT_MSG: {
mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
} break;
case SERVICE_FOREGROUND_CRASH_MSG: {
mServices.serviceForegroundCrash(
(ProcessRecord) msg.obj, msg.getData().getCharSequence(SERVICE_RECORD_KEY));
} break;
case DISPATCH_PENDING_INTENT_CANCEL_MSG: {
RemoteCallbackList callbacks
= (RemoteCallbackList)msg.obj;
int N = callbacks.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null);
} catch (RemoteException e) {
}
}
callbacks.finishBroadcast();
} break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r.thread != null) {
try {
r.thread.updateTimeZone();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update time zone for: " + r.info.processName);
}
}
}
}
} break;
case CLEAR_DNS_CACHE_MSG: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r.thread != null) {
try {
r.thread.clearDnsCache();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to clear dns cache for: " + r.info.processName);
}
}
}
}
} break;
case UPDATE_HTTP_PROXY_MSG: {
ProxyInfo proxy = (ProxyInfo)msg.obj;
String host = "";
String port = "";
String exclList = "";
Uri pacFileUrl = Uri.EMPTY;
if (proxy != null) {
host = proxy.getHost();
port = Integer.toString(proxy.getPort());
exclList = proxy.getExclusionListAsString();
pacFileUrl = proxy.getPacFileUrl();
}
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
// Don't dispatch to isolated processes as they can't access
// ConnectivityManager and don't have network privileges anyway.
if (r.thread != null && !r.isolated) {
try {
r.thread.setHttpProxy(host, port, exclList, pacFileUrl);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update http proxy for: " +
r.info.processName);
}
}
}
}
} break;
case PROC_START_TIMEOUT_MSG: {
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processStartTimedOutLocked(app);
}
} break;
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processContentProviderPublishTimedOutLocked(app);
}
} break;
case KILL_APPLICATION_MSG: {
synchronized (ActivityManagerService.this) {
final int appId = msg.arg1;
final int userId = msg.arg2;
Bundle bundle = (Bundle)msg.obj;
String pkg = bundle.getString("pkg");
String reason = bundle.getString("reason");
forceStopPackageLocked(pkg, appId, false, false, true, false,
false, userId, reason);
}
} break;
case FINALIZE_PENDING_INTENT_MSG: {
((PendingIntentRecord)msg.obj).completeFinalize();
} break;
case POST_HEAVY_NOTIFICATION_MSG: {
INotificationManager inm = NotificationManager.getService();
if (inm == null) {
return;
}
ActivityRecord root = (ActivityRecord)msg.obj;
ProcessRecord process = root.app;
if (process == null) {
return;
}
try {
Context context = mContext.createPackageContext(process.info.packageName, 0);
String text = mContext.getString(R.string.heavy_weight_notification,
context.getApplicationInfo().loadLabel(context.getPackageManager()));
Notification notification =
new Notification.Builder(context,
SystemNotificationChannels.HEAVY_WEIGHT_APP)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setWhen(0)
.setOngoing(true)
.setTicker(text)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color))
.setContentTitle(text)
.setContentText(
mContext.getText(R.string.heavy_weight_notification_detail))
.setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
root.intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
new UserHandle(root.userId)))
.build();
try {
inm.enqueueNotificationWithTag("android", "android", null,
SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION,
notification, root.userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for heavy-weight app", e);
} catch (RemoteException e) {
}
} catch (NameNotFoundException e) {
Slog.w(TAG, "Unable to create context for heavy notification", e);
}
} break;
case CANCEL_HEAVY_NOTIFICATION_MSG: {
INotificationManager inm = NotificationManager.getService();
if (inm == null) {
return;
}
try {
inm.cancelNotificationWithTag("android", null,
SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
} catch (RemoteException e) {
}
} break;
case CHECK_EXCESSIVE_POWER_USE_MSG: {
synchronized (ActivityManagerService.this) {
checkExcessivePowerUsageLocked();
removeMessages(CHECK_EXCESSIVE_POWER_USE_MSG);
Message nmsg = obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
}
} break;
case REPORT_MEM_USAGE_MSG: {
final ArrayList memInfos = (ArrayList)msg.obj;
Thread thread = new Thread() {
@Override public void run() {
reportMemUsage(memInfos);
}
};
thread.start();
break;
}
case IMMERSIVE_MODE_LOCK_MSG: {
final boolean nextState = (msg.arg1 != 0);
if (mUpdateLock.isHeld() != nextState) {
if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
"Applying new update lock state '" + nextState
+ "' for " + (ActivityRecord)msg.obj);
if (nextState) {
mUpdateLock.acquire();
} else {
mUpdateLock.release();
}
}
break;
}
case PERSIST_URI_GRANTS_MSG: {
writeGrantedUriPermissions();
break;
}
case UPDATE_TIME_PREFERENCE_MSG: {
// The user's time format preference might have changed.
// For convenience we re-use the Intent extra values.
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r.thread != null) {
try {
r.thread.updateTimePrefs(msg.arg1);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to update preferences for: "
+ r.info.processName);
}
}
}
}
break;
}
case ENTER_ANIMATION_COMPLETE_MSG: {
synchronized (ActivityManagerService.this) {
ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
if (r != null && r.app != null && r.app.thread != null) {
try {
r.app.thread.scheduleEnterAnimationComplete(r.appToken);
} catch (RemoteException e) {
}
}
}
break;
}
case FINISH_BOOTING_MSG: {
if (msg.arg1 != 0) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting");
finishBooting();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
if (msg.arg2 != 0) {
enableScreenAfterBoot();
}
break;
}
case SEND_LOCALE_TO_MOUNT_DAEMON_MSG: {
try {
Locale l = (Locale) msg.obj;
IBinder service = ServiceManager.getService("mount");
IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
Log.d(TAG, "Storing locale " + l.toLanguageTag() + " for decryption UI");
storageManager.setField(StorageManager.SYSTEM_LOCALE_KEY, l.toLanguageTag());
} catch (RemoteException e) {
Log.e(TAG, "Error storing locale for decryption UI", e);
}
break;
}
case NOTIFY_CLEARTEXT_NETWORK_MSG: {
final int uid = msg.arg1;
final byte[] firstPacket = (byte[]) msg.obj;
synchronized (mPidsSelfLocked) {
for (int i = 0; i < mPidsSelfLocked.size(); i++) {
final ProcessRecord p = mPidsSelfLocked.valueAt(i);
if (p.uid == uid) {
try {
p.thread.notifyCleartextNetwork(firstPacket);
} catch (RemoteException ignored) {
}
}
}
}
break;
}
case POST_DUMP_HEAP_NOTIFICATION_MSG: {
final String procName;
final int uid;
final long memLimit;
final String reportPackage;
synchronized (ActivityManagerService.this) {
procName = mMemWatchDumpProcName;
uid = mMemWatchDumpUid;
Pair val = mMemWatchProcesses.get(procName, uid);
if (val == null) {
val = mMemWatchProcesses.get(procName, 0);
}
if (val != null) {
memLimit = val.first;
reportPackage = val.second;
} else {
memLimit = 0;
reportPackage = null;
}
}
if (procName == null) {
return;
}
if (DEBUG_PSS) Slog.d(TAG_PSS,
"Showing dump heap notification from " + procName + "/" + uid);
INotificationManager inm = NotificationManager.getService();
if (inm == null) {
return;
}
String text = mContext.getString(R.string.dump_heap_notification, procName);
Intent deleteIntent = new Intent();
deleteIntent.setAction(DumpHeapActivity.ACTION_DELETE_DUMPHEAP);
Intent intent = new Intent();
intent.setClassName("android", DumpHeapActivity.class.getName());
intent.putExtra(DumpHeapActivity.KEY_PROCESS, procName);
intent.putExtra(DumpHeapActivity.KEY_SIZE, memLimit);
if (reportPackage != null) {
intent.putExtra(DumpHeapActivity.KEY_DIRECT_LAUNCH, reportPackage);
}
int userId = UserHandle.getUserId(uid);
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setWhen(0)
.setOngoing(true)
.setAutoCancel(true)
.setTicker(text)
.setColor(mContext.getColor(
com.android.internal.R.color.system_notification_accent_color))
.setContentTitle(text)
.setContentText(
mContext.getText(R.string.dump_heap_notification_detail))
.setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT, null,
new UserHandle(userId)))
.setDeleteIntent(PendingIntent.getBroadcastAsUser(mContext, 0,
deleteIntent, 0, UserHandle.SYSTEM))
.build();
try {
inm.enqueueNotificationWithTag("android", "android", null,
SystemMessage.NOTE_DUMP_HEAP_NOTIFICATION,
notification, userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for dump heap", e);
} catch (RemoteException e) {
}
} break;
case DELETE_DUMPHEAP_MSG: {
revokeUriPermission(ActivityThread.currentActivityThread().getApplicationThread(),
null, DumpHeapActivity.JAVA_URI,
Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
UserHandle.myUserId());
synchronized (ActivityManagerService.this) {
mMemWatchDumpFile = null;
mMemWatchDumpProcName = null;
mMemWatchDumpPid = -1;
mMemWatchDumpUid = -1;
}
} break;
case REPORT_TIME_TRACKER_MSG: {
AppTimeTracker tracker = (AppTimeTracker)msg.obj;
tracker.deliverResult(mContext);
} break;
case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: {
IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj;
try {
connection.shutdown();
} catch (RemoteException e) {
Slog.w(TAG, "Error shutting down UiAutomationConnection");
}
// Only a UiAutomation can set this flag and now that
// it is finished we make sure it is reset to its default.
mUserIsMonkey = false;
} break;
case IDLE_UIDS_MSG: {
idleUids();
} break;
case VR_MODE_CHANGE_MSG: {
if (!mVrController.onVrModeChanged((ActivityRecord) msg.obj)) {
return;
}
synchronized (ActivityManagerService.this) {
final boolean disableNonVrUi = mVrController.shouldDisableNonVrUiLocked();
mWindowManager.disableNonVrUi(disableNonVrUi);
if (disableNonVrUi) {
// If we are in a VR mode where Picture-in-Picture mode is unsupported,
// then remove the pinned stack.
mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
}
}
} break;
case DISPATCH_SCREEN_AWAKE_MSG: {
final boolean isAwake = msg.arg1 != 0;
for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
mScreenObservers.get(i).onAwakeStateChanged(isAwake);
}
} break;
case DISPATCH_SCREEN_KEYGUARD_MSG: {
final boolean isShowing = msg.arg1 != 0;
for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
}
} break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r.thread != null) {
try {
r.thread.handleTrustStorageUpdate();
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to handle trust storage update for: " +
r.info.processName);
}
}
}
}
} break;
}
}
};
static final int COLLECT_PSS_BG_MSG = 1;
final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case COLLECT_PSS_BG_MSG: {
long start = SystemClock.uptimeMillis();
MemInfoReader memInfo = null;
synchronized (ActivityManagerService.this) {
if (mFullPssPending) {
mFullPssPending = false;
memInfo = new MemInfoReader();
}
}
if (memInfo != null) {
updateCpuStatsNow();
long nativeTotalPss = 0;
final List stats;
synchronized (mProcessCpuTracker) {
stats = mProcessCpuTracker.getStats( (st)-> {
return st.vsize > 0 && st.uid < FIRST_APPLICATION_UID;
});
}
final int N = stats.size();
for (int j = 0; j < N; j++) {
synchronized (mPidsSelfLocked) {
if (mPidsSelfLocked.indexOfKey(stats.get(j).pid) >= 0) {
// This is one of our own processes; skip it.
continue;
}
}
nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
}
memInfo.readMemInfo();
synchronized (ActivityManagerService.this) {
if (DEBUG_PSS) Slog.d(TAG_PSS, "Collected native and kernel memory in "
+ (SystemClock.uptimeMillis()-start) + "ms");
final long cachedKb = memInfo.getCachedSizeKb();
final long freeKb = memInfo.getFreeSizeKb();
final long zramKb = memInfo.getZramTotalSizeKb();
final long kernelKb = memInfo.getKernelUsedSizeKb();
EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
kernelKb*1024, nativeTotalPss*1024);
mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
nativeTotalPss);
}
}
int num = 0;
long[] tmp = new long[3];
do {
ProcessRecord proc;
int procState;
int statType;
int pid;
long lastPssTime;
synchronized (ActivityManagerService.this) {
if (mPendingPssProcesses.size() <= 0) {
if (mTestPssMode || DEBUG_PSS) Slog.d(TAG_PSS,
"Collected pss of " + num + " processes in "
+ (SystemClock.uptimeMillis() - start) + "ms");
mPendingPssProcesses.clear();
return;
}
proc = mPendingPssProcesses.remove(0);
procState = proc.pssProcState;
statType = proc.pssStatType;
lastPssTime = proc.lastPssTime;
long now = SystemClock.uptimeMillis();
if (proc.thread != null && procState == proc.setProcState
&& (lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
< now) {
pid = proc.pid;
} else {
ProcessList.abortNextPssTime(proc.procStateMemTracker);
if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
": still need " +
(lastPssTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE-now) +
"ms until safe");
proc = null;
pid = 0;
}
}
if (proc != null) {
long startTime = SystemClock.currentThreadTimeMillis();
long pss = Debug.getPss(pid, tmp, null);
long endTime = SystemClock.currentThreadTimeMillis();
synchronized (ActivityManagerService.this) {
if (pss != 0 && proc.thread != null && proc.setProcState == procState
&& proc.pid == pid && proc.lastPssTime == lastPssTime) {
num++;
ProcessList.commitNextPssTime(proc.procStateMemTracker);
recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], tmp[2],
statType, endTime-startTime, SystemClock.uptimeMillis());
} else {
ProcessList.abortNextPssTime(proc.procStateMemTracker);
if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
": " + (proc.thread == null ? "NO_THREAD " : "") +
(proc.pid != pid ? "PID_CHANGED " : "") +
" initState=" + procState + " curState=" +
proc.setProcState + " " +
(proc.lastPssTime != lastPssTime ? "TIME_CHANGED" : ""));
}
}
}
} while (true);
}
}
}
};
public void setSystemProcess() {
try {
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_HIGH);
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
ServiceManager.addService("dbinfo", new DbBinder(this));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(this),
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
}
ServiceManager.addService("permission", new PermissionController(this));
ServiceManager.addService("processinfo", new ProcessInfoService(this));
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
app.persistent = true;
app.pid = MY_PID;
app.maxAdj = ProcessList.SYSTEM_ADJ;
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.put(app.pid, app);
}
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find android system package", e);
}
// Start watching app ops after we and the package manager are up and running.
mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null,
new IAppOpsCallback.Stub() {
@Override public void opChanged(int op, int uid, String packageName) {
if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
if (mAppOpsService.checkOperation(op, uid, packageName)
!= AppOpsManager.MODE_ALLOWED) {
runInBackgroundDisabled(uid);
}
}
}
});
}
public void setWindowManager(WindowManagerService wm) {
synchronized (this) {
mWindowManager = wm;
mStackSupervisor.setWindowManager(wm);
mLockTaskController.setWindowManager(wm);
}
}
public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) {
mUsageStatsService = usageStatsManager;
}
public void startObservingNativeCrashes() {
final NativeCrashListener ncl = new NativeCrashListener(this);
ncl.start();
}
public IAppOpsService getAppOpsService() {
return mAppOpsService;
}
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
new PriorityDump.PriorityDumper() {
@Override
public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args,
boolean asProto) {
dump(fd, pw, new String[] {"-a"}, asProto);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
mActivityManagerService.dumpApplicationMemoryUsage(
fd, pw, " ", args, false, null, asProto);
}
};
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
}
static class GraphicsBinder extends Binder {
ActivityManagerService mActivityManagerService;
GraphicsBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"gfxinfo", pw)) return;
mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
}
}
static class DbBinder extends Binder {
ActivityManagerService mActivityManagerService;
DbBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"dbinfo", pw)) return;
mActivityManagerService.dumpDbInfo(fd, pw, args);
}
}
static class CpuBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
new PriorityDump.PriorityDumper() {
@Override
public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
boolean asProto) {
if (asProto) return;
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"cpuinfo", pw)) return;
synchronized (mActivityManagerService.mProcessCpuTracker) {
pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
SystemClock.uptimeMillis()));
}
}
};
CpuBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
}
public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
public Lifecycle(Context context) {
super(context);
mService = new ActivityManagerService(context);
}
@Override
public void onStart() {
mService.start();
}
@Override
public void onBootPhase(int phase) {
mService.mBootPhase = phase;
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mService.mBatteryStatsService.systemServicesReady();
mService.mServices.systemServicesReady();
}
}
@Override
public void onCleanupUser(int userId) {
mService.mBatteryStatsService.onCleanupUser(userId);
}
public ActivityManagerService getService() {
return mService;
}
}
/**
* Encapsulates global settings related to hidden API enforcement behaviour, including tracking
* the latest value via a content observer.
*/
static class HiddenApiSettings extends ContentObserver {
private final Context mContext;
private boolean mBlacklistDisabled;
private String mExemptionsStr;
private List mExemptions = Collections.emptyList();
private int mLogSampleRate = -1;
@HiddenApiEnforcementPolicy private int mPolicyPreP = HIDDEN_API_ENFORCEMENT_DEFAULT;
@HiddenApiEnforcementPolicy private int mPolicyP = HIDDEN_API_ENFORCEMENT_DEFAULT;
public HiddenApiSettings(Handler handler, Context context) {
super(handler);
mContext = context;
}
public void registerObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
false,
this);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE),
false,
this);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS),
false,
this);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY_P_APPS),
false,
this);
update();
}
private void update() {
String exemptions = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS);
if (!TextUtils.equals(exemptions, mExemptionsStr)) {
mExemptionsStr = exemptions;
if ("*".equals(exemptions)) {
mBlacklistDisabled = true;
mExemptions = Collections.emptyList();
} else {
mBlacklistDisabled = false;
mExemptions = TextUtils.isEmpty(exemptions)
? Collections.emptyList()
: Arrays.asList(exemptions.split(","));
}
if (!zygoteProcess.setApiBlacklistExemptions(mExemptions)) {
Slog.e(TAG, "Failed to set API blacklist exemptions!");
// leave mExemptionsStr as is, so we don't try to send the same list again.
mExemptions = Collections.emptyList();
}
}
int logSampleRate = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, -1);
if (logSampleRate < 0 || logSampleRate > 0x10000) {
logSampleRate = -1;
}
if (logSampleRate != -1 && logSampleRate != mLogSampleRate) {
mLogSampleRate = logSampleRate;
zygoteProcess.setHiddenApiAccessLogSampleRate(mLogSampleRate);
}
mPolicyPreP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS);
mPolicyP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_P_APPS);
}
private @HiddenApiEnforcementPolicy int getValidEnforcementPolicy(String settingsKey) {
int policy = Settings.Global.getInt(mContext.getContentResolver(), settingsKey,
ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT);
if (ApplicationInfo.isValidHiddenApiEnforcementPolicy(policy)) {
return policy;
} else {
return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
}
}
boolean isDisabled() {
return mBlacklistDisabled;
}
@HiddenApiEnforcementPolicy int getPolicyForPrePApps() {
return mPolicyPreP;
}
@HiddenApiEnforcementPolicy int getPolicyForPApps() {
return mPolicyP;
}
public void onChange(boolean selfChange) {
update();
}
}
@VisibleForTesting
public ActivityManagerService(Injector injector) {
mInjector = injector;
mContext = mInjector.getContext();
mUiContext = null;
GL_ES_VERSION = 0;
mActivityStartController = null;
mAppErrors = null;
mAppWarnings = null;
mAppOpsService = mInjector.getAppOpsService(null, null);
mBatteryStatsService = null;
mCompatModePackages = null;
mConstants = null;
mGrantFile = null;
mHandler = null;
mHandlerThread = null;
mIntentFirewall = null;
mKeyguardController = null;
mPermissionReviewRequired = false;
mProcessCpuThread = null;
mProcessStats = null;
mProviderMap = null;
mRecentTasks = null;
mServices = null;
mStackSupervisor = null;
mSystemThread = null;
mTaskChangeNotificationController = null;
mUiHandler = injector.getUiHandler(null);
mUserController = null;
mVrController = null;
mLockTaskController = null;
mLifecycleManager = null;
mProcStartHandlerThread = null;
mProcStartHandler = null;
mHiddenApiBlacklist = null;
}
// Note: This method is invoked on the main thread but may need to attach various
// handlers to other threads. So take care to be explicit about the looper.
public ActivityManagerService(Context systemContext) {
LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
mInjector = new Injector();
mContext = systemContext;
mFactoryTest = FactoryTest.getMode();
mSystemThread = ActivityThread.currentActivityThread();
mUiContext = mSystemThread.getSystemUiContext();
Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
mPermissionReviewRequired = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_permissionReviewRequired);
mHandlerThread = new ServiceThread(TAG,
THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
mHandler = new MainHandler(mHandlerThread.getLooper());
mUiHandler = mInjector.getUiHandler(this);
mProcStartHandlerThread = new ServiceThread(TAG + ":procStart",
THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
mProcStartHandlerThread.start();
mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
mConstants = new ActivityManagerConstants(this, mHandler);
/* static; one-time init here */
if (sKillHandler == null) {
sKillThread = new ServiceThread(TAG + ":kill",
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
sKillThread.start();
sKillHandler = new KillHandler(sKillThread.getLooper());
}
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", BROADCAST_BG_TIMEOUT, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
mServices = new ActiveServices(this);
mProviderMap = new ProviderMap(this);
mAppErrors = new AppErrors(mUiContext, this);
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
systemDir.mkdirs();
mAppWarnings = new AppWarnings(this, mUiContext, mHandler, mUiHandler, systemDir);
// TODO: Move creation of battery stats service outside of activity manager service.
mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler);
mBatteryStatsService.getActiveStatistics().readLocked();
mBatteryStatsService.scheduleWriteToDisk();
mOnBattery = DEBUG_POWER ? true
: mBatteryStatsService.getActiveStatistics().getIsOnBattery();
mBatteryStatsService.getActiveStatistics().setCallback(this);
mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler);
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
mUserController = new UserController(this);
mVrController = new VrController(this);
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) {
mUseFifoUiScheduling = true;
}
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
mConfigurationSeq = mTempConfig.seq = 1;
mStackSupervisor = createStackSupervisor();
mStackSupervisor.onConfigurationChanged(mTempConfig);
mKeyguardController = mStackSupervisor.getKeyguardController();
mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
mTaskChangeNotificationController =
new TaskChangeNotificationController(this, mStackSupervisor, mHandler);
mActivityStartController = new ActivityStartController(this);
mRecentTasks = createRecentTasks();
mStackSupervisor.setRecentTasks(mRecentTasks);
mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mHandler);
mLifecycleManager = new ClientLifecycleManager();
mProcessCpuThread = new Thread("CpuTracker") {
@Override
public void run() {
synchronized (mProcessCpuTracker) {
mProcessCpuInitLatch.countDown();
mProcessCpuTracker.init();
}
while (true) {
try {
try {
synchronized(this) {
final long now = SystemClock.uptimeMillis();
long nextCpuDelay = (mLastCpuTime.get()+MONITOR_CPU_MAX_TIME)-now;
long nextWriteDelay = (mLastWriteTime+BATTERY_STATS_TIME)-now;
//Slog.i(TAG, "Cpu delay=" + nextCpuDelay
// + ", write delay=" + nextWriteDelay);
if (nextWriteDelay < nextCpuDelay) {
nextCpuDelay = nextWriteDelay;
}
if (nextCpuDelay > 0) {
mProcessCpuMutexFree.set(true);
this.wait(nextCpuDelay);
}
}
} catch (InterruptedException e) {
}
updateCpuStatsNow();
} catch (Exception e) {
Slog.e(TAG, "Unexpected exception collecting process stats", e);
}
}
}
};
mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
// bind background thread to little cores
// this is expected to fail inside of framework tests because apps can't touch cpusets directly
// make sure we've already adjusted system_server's internal view of itself first
updateOomAdjLocked();
try {
Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
Process.THREAD_GROUP_BG_NONINTERACTIVE);
} catch (Exception e) {
Slog.w(TAG, "Setting background thread cpuset failed");
}
}
protected ActivityStackSupervisor createStackSupervisor() {
final ActivityStackSupervisor supervisor = new ActivityStackSupervisor(this, mHandler.getLooper());
supervisor.initialize();
return supervisor;
}
protected RecentTasks createRecentTasks() {
return new RecentTasks(this, mStackSupervisor);
}
RecentTasks getRecentTasks() {
return mRecentTasks;
}
public void setSystemServiceManager(SystemServiceManager mgr) {
mSystemServiceManager = mgr;
}
public void setInstaller(Installer installer) {
mInstaller = installer;
}
private void start() {
removeAllProcessGroups();
mProcessCpuThread.start();
mBatteryStatsService.publish();
mAppOpsService.publish(mContext);
Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, new LocalService());
// Wait for the synchronized block started in mProcessCpuThread,
// so that any other acccess to mProcessCpuTracker from main thread
// will be blocked during mProcessCpuTracker initialization.
try {
mProcessCpuInitLatch.await();
} catch (InterruptedException e) {
Slog.wtf(TAG, "Interrupted wait during start", e);
Thread.currentThread().interrupt();
throw new IllegalStateException("Interrupted wait during start");
}
}
void onUserStoppedLocked(int userId) {
mRecentTasks.unloadUserDataFromMemoryLocked(userId);
mAllowAppSwitchUids.remove(userId);
}
public void initPowerManagement() {
mStackSupervisor.initPowerManagement();
mBatteryStatsService.initPowerManagement();
mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
mVoiceWakeLock.setReferenceCounted(false);
}
private ArraySet getBackgroundLaunchBroadcasts() {
if (mBackgroundLaunchBroadcasts == null) {
mBackgroundLaunchBroadcasts = SystemConfig.getInstance().getAllowImplicitBroadcasts();
}
return mBackgroundLaunchBroadcasts;
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
if (code == SYSPROPS_TRANSACTION) {
// We need to tell all apps about the system property change.
ArrayList procs = new ArrayList();
synchronized(this) {
final int NP = mProcessNames.getMap().size();
for (int ip=0; ip apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia= now - MONITOR_CPU_MIN_TIME) {
return;
}
if (mProcessCpuMutexFree.compareAndSet(true, false)) {
synchronized (mProcessCpuThread) {
mProcessCpuThread.notify();
}
}
}
void updateCpuStatsNow() {
synchronized (mProcessCpuTracker) {
mProcessCpuMutexFree.set(false);
final long now = SystemClock.uptimeMillis();
boolean haveNewCpuStats = false;
if (MONITOR_CPU_USAGE &&
mLastCpuTime.get() < (now-MONITOR_CPU_MIN_TIME)) {
mLastCpuTime.set(now);
mProcessCpuTracker.update();
if (mProcessCpuTracker.hasGoodLastStats()) {
haveNewCpuStats = true;
//Slog.i(TAG, mProcessCpu.printCurrentState());
//Slog.i(TAG, "Total CPU usage: "
// + mProcessCpu.getTotalCpuPercent() + "%");
// Slog the cpu usage if the property is set.
if ("true".equals(SystemProperties.get("events.cpu"))) {
int user = mProcessCpuTracker.getLastUserTime();
int system = mProcessCpuTracker.getLastSystemTime();
int iowait = mProcessCpuTracker.getLastIoWaitTime();
int irq = mProcessCpuTracker.getLastIrqTime();
int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
int idle = mProcessCpuTracker.getLastIdleTime();
int total = user + system + iowait + irq + softIrq + idle;
if (total == 0) total = 1;
EventLog.writeEvent(EventLogTags.CPU,
((user+system+iowait+irq+softIrq) * 100) / total,
(user * 100) / total,
(system * 100) / total,
(iowait * 100) / total,
(irq * 100) / total,
(softIrq * 100) / total);
}
}
}
final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics();
synchronized(bstats) {
synchronized(mPidsSelfLocked) {
if (haveNewCpuStats) {
if (bstats.startAddingCpuLocked()) {
int totalUTime = 0;
int totalSTime = 0;
final int N = mProcessCpuTracker.countStats();
for (int i=0; i getCommonServicesLocked(boolean isolated) {
// Isolated processes won't get this optimization, so that we don't
// violate the rules about which services they have access to.
if (isolated) {
if (mIsolatedAppBindArgs == null) {
mIsolatedAppBindArgs = new ArrayMap<>(1);
addServiceToMap(mIsolatedAppBindArgs, "package");
}
return mIsolatedAppBindArgs;
}
if (mAppBindArgs == null) {
mAppBindArgs = new ArrayMap<>();
// Add common services.
// IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
// Enable the check in ApplicationThread.bindApplication() to make sure.
addServiceToMap(mAppBindArgs, "package");
addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
addServiceToMap(mAppBindArgs, Context.CONNECTIVITY_SERVICE);
addServiceToMap(mAppBindArgs, Context.ACCESSIBILITY_SERVICE);
addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
addServiceToMap(mAppBindArgs, "graphicsstats");
addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
addServiceToMap(mAppBindArgs, "content");
addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
addServiceToMap(mAppBindArgs, "mount");
}
return mAppBindArgs;
}
private static void addServiceToMap(ArrayMap map, String name) {
final IBinder service = ServiceManager.getService(name);
if (service != null) {
map.put(name, service);
if (false) {
Log.i(TAG, "Adding " + name + " to the pre-loaded service cache.");
}
}
}
/**
* Update AMS states when an activity is resumed. This should only be called by
* {@link ActivityStack#onActivityStateChanged(ActivityRecord, ActivityState, String)} when an
* activity is resumed.
*/
@GuardedBy("this")
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
final TaskRecord task = r.getTask();
if (task.isActivityTypeStandard()) {
if (mCurAppTimeTracker != r.appTimeTracker) {
// We are switching app tracking. Complete the current one.
if (mCurAppTimeTracker != null) {
mCurAppTimeTracker.stop();
mHandler.obtainMessage(
REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
mCurAppTimeTracker = null;
}
if (r.appTimeTracker != null) {
mCurAppTimeTracker = r.appTimeTracker;
startTimeTrackingFocusedActivityLocked();
}
} else {
startTimeTrackingFocusedActivityLocked();
}
} else {
r.appTimeTracker = null;
}
// TODO: VI Maybe r.task.voiceInteractor || r.voiceInteractor != null
// TODO: Probably not, because we don't want to resume voice on switching
// back to this activity
if (task.voiceInteractor != null) {
startRunningVoiceLocked(task.voiceSession, r.info.applicationInfo.uid);
} else {
finishRunningVoiceLocked();
if (mLastResumedActivity != null) {
final IVoiceInteractionSession session;
final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
if (lastResumedActivityTask != null
&& lastResumedActivityTask.voiceSession != null) {
session = lastResumedActivityTask.voiceSession;
} else {
session = mLastResumedActivity.voiceSession;
}
if (session != null) {
// We had been in a voice interaction session, but now focused has
// move to something different. Just finish the session, we can't
// return to it and retain the proper state and synchronization with
// the voice interaction service.
finishVoiceTask(session);
}
}
}
if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
mUserController.sendForegroundProfileChanged(r.userId);
}
updateResumedAppTrace(r);
mLastResumedActivity = r;
mWindowManager.setFocusedApp(r.appToken, true);
applyUpdateLockStateLocked(r);
applyUpdateVrModeLocked(r);
EventLogTags.writeAmSetResumedActivity(
r == null ? -1 : r.userId,
r == null ? "NULL" : r.shortComponentName,
reason);
}
private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
if (mTracedResumedActivity != null) {
Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
constructResumedTraceName(mTracedResumedActivity.packageName), 0);
}
if (resumed != null) {
Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
constructResumedTraceName(resumed.packageName), 0);
}
mTracedResumedActivity = resumed;
}
private String constructResumedTraceName(String packageName) {
return "focused app: " + packageName;
}
@Override
public void setFocusedStack(int stackId) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
return;
}
final ActivityRecord r = stack.topRunningActivityLocked();
if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedStack")) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
@Override
public void setFocusedTask(int taskId) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
return;
}
final ActivityRecord r = task.topRunningActivityLocked();
if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
/** Sets the task stack listener that gets callbacks when a task stack changes. */
@Override
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"registerTaskStackListener()");
mTaskChangeNotificationController.registerTaskStackListener(listener);
}
/**
* Unregister a task stack listener so that it stops receiving callbacks.
*/
@Override
public void unregisterTaskStackListener(ITaskStackListener listener) throws RemoteException {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"unregisterTaskStackListener()");
mTaskChangeNotificationController.unregisterTaskStackListener(listener);
}
@Override
public void notifyActivityDrawn(IBinder token) {
if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
synchronized (this) {
ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
if (r != null) {
r.getStack().notifyActivityDrawnLocked(r);
}
}
}
final void applyUpdateLockStateLocked(ActivityRecord r) {
// Modifications to the UpdateLock state are done on our handler, outside
// the activity manager's locks. The new state is determined based on the
// state *now* of the relevant activity record. The object is passed to
// the handler solely for logging detail, not to be consulted/modified.
final boolean nextState = r != null && r.immersive;
mHandler.sendMessage(
mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
}
final void applyUpdateVrModeLocked(ActivityRecord r) {
// VR apps are expected to run in a main display. If an app is turning on VR for
// itself, but lives in a dynamic stack, then make sure that it is moved to the main
// fullscreen stack before enabling VR Mode.
// TODO: The goal of this code is to keep the VR app on the main display. When the
// stack implementation changes in the future, keep in mind that the use of the fullscreen
// stack is a means to move the activity to the main display and a moveActivityToDisplay()
// option would be a better choice here.
if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
+ " to main stack for VR");
final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
}
mHandler.sendMessage(
mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
}
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
msg.obj = r.getTask().askedCompatMode ? null : r;
mUiHandler.sendMessage(msg);
}
final AppWarnings getAppWarningsLocked() {
return mAppWarnings;
}
/**
* Shows app warning dialogs, if necessary.
*
* @param r activity record for which the warnings may be displayed
*/
final void showAppWarningsIfNeededLocked(ActivityRecord r) {
mAppWarnings.showUnsupportedCompileSdkDialogIfNeeded(r);
mAppWarnings.showUnsupportedDisplaySizeDialogIfNeeded(r);
mAppWarnings.showDeprecatedTargetDialogIfNeeded(r);
}
private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui < 0) {
Slog.wtf(TAG, "Adding dependent process " + app + " not on LRU list: "
+ what + " " + obj + " from " + srcApp);
return index;
}
if (lrui >= index) {
// Don't want to cause this to move dependent processes *back* in the
// list as if they were less frequently used.
return index;
}
if (lrui >= mLruProcessActivityStart) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
mLruProcesses.remove(lrui);
if (index > 0) {
index--;
}
if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+ " in LRU list: " + app);
mLruProcesses.add(index, app);
return index;
}
static void killProcessGroup(int uid, int pid) {
if (sKillHandler != null) {
sKillHandler.sendMessage(
sKillHandler.obtainMessage(KillHandler.KILL_PROCESS_GROUP_MSG, uid, pid));
} else {
Slog.w(TAG, "Asked to kill process group before system bringup!");
Process.killProcessGroup(uid, pid);
}
}
final void removeLruProcessLocked(ProcessRecord app) {
int lrui = mLruProcesses.lastIndexOf(app);
if (lrui >= 0) {
if (!app.killed) {
if (app.persistent) {
Slog.w(TAG, "Removing persistent process that hasn't been killed: " + app);
} else {
Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
if (app.pid > 0) {
killProcessQuiet(app.pid);
killProcessGroup(app.uid, app.pid);
} else {
app.pendingStart = false;
}
}
}
if (lrui <= mLruProcessActivityStart) {
mLruProcessActivityStart--;
}
if (lrui <= mLruProcessServiceStart) {
mLruProcessServiceStart--;
}
mLruProcesses.remove(lrui);
}
}
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
ProcessRecord client) {
final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
|| app.treatLikeActivity || app.recentTasks.size() > 0;
final boolean hasService = false; // not impl yet. app.services.size() > 0;
if (!activityChange && hasActivity) {
// The process has activities, so we are only allowing activity-based adjustments
// to move it. It should be kept in the front of the list with other
// processes that have activities, and we don't want those to change their
// order except due to activity operations.
return;
}
mLruSeq++;
final long now = SystemClock.uptimeMillis();
app.lastActivityTime = now;
// First a quick reject: if the app is already at the position we will
// put it, then there is nothing to do.
if (hasActivity) {
final int N = mLruProcesses.size();
if (N > 0 && mLruProcesses.get(N-1) == app) {
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
return;
}
} else {
if (mLruProcessServiceStart > 0
&& mLruProcesses.get(mLruProcessServiceStart-1) == app) {
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
return;
}
}
int lrui = mLruProcesses.lastIndexOf(app);
if (app.persistent && lrui >= 0) {
// We don't care about the position of persistent processes, as long as
// they are in the list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, persistent: " + app);
return;
}
/* In progress: compute new position first, so we can avoid doing work
if the process is not actually going to move. Not yet working.
int addIndex;
int nextIndex;
boolean inActivity = false, inService = false;
if (hasActivity) {
// Process has activities, put it at the very tipsy-top.
addIndex = mLruProcesses.size();
nextIndex = mLruProcessServiceStart;
inActivity = true;
} else if (hasService) {
// Process has services, put it at the top of the service list.
addIndex = mLruProcessActivityStart;
nextIndex = mLruProcessServiceStart;
inActivity = true;
inService = true;
} else {
// Process not otherwise of interest, it goes to the top of the non-service area.
addIndex = mLruProcessServiceStart;
if (client != null) {
int clientIndex = mLruProcesses.lastIndexOf(client);
if (clientIndex < 0) Slog.d(TAG, "Unknown client " + client + " when updating "
+ app);
if (clientIndex >= 0 && addIndex > clientIndex) {
addIndex = clientIndex;
}
}
nextIndex = addIndex > 0 ? addIndex-1 : addIndex;
}
Slog.d(TAG, "Update LRU at " + lrui + " to " + addIndex + " (act="
+ mLruProcessActivityStart + "): " + app);
*/
if (lrui >= 0) {
if (lrui < mLruProcessActivityStart) {
mLruProcessActivityStart--;
}
if (lrui < mLruProcessServiceStart) {
mLruProcessServiceStart--;
}
/*
if (addIndex > lrui) {
addIndex--;
}
if (nextIndex > lrui) {
nextIndex--;
}
*/
mLruProcesses.remove(lrui);
}
/*
mLruProcesses.add(addIndex, app);
if (inActivity) {
mLruProcessActivityStart++;
}
if (inService) {
mLruProcessActivityStart++;
}
*/
int nextIndex;
if (hasActivity) {
final int N = mLruProcesses.size();
if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
// activities... move it up, but one below the top (the top
// should always have a real activity).
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Adding to second-top of LRU activity list: " + app);
mLruProcesses.add(N - 1, app);
// To keep it from spamming the LRU list (by making a bunch of clients),
// we will push down any other entries owned by the app.
final int uid = app.info.uid;
for (int i = N - 2; i > mLruProcessActivityStart; i--) {
ProcessRecord subProc = mLruProcesses.get(i);
if (subProc.info.uid == uid) {
// We want to push this one down the list. If the process after
// it is for the same uid, however, don't do so, because we don't
// want them internally to be re-ordered.
if (mLruProcesses.get(i - 1).info.uid != uid) {
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Pushing uid " + uid + " swapping at " + i + ": "
+ mLruProcesses.get(i) + " : " + mLruProcesses.get(i - 1));
ProcessRecord tmp = mLruProcesses.get(i);
mLruProcesses.set(i, mLruProcesses.get(i - 1));
mLruProcesses.set(i - 1, tmp);
i--;
}
} else {
// A gap, we can stop here.
break;
}
}
} else {
// Process has activities, put it at the very tipsy-top.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
mLruProcesses.add(app);
}
nextIndex = mLruProcessServiceStart;
} else if (hasService) {
// Process has services, put it at the top of the service list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
mLruProcesses.add(mLruProcessActivityStart, app);
nextIndex = mLruProcessServiceStart;
mLruProcessActivityStart++;
} else {
// Process not otherwise of interest, it goes to the top of the non-service area.
int index = mLruProcessServiceStart;
if (client != null) {
// If there is a client, don't allow the process to be moved up higher
// in the list than that client.
int clientIndex = mLruProcesses.lastIndexOf(client);
if (DEBUG_LRU && clientIndex < 0) Slog.d(TAG_LRU, "Unknown client " + client
+ " when updating " + app);
if (clientIndex <= lrui) {
// Don't allow the client index restriction to push it down farther in the
// list than it already is.
clientIndex = lrui;
}
if (clientIndex >= 0 && index > clientIndex) {
index = clientIndex;
}
}
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding at " + index + " of LRU list: " + app);
mLruProcesses.add(index, app);
nextIndex = index-1;
mLruProcessActivityStart++;
mLruProcessServiceStart++;
}
// If the app is currently using a content provider or service,
// bump those processes as well.
for (int j=app.connections.size()-1; j>=0; j--) {
ConnectionRecord cr = app.connections.valueAt(j);
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
&& !cr.binding.service.app.persistent) {
nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
"service connection", cr, app);
}
}
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
"provider reference", cpr, app);
}
}
}
final ProcessRecord getProcessRecordLocked(String processName, int uid, boolean keepIfLarge) {
if (uid == SYSTEM_UID) {
// The system gets to run in any process. If there are multiple
// processes with the same uid, just pick the first (this
// should never happen).
SparseArray procs = mProcessNames.getMap().get(processName);
if (procs == null) return null;
final int procCount = procs.size();
for (int i = 0; i < procCount; i++) {
final int procUid = procs.keyAt(i);
if (UserHandle.isApp(procUid) || !UserHandle.isSameUser(procUid, uid)) {
// Don't use an app process or different user process for system component.
continue;
}
return procs.valueAt(i);
}
}
ProcessRecord proc = mProcessNames.get(processName, uid);
if (false && proc != null && !keepIfLarge
&& proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY
&& proc.lastCachedPss >= 4000) {
// Turn this condition on to cause killing to happen regularly, for testing.
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
} else if (proc != null && !keepIfLarge
&& mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
&& proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
if (proc.baseProcessTracker != null) {
proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
}
proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", true);
}
}
return proc;
}
void notifyPackageUse(String packageName, int reason) {
synchronized(this) {
getPackageManagerInternalLocked().notifyPackageUse(packageName, reason);
}
}
boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
return transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
|| transit == TRANSIT_TASK_TO_FRONT;
}
boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs,
String processName, String abiOverride, int uid, Runnable crashHandler) {
synchronized(this) {
ApplicationInfo info = new ApplicationInfo();
// In general the ApplicationInfo.uid isn't neccesarily equal to ProcessRecord.uid.
// For isolated processes, the former contains the parent's uid and the latter the
// actual uid of the isolated process.
// In the special case introduced by this method (which is, starting an isolated
// process directly from the SystemServer without an actual parent app process) the
// closest thing to a parent's uid is SYSTEM_UID.
// The only important thing here is to keep AI.uid != PR.uid, in order to trigger
// the |isolated| logic in the ProcessRecord constructor.
info.uid = SYSTEM_UID;
info.processName = processName;
info.className = entryPoint;
info.packageName = "android";
info.seInfoUser = SELinuxUtil.COMPLETE_STR;
info.targetSdkVersion = Build.VERSION.SDK_INT;
ProcessRecord proc = startProcessLocked(processName, info /* info */,
false /* knownToBeDead */, 0 /* intentFlags */, "" /* hostingType */,
null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
uid, true /* keepIfLarge */, abiOverride, entryPoint, entryPointArgs,
crashHandler);
return proc != null;
}
}
@GuardedBy("this")
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}
@GuardedBy("this")
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
long startTime = SystemClock.elapsedRealtime();
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
checkTime(startTime, "startProcess: after getProcessRecord");
if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
// If we are in the background, then check to see if this process
// is bad. If so, we will just silently fail.
if (mAppErrors.isBadProcessLocked(info)) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+ "/" + info.processName);
return null;
}
} else {
// When the user is explicitly starting a process, then clear its
// crash count so that we won't make it bad until they see at
// least one crash dialog again, and make the process good again
// if it had been bad.
if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+ "/" + info.processName);
mAppErrors.resetProcessCrashTimeLocked(info);
if (mAppErrors.isBadProcessLocked(info)) {
EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
UserHandle.getUserId(info.uid), info.uid,
info.processName);
mAppErrors.clearBadProcessLocked(info);
if (app != null) {
app.bad = false;
}
}
}
} else {
// If this is an isolated process, it can't re-use an existing process.
app = null;
}
// We don't have to do anything more if:
// (1) There is an existing application record; and
// (2) The caller doesn't think it is dead, OR there is no thread
// object attached to it so we know it couldn't have crashed; and
// (3) There is a pid assigned to it, so it is either starting or
// already running.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
+ " app=" + app + " knownToBeDead=" + knownToBeDead
+ " thread=" + (app != null ? app.thread : null)
+ " pid=" + (app != null ? app.pid : -1));
if (app != null && app.pid > 0) {
if ((!knownToBeDead && !app.killed) || app.thread == null) {
// We already have the app running, or are waiting for it to
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName, info.versionCode, mProcessStats);
checkTime(startTime, "startProcess: done, added package to proc");
return app;
}
// An application record is attached to a previous process,
// clean it up now.
if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_PROCESSES, "App died: " + app);
checkTime(startTime, "startProcess: bad proc running, killing");
killProcessGroup(app.uid, app.pid);
handleAppDiedLocked(app, true, true);
checkTime(startTime, "startProcess: done killing old proc");
}
String hostingNameStr = hostingName != null
? hostingName.flattenToShortString() : null;
if (app == null) {
checkTime(startTime, "startProcess: creating new process record");
app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
if (app == null) {
Slog.w(TAG, "Failed making new process record for "
+ processName + "/" + info.uid + " isolated=" + isolated);
return null;
}
app.crashHandler = crashHandler;
app.isolatedEntryPoint = entryPoint;
app.isolatedEntryPointArgs = entryPointArgs;
checkTime(startTime, "startProcess: done creating new process record");
} else {
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName, info.versionCode, mProcessStats);
checkTime(startTime, "startProcess: added package to existing proc");
}
// If the system is not ready yet, then hold off on starting this
// process until it is.
if (!mProcessesReady
&& !isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if (!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
"System not ready, putting on hold: " + app);
checkTime(startTime, "startProcess: returning with proc on hold");
return app;
}
checkTime(startTime, "startProcess: stepping in to startProcess");
final boolean success = startProcessLocked(app, hostingType, hostingNameStr, abiOverride);
checkTime(startTime, "startProcess: done starting proc!");
return success ? app : null;
}
boolean isAllowedWhileBooting(ApplicationInfo ai) {
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
@GuardedBy("this")
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
}
@GuardedBy("this")
private final boolean startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr, String abiOverride) {
return startProcessLocked(app, hostingType, hostingNameStr,
false /* disableHiddenApiChecks */, abiOverride);
}
/**
* @return {@code true} if process start is successful, false otherwise.
*/
@GuardedBy("this")
private final boolean startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
if (app.pendingStart) {
return true;
}
long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != MY_PID) {
checkTime(startTime, "startProcess: removing from pids map");
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(app.pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
checkTime(startTime, "startProcess: done removing from pids map");
app.setPid(0);
}
if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
"startProcessLocked removing on hold: " + app);
mProcessesOnHold.remove(app);
checkTime(startTime, "startProcess: starting to update cpu stats");
updateCpuStats();
checkTime(startTime, "startProcess: done updating cpu stats");
try {
try {
final int userId = UserHandle.getUserId(app.uid);
AppGlobals.getPackageManager().checkPackageStartable(app.info.packageName, userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
int uid = app.uid;
int[] gids = null;
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
if (!app.isolated) {
int[] permGids = null;
try {
checkTime(startTime, "startProcess: getting gids from package manager");
final IPackageManager pm = AppGlobals.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName,
MATCH_DEBUG_TRIAGED_MISSING, app.userId);
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
app.info.packageName);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
/*
* Add shared application and profile GIDs so applications can share some
* resources like shared libraries and access user-wide resources
*/
if (ArrayUtils.isEmpty(permGids)) {
gids = new int[3];
} else {
gids = new int[permGids.length + 3];
System.arraycopy(permGids, 0, gids, 3, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
// Replace any invalid GIDs
if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
}
checkTime(startTime, "startProcess: building args");
if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopComponent != null
&& app.processName.equals(mTopComponent.getPackageName())) {
uid = 0;
}
if (mFactoryTest == FactoryTest.FACTORY_TEST_HIGH_LEVEL
&& (app.info.flags&ApplicationInfo.FLAG_FACTORY_TEST) != 0) {
uid = 0;
}
}
int runtimeFlags = 0;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
// Also turn on CheckJNI for debuggable apps. It's quite
// awkward to turn on otherwise.
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
// Run the app in safe mode if its manifest requests so or the
// system is booted in safe mode.
if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
mSafeMode == true) {
runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
}
if ("1".equals(SystemProperties.get("debug.checkjni"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
if ("1".equals(genDebugInfoProperty) || "true".equals(genDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
String genMiniDebugInfoProperty = SystemProperties.get("dalvik.vm.minidebuginfo");
if ("1".equals(genMiniDebugInfoProperty) || "true".equals(genMiniDebugInfoProperty)) {
runtimeFlags |= Zygote.DEBUG_GENERATE_MINI_DEBUG_INFO;
}
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
if ("1".equals(SystemProperties.get("debug.assert"))) {
runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) {
// Enable all debug flags required by the native debugger.
runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT; // Don't interpret anything
runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; // Disbale optimizations
mNativeDebuggingApp = null;
}
if (app.info.isPrivilegedApp() &&
DexManager.isPackageSelectedToRunOob(app.pkgList.keySet())) {
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
app.info.maybeUpdateHiddenApiEnforcementPolicy(
mHiddenApiBlacklist.getPolicyForPrePApps(),
mHiddenApiBlacklist.getPolicyForPApps());
@HiddenApiEnforcementPolicy int policy =
app.info.getHiddenApiEnforcementPolicy();
int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
throw new IllegalStateException("Invalid API policy: " + policy);
}
runtimeFlags |= policyBits;
}
String invokeWith = null;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// Debuggable apps may include a wrapper script with their library directory.
String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
if (new File(wrapperFileName).exists()) {
invokeWith = "/system/bin/logwrapper " + wrapperFileName;
}
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
if (requiredAbi == null) {
requiredAbi = Build.SUPPORTED_ABIS[0];
}
String instructionSet = null;
if (app.info.primaryCpuAbi != null) {
instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
}
app.gids = gids;
app.requiredAbi = requiredAbi;
app.instructionSet = instructionSet;
// the per-user SELinux context must be set
if (TextUtils.isEmpty(app.info.seInfoUser)) {
Slog.wtf(TAG, "SELinux tag not defined",
new IllegalStateException("SELinux tag not defined for "
+ app.info.packageName + " (uid " + app.uid + ")"));
}
final String seInfo = app.info.seInfo
+ (TextUtils.isEmpty(app.info.seInfoUser) ? "" : app.info.seInfoUser);
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
final String entryPoint = "android.app.ActivityThread";
return startProcessLocked(hostingType, hostingNameStr, entryPoint, app, uid, gids,
runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet, invokeWith,
startTime);
} catch (RuntimeException e) {
Slog.e(TAG, "Failure starting process " + app.processName, e);
// Something went very wrong while trying to start this process; one
// common case is when the package is frozen due to an active
// upgrade. To recover, clean up any active bookkeeping related to
// starting this process. (We already invoked this method once when
// the package was initially frozen through KILL_APPLICATION_MSG, so
// it doesn't hurt to use it again.)
forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid), false,
false, true, false, false, UserHandle.getUserId(app.userId), "start failure");
return false;
}
}
@GuardedBy("this")
private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
app.pendingStart = true;
app.killedByAm = false;
app.removed = false;
app.killed = false;
final long startSeq = app.startSeq = ++mProcStartSeqCounter;
app.setStartParams(uid, hostingType, hostingNameStr, seInfo, startTime);
if (mConstants.FLAG_PROCESS_START_ASYNC) {
if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
"Posting procStart msg for " + app.toShortString());
mProcStartHandler.post(() -> {
try {
synchronized (ActivityManagerService.this) {
final String reason = isProcStartValidLocked(app, startSeq);
if (reason != null) {
Slog.w(TAG_PROCESSES, app + " not valid anymore,"
+ " don't start process, " + reason);
app.pendingStart = false;
return;
}
app.usingWrapper = invokeWith != null
|| SystemProperties.get("wrap." + app.processName) != null;
mPendingStarts.put(startSeq, app);
}
final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
app, app.startUid, gids, runtimeFlags, mountExternal, app.seInfo,
requiredAbi, instructionSet, invokeWith, app.startTime);
synchronized (ActivityManagerService.this) {
handleProcessStartedLocked(app, startResult, startSeq);
}
} catch (RuntimeException e) {
synchronized (ActivityManagerService.this) {
Slog.e(TAG, "Failure starting process " + app.processName, e);
mPendingStarts.remove(startSeq);
app.pendingStart = false;
forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
false, false, true, false, false,
UserHandle.getUserId(app.userId), "start failure");
}
}
});
return true;
} else {
try {
final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
invokeWith, startTime);
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
} catch (RuntimeException e) {
Slog.e(TAG, "Failure starting process " + app.processName, e);
app.pendingStart = false;
forceStopPackageLocked(app.info.packageName, UserHandle.getAppId(app.uid),
false, false, true, false, false,
UserHandle.getUserId(app.userId), "start failure");
}
return app.pid > 0;
}
}
private ProcessStartResult startProcess(String hostingType, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
final ProcessStartResult startResult;
if (hostingType.equals("webview_service")) {
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
}
checkTime(startTime, "startProcess: returned from zygote!");
return startResult;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
@GuardedBy("this")
private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
StringBuilder sb = null;
if (app.killedByAm) {
if (sb == null) sb = new StringBuilder();
sb.append("killedByAm=true;");
}
if (mProcessNames.get(app.processName, app.uid) != app) {
if (sb == null) sb = new StringBuilder();
sb.append("No entry in mProcessNames;");
}
if (!app.pendingStart) {
if (sb == null) sb = new StringBuilder();
sb.append("pendingStart=false;");
}
if (app.startSeq > expectedStartSeq) {
if (sb == null) sb = new StringBuilder();
sb.append("seq=" + app.startSeq + ",expected=" + expectedStartSeq + ";");
}
return sb == null ? null : sb.toString();
}
@GuardedBy("this")
private boolean handleProcessStartedLocked(ProcessRecord pending,
ProcessStartResult startResult, long expectedStartSeq) {
// Indicates that this process start has been taken care of.
if (mPendingStarts.get(expectedStartSeq) == null) {
if (pending.pid == startResult.pid) {
pending.usingWrapper = startResult.usingWrapper;
// TODO: Update already existing clients of usingWrapper
}
return false;
}
return handleProcessStartedLocked(pending, startResult.pid, startResult.usingWrapper,
expectedStartSeq, false);
}
@GuardedBy("this")
private boolean handleProcessStartedLocked(ProcessRecord app, int pid, boolean usingWrapper,
long expectedStartSeq, boolean procAttached) {
mPendingStarts.remove(expectedStartSeq);
final String reason = isProcStartValidLocked(app, expectedStartSeq);
if (reason != null) {
Slog.w(TAG_PROCESSES, app + " start not valid, killing pid=" + pid
+ ", " + reason);
app.pendingStart = false;
Process.killProcessQuiet(pid);
Process.killProcessGroup(app.uid, app.pid);
return false;
}
mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);
checkTime(app.startTime, "startProcess: done updating battery stats");
EventLog.writeEvent(EventLogTags.AM_PROC_START,
UserHandle.getUserId(app.startUid), pid, app.startUid,
app.processName, app.hostingType,
app.hostingNameStr != null ? app.hostingNameStr : "");
try {
AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
app.seInfo, app.info.sourceDir, pid);
} catch (RemoteException ex) {
// Ignore
}
if (app.persistent) {
Watchdog.getInstance().processStarted(app.processName, pid);
}
checkTime(app.startTime, "startProcess: building log message");
StringBuilder buf = mStringBuilder;
buf.setLength(0);
buf.append("Start proc ");
buf.append(pid);
buf.append(':');
buf.append(app.processName);
buf.append('/');
UserHandle.formatUid(buf, app.startUid);
if (app.isolatedEntryPoint != null) {
buf.append(" [");
buf.append(app.isolatedEntryPoint);
buf.append("]");
}
buf.append(" for ");
buf.append(app.hostingType);
if (app.hostingNameStr != null) {
buf.append(" ");
buf.append(app.hostingNameStr);
}
reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
app.setPid(pid);
app.usingWrapper = usingWrapper;
app.pendingStart = false;
checkTime(app.startTime, "startProcess: starting to update pids map");
ProcessRecord oldApp;
synchronized (mPidsSelfLocked) {
oldApp = mPidsSelfLocked.get(pid);
}
// If there is already an app occupying that pid that hasn't been cleaned up
if (oldApp != null && !app.isolated) {
// Clean up anything relating to this pid first
Slog.w(TAG, "Reusing pid " + pid
+ " while app is still mapped to it");
cleanUpApplicationRecordLocked(oldApp, false, false, -1,
true /*replacingPid*/);
}
synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(pid, app);
if (!procAttached) {
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
checkTime(app.startTime, "startProcess: done updating pids map");
return true;
}
void updateUsageStats(ActivityRecord component, boolean resumed) {
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
"updateUsageStats: comp=" + component + "res=" + resumed);
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
component.app.uid, component.realActivity.getPackageName(),
component.realActivity.getShortClassName(), resumed ?
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
if (resumed) {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
UsageEvents.Event.MOVE_TO_FOREGROUND);
}
synchronized (stats) {
stats.noteActivityResumedLocked(component.app.uid);
}
} else {
if (mUsageStatsService != null) {
mUsageStatsService.reportEvent(component.realActivity, component.userId,
UsageEvents.Event.MOVE_TO_BACKGROUND);
}
synchronized (stats) {
stats.noteActivityPausedLocked(component.app.uid);
}
}
}
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and don't try to start anything.
return false;
}
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instr == null) {
intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
// For ANR debugging to verify if the user activity is the one that actually
// launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
mActivityStartController.startHomeActivity(intent, aInfo, myReason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId) {
ActivityInfo ai = null;
ComponentName comp = intent.getComponent();
try {
if (comp != null) {
// Factory test.
ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
} else {
ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags, userId);
if (info != null) {
ai = info.activityInfo;
}
}
} catch (RemoteException e) {
// ignore
}
return ai;
}
boolean getCheckedForSetup() {
return mCheckedForSetup;
}
void setCheckedForSetup(boolean checked) {
mCheckedForSetup = checked;
}
CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) {
return mCompatModePackages.compatibilityInfoForPackageLocked(ai);
}
void enforceNotIsolatedCaller(String caller) {
if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
}
@Override
public int getFrontActivityScreenCompatMode() {
enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
synchronized (this) {
return mCompatModePackages.getFrontActivityScreenCompatModeLocked();
}
}
@Override
public void setFrontActivityScreenCompatMode(int mode) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setFrontActivityScreenCompatMode");
synchronized (this) {
mCompatModePackages.setFrontActivityScreenCompatModeLocked(mode);
}
}
@Override
public int getPackageScreenCompatMode(String packageName) {
enforceNotIsolatedCaller("getPackageScreenCompatMode");
synchronized (this) {
return mCompatModePackages.getPackageScreenCompatModeLocked(packageName);
}
}
@Override
public void setPackageScreenCompatMode(String packageName, int mode) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setPackageScreenCompatMode");
synchronized (this) {
mCompatModePackages.setPackageScreenCompatModeLocked(packageName, mode);
}
}
@Override
public boolean getPackageAskScreenCompat(String packageName) {
enforceNotIsolatedCaller("getPackageAskScreenCompat");
synchronized (this) {
return mCompatModePackages.getPackageAskCompatModeLocked(packageName);
}
}
@Override
public void setPackageAskScreenCompat(String packageName, boolean ask) {
enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
"setPackageAskScreenCompat");
synchronized (this) {
mCompatModePackages.setPackageAskCompatModeLocked(packageName, ask);
}
}
private boolean hasUsageStatsPermission(String callingPackage) {
final int mode = mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS,
Binder.getCallingUid(), callingPackage);
if (mode == AppOpsManager.MODE_DEFAULT) {
return checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
== PackageManager.PERMISSION_GRANTED;
}
return mode == AppOpsManager.MODE_ALLOWED;
}
@Override
public int getPackageProcessState(String packageName, String callingPackage) {
if (!hasUsageStatsPermission(callingPackage)) {
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"getPackageProcessState");
}
int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
synchronized (this) {
for (int i=mLruProcesses.size()-1; i>=0; i--) {
final ProcessRecord proc = mLruProcesses.get(i);
if (procState > proc.setProcState) {
if (proc.pkgList.containsKey(packageName) ||
(proc.pkgDeps != null && proc.pkgDeps.contains(packageName))) {
procState = proc.setProcState;
}
}
}
}
return procState;
}
@Override
public boolean setProcessMemoryTrimLevel(String process, int userId, int level)
throws RemoteException {
synchronized (this) {
final ProcessRecord app = findProcessLocked(process, userId, "setProcessMemoryTrimLevel");
if (app == null) {
throw new IllegalArgumentException("Unknown process: " + process);
}
if (app.thread == null) {
throw new IllegalArgumentException("Process has no app thread");
}
if (app.trimMemoryLevel >= level) {
throw new IllegalArgumentException(
"Unable to set a higher trim level than current level");
}
if (!(level < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN ||
app.curProcState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)) {
throw new IllegalArgumentException("Unable to set a background trim level "
+ "on a foreground process");
}
app.thread.scheduleTrimMemory(level);
app.trimMemoryLevel = level;
return true;
}
}
private void dispatchProcessesChanged() {
int N;
synchronized (this) {
N = mPendingProcessChanges.size();
if (mActiveProcessChanges.length < N) {
mActiveProcessChanges = new ProcessChangeItem[N];
}
mPendingProcessChanges.toArray(mActiveProcessChanges);
mPendingProcessChanges.clear();
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"*** Delivering " + N + " process changes");
}
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
i--;
final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
if (observer != null) {
try {
for (int j=0; j 0) {
i--;
final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
if (observer != null) {
try {
observer.onProcessDied(pid, uid);
} catch (RemoteException e) {
}
}
}
mProcessObservers.finishBroadcast();
}
@VisibleForTesting
void dispatchUidsChanged() {
int N;
synchronized (this) {
N = mPendingUidChanges.size();
if (mActiveUidChanges.length < N) {
mActiveUidChanges = new UidRecord.ChangeItem[N];
}
for (int i=0; i 0) {
i--;
dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i),
(UidObserverRegistration) mUidObservers.getBroadcastCookie(i), N);
}
mUidObservers.finishBroadcast();
if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
for (int j = 0; j < N; ++j) {
final UidRecord.ChangeItem item = mActiveUidChanges[j];
if ((item.change & UidRecord.CHANGE_GONE) != 0) {
mValidateUids.remove(item.uid);
} else {
UidRecord validateUid = mValidateUids.get(item.uid);
if (validateUid == null) {
validateUid = new UidRecord(item.uid);
mValidateUids.put(item.uid, validateUid);
}
if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
validateUid.idle = true;
} else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
validateUid.idle = false;
}
validateUid.curProcState = validateUid.setProcState = item.processState;
validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
}
}
}
synchronized (this) {
for (int j = 0; j < N; j++) {
mAvailUidChanges.add(mActiveUidChanges[j]);
}
}
}
private void dispatchUidsChangedForObserver(IUidObserver observer,
UidObserverRegistration reg, int changesSize) {
if (observer == null) {
return;
}
try {
for (int j = 0; j < changesSize; j++) {
UidRecord.ChangeItem item = mActiveUidChanges[j];
final int change = item.change;
if (change == UidRecord.CHANGE_PROCSTATE &&
(reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
// No-op common case: no significant change, the observer is not
// interested in all proc state changes.
continue;
}
final long start = SystemClock.uptimeMillis();
if ((change & UidRecord.CHANGE_IDLE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_IDLE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID idle uid=" + item.uid);
observer.onUidIdle(item.uid, item.ephemeral);
}
} else if ((change & UidRecord.CHANGE_ACTIVE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_ACTIVE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID active uid=" + item.uid);
observer.onUidActive(item.uid);
}
}
if ((reg.which & ActivityManager.UID_OBSERVER_CACHED) != 0) {
if ((change & UidRecord.CHANGE_CACHED) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID cached uid=" + item.uid);
observer.onUidCachedChanged(item.uid, true);
} else if ((change & UidRecord.CHANGE_UNCACHED) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID active uid=" + item.uid);
observer.onUidCachedChanged(item.uid, false);
}
}
if ((change & UidRecord.CHANGE_GONE) != 0) {
if ((reg.which & ActivityManager.UID_OBSERVER_GONE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID gone uid=" + item.uid);
observer.onUidGone(item.uid, item.ephemeral);
}
if (reg.lastProcStates != null) {
reg.lastProcStates.delete(item.uid);
}
} else {
if ((reg.which & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"UID CHANGED uid=" + item.uid
+ ": " + item.processState);
boolean doReport = true;
if (reg.cutpoint >= ActivityManager.MIN_PROCESS_STATE) {
final int lastState = reg.lastProcStates.get(item.uid,
ActivityManager.PROCESS_STATE_UNKNOWN);
if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) {
final boolean lastAboveCut = lastState <= reg.cutpoint;
final boolean newAboveCut = item.processState <= reg.cutpoint;
doReport = lastAboveCut != newAboveCut;
} else {
doReport = item.processState
!= ActivityManager.PROCESS_STATE_NONEXISTENT;
}
}
if (doReport) {
if (reg.lastProcStates != null) {
reg.lastProcStates.put(item.uid, item.processState);
}
observer.onUidStateChanged(item.uid, item.processState,
item.procStateSeq);
}
}
}
final int duration = (int) (SystemClock.uptimeMillis() - start);
if (reg.mMaxDispatchTime < duration) {
reg.mMaxDispatchTime = duration;
}
if (duration >= SLOW_UID_OBSERVER_THRESHOLD_MS) {
reg.mSlowDispatchCount++;
}
}
} catch (RemoteException e) {
}
}
void dispatchOomAdjObserver(String msg) {
OomAdjObserver observer;
synchronized (this) {
observer = mCurOomAdjObserver;
}
if (observer != null) {
observer.onOomAdjMessage(msg);
}
}
void setOomAdjObserver(int uid, OomAdjObserver observer) {
synchronized (this) {
mCurOomAdjUid = uid;
mCurOomAdjObserver = observer;
}
}
void clearOomAdjObserver() {
synchronized (this) {
mCurOomAdjUid = -1;
mCurOomAdjObserver = null;
}
}
void reportOomAdjMessageLocked(String tag, String msg) {
Slog.d(tag, msg);
if (mCurOomAdjObserver != null) {
mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
}
}
void reportUidInfoMessageLocked(String tag, String msg, int uid) {
Slog.i(TAG, msg);
if (mCurOomAdjObserver != null && uid == mCurOomAdjUid) {
mUiHandler.obtainMessage(DISPATCH_OOM_ADJ_OBSERVER_MSG, msg).sendToTarget();
}
}
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
true /*validateIncomingUser*/);
}
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
boolean validateIncomingUser) {
enforceNotIsolatedCaller("startActivity");
userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
// TODO: Switch to user app stacks here.
return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute();
}
@Override
public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
int userId) {
// This is very dangerous -- it allows you to perform a start activity (including
// permission grants) as any app that may launch one of your own activities. So
// we will only allow this to be done from activities that are part of the core framework,
// and then only when they are running as the system.
final ActivityRecord sourceRecord;
final int targetUid;
final String targetPackage;
final boolean isResolver;
synchronized (this) {
if (resultTo == null) {
throw new SecurityException("Must be called from an activity");
}
sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
if (sourceRecord == null) {
throw new SecurityException("Called with bad activity token: " + resultTo);
}
if (!sourceRecord.info.packageName.equals("android")) {
throw new SecurityException(
"Must be called from an activity that is declared in the android package");
}
if (sourceRecord.app == null) {
throw new SecurityException("Called without a process attached to activity");
}
if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) {
// This is still okay, as long as this activity is running under the
// uid of the original calling activity.
if (sourceRecord.app.uid != sourceRecord.launchedFromUid) {
throw new SecurityException(
"Calling activity in uid " + sourceRecord.app.uid
+ " must be system uid or original calling uid "
+ sourceRecord.launchedFromUid);
}
}
if (ignoreTargetSecurity) {
if (intent.getComponent() == null) {
throw new SecurityException(
"Component must be specified with ignoreTargetSecurity");
}
if (intent.getSelector() != null) {
throw new SecurityException(
"Selector not allowed with ignoreTargetSecurity");
}
}
targetUid = sourceRecord.launchedFromUid;
targetPackage = sourceRecord.launchedFromPackage;
isResolver = sourceRecord.isResolverOrChildActivity();
}
if (userId == UserHandle.USER_NULL) {
userId = UserHandle.getUserId(sourceRecord.app.uid);
}
// TODO: Switch to user app stacks here.
try {
return mActivityStartController.obtainStarter(intent, "startActivityAsCaller")
.setCallingUid(targetUid)
.setCallingPackage(targetPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setActivityOptions(bOptions)
.setMayWait(userId)
.setIgnoreTargetSecurity(ignoreTargetSecurity)
.setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
.execute();
} catch (SecurityException e) {
// XXX need to figure out how to propagate to original app.
// A SecurityException here is generally actually a fault of the original
// calling activity (such as a fairly granting permissions), so propagate it
// back to them.
/*
StringBuilder msg = new StringBuilder();
msg.append("While launching");
msg.append(intent.toString());
msg.append(": ");
msg.append(e.getMessage());
*/
throw e;
}
}
@Override
public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivityAndWait");
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
WaitResult res = new WaitResult();
// TODO: Switch to user app stacks here.
mActivityStartController.obtainStarter(intent, "startActivityAndWait")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setActivityOptions(bOptions)
.setMayWait(userId)
.setProfilerInfo(profilerInfo)
.setWaitResult(res)
.execute();
return res;
}
@Override
public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, Configuration config, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivityWithConfig");
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
// TODO: Switch to user app stacks here.
return mActivityStartController.obtainStarter(intent, "startActivityWithConfig")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setGlobalConfiguration(config)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute();
}
@Override
public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startActivityIntentSender");
// Refuse possible leaked file descriptors
if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (!(target instanceof PendingIntentRecord)) {
throw new IllegalArgumentException("Bad PendingIntent object");
}
PendingIntentRecord pir = (PendingIntentRecord)target;
synchronized (this) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
final ActivityStack stack = getFocusedStack();
if (stack.mResumedActivity != null &&
stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
mAppSwitchesAllowedTime = 0;
}
}
int ret = pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
return ret;
}
@Override
public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, IVoiceInteractionSession session,
IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions, int userId) {
enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
if (session == null || interactor == null) {
throw new NullPointerException("null session or interactor");
}
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ALLOW_FULL_ONLY, "startVoiceActivity", null);
// TODO: Switch to user app stacks here.
return mActivityStartController.obtainStarter(intent, "startVoiceActivity")
.setCallingUid(callingUid)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setVoiceSession(session)
.setVoiceInteractor(interactor)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute();
}
@Override
public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
Intent intent, String resolvedType, Bundle bOptions, int userId) {
enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ALLOW_FULL_ONLY, "startAssistantActivity", null);
return mActivityStartController.obtainStarter(intent, "startAssistantActivity")
.setCallingUid(callingUid)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute();
}
@Override
public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
IRecentsAnimationRunner recentsAnimationRunner) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
final int recentsUid = mRecentTasks.getRecentsComponentUid();
// Start a new recents animation
final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
mActivityStartController, mWindowManager, mUserController, callingPid);
anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
recentsUid, assistDataReceiver);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
final long callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
// Cancel the recents animation synchronously (do not hold the WM lock)
mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
? REORDER_MOVE_TO_ORIGINAL_POSITION
: REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options)
throws RemoteException {
Slog.i(TAG, "Activity tried to startVoiceInteraction");
synchronized (this) {
ActivityRecord activity = getFocusedStack().getTopActivity();
if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
throw new SecurityException("Only focused activity can call startVoiceInteraction");
}
if (mRunningVoice != null || activity.getTask().voiceSession != null
|| activity.voiceSession != null) {
Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
return;
}
if (activity.pendingVoiceInteractionStart) {
Slog.w(TAG, "Pending start of voice interaction already.");
return;
}
activity.pendingVoiceInteractionStart = true;
}
LocalServices.getService(VoiceInteractionManagerInternal.class)
.startLocalVoiceInteraction(callingActivity, options);
}
@Override
public void stopLocalVoiceInteraction(IBinder callingActivity) throws RemoteException {
LocalServices.getService(VoiceInteractionManagerInternal.class)
.stopLocalVoiceInteraction(callingActivity);
}
@Override
public boolean supportsLocalVoiceInteraction() throws RemoteException {
return LocalServices.getService(VoiceInteractionManagerInternal.class)
.supportsLocalVoiceInteraction();
}
@GuardedBy("this")
void onLocalVoiceInteractionStartedLocked(IBinder activity,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
if (activityToCallback == null) return;
activityToCallback.setVoiceSessionLocked(voiceSession);
// Inform the activity
try {
activityToCallback.app.thread.scheduleLocalVoiceInteractionStarted(activity,
voiceInteractor);
long token = Binder.clearCallingIdentity();
try {
startRunningVoiceLocked(voiceSession, activityToCallback.appInfo.uid);
} finally {
Binder.restoreCallingIdentity(token);
}
// TODO: VI Should we cache the activity so that it's easier to find later
// rather than scan through all the stacks and activities?
} catch (RemoteException re) {
activityToCallback.clearVoiceSessionLocked();
// TODO: VI Should this terminate the voice session?
}
}
@Override
public void setVoiceKeepAwake(IVoiceInteractionSession session, boolean keepAwake) {
synchronized (this) {
if (mRunningVoice != null && mRunningVoice.asBinder() == session.asBinder()) {
if (keepAwake) {
mVoiceWakeLock.acquire();
} else {
mVoiceWakeLock.release();
}
}
}
}
@Override
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle bOptions) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
if (r == null) {
SafeActivityOptions.abort(options);
return false;
}
if (r.app == null || r.app.thread == null) {
// The caller is not running... d'oh!
SafeActivityOptions.abort(options);
return false;
}
intent = new Intent(intent);
// The caller is not allowed to change the data.
intent.setDataAndType(r.intent.getData(), r.intent.getType());
// And we are resetting to find the next component...
intent.setComponent(null);
final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
ActivityInfo aInfo = null;
try {
List resolves =
AppGlobals.getPackageManager().queryIntentActivities(
intent, r.resolvedType,
PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
UserHandle.getCallingUserId()).getList();
// Look for the original activity in the list...
final int N = resolves != null ? resolves.size() : 0;
for (int i=0; i activities = new ArrayList<>(proc.activities);
for (int i = 0; i < activities.size(); i++) {
ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
null, "finish-heavy", true);
}
}
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
proc.userId, 0));
mHeavyWeightProcess = null;
}
}
@Override
public void crashApplication(int uid, int initialPid, String packageName, int userId,
String message) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: crashApplication() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
synchronized(this) {
mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId, message);
}
}
@Override
public final void finishSubActivity(IBinder token, String resultWho,
int requestCode) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
}
Binder.restoreCallingIdentity(origId);
}
}
@Override
public boolean finishActivityAffinity(IBinder token) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
try {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return false;
}
// Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
// can finish.
final TaskRecord task = r.getTask();
if (mLockTaskController.activityBlockedFromFinish(r)) {
return false;
}
return task.getStack().finishActivityAffinityLocked(r);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
@Override
public void finishVoiceTask(IVoiceInteractionSession session) {
synchronized (this) {
final long origId = Binder.clearCallingIdentity();
try {
// TODO: VI Consider treating local voice interactions and voice tasks
// differently here
mStackSupervisor.finishVoiceTask(session);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
@Override
public boolean releaseActivityInstance(IBinder token) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
try {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return false;
}
return r.getStack().safelyDestroyActivityLocked(r, "app-req");
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
@Override
public void releaseSomeActivities(IApplicationThread appInt) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
try {
ProcessRecord app = getRecordForAppLocked(appInt);
mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
@Override
public boolean willActivityBeVisible(IBinder token) {
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
return stack.willActivityBeVisibleLocked(token);
}
return false;
}
}
@Override
public void overridePendingTransition(IBinder token, String packageName,
int enterAnim, int exitAnim) {
synchronized(this) {
ActivityRecord self = ActivityRecord.isInStackLocked(token);
if (self == null) {
return;
}
final long origId = Binder.clearCallingIdentity();
if (self.isState(ActivityState.RESUMED, ActivityState.PAUSING)) {
mWindowManager.overridePendingAppTransition(packageName,
enterAnim, exitAnim, null);
}
Binder.restoreCallingIdentity(origId);
}
}
/**
* Main function for removing an existing process from the activity manager
* as a result of that process going away. Clears out all connections
* to the process.
*/
@GuardedBy("this")
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
false /*replacingPid*/);
if (!kept && !restarting) {
removeLruProcessLocked(app);
if (pid > 0) {
ProcessList.remove(pid);
}
}
if (mProfileProc == app) {
clearProfilerLocked();
}
// Remove this application's activities from active lists.
boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
app.clearRecentTasks();
app.activities.clear();
if (app.instr != null) {
Slog.w(TAG, "Crash of app " + app.processName
+ " running instrumentation " + app.instr.mClass);
Bundle info = new Bundle();
info.putString("shortMsg", "Process crashed.");
finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
}
mWindowManager.deferSurfaceLayout();
try {
if (!restarting && hasVisibleActivities
&& !mStackSupervisor.resumeFocusedStackTopActivityLocked()) {
// If there was nothing to resume, and we are not already restarting this process, but
// there is a visible activity that is hosted by the process... then make sure all
// visible activities are running, taking care of restarting this process.
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
} finally {
mWindowManager.continueSurfaceLayout();
}
}
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
final IBinder threadBinder = thread.asBinder();
// Find the application record.
for (int i=mLruProcesses.size()-1; i>=0; i--) {
final ProcessRecord rec = mLruProcesses.get(i);
if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
return i;
}
}
return -1;
}
ProcessRecord getRecordForAppLocked(IApplicationThread thread) {
if (thread == null) {
return null;
}
int appIndex = getLRURecordIndexForAppLocked(thread);
if (appIndex >= 0) {
return mLruProcesses.get(appIndex);
}
// Validation: if it isn't in the LRU list, it shouldn't exist, but let's
// double-check that.
final IBinder threadBinder = thread.asBinder();
final ArrayMap> pmap = mProcessNames.getMap();
for (int i = pmap.size()-1; i >= 0; i--) {
final SparseArray procs = pmap.valueAt(i);
for (int j = procs.size()-1; j >= 0; j--) {
final ProcessRecord proc = procs.valueAt(j);
if (proc.thread != null && proc.thread.asBinder() == threadBinder) {
Slog.wtf(TAG, "getRecordForApp: exists in name list but not in LRU list: "
+ proc);
return proc;
}
}
}
return null;
}
final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) {
// If there are no longer any background processes running,
// and the app that died was not running instrumentation,
// then tell everyone we are now low on memory.
boolean haveBg = false;
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
if (rec.thread != null
&& rec.setProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
haveBg = true;
break;
}
}
if (!haveBg) {
boolean doReport = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
if (doReport) {
long now = SystemClock.uptimeMillis();
if (now < (mLastMemUsageReportTime+5*60*1000)) {
doReport = false;
} else {
mLastMemUsageReportTime = now;
}
}
final ArrayList memInfos
= doReport ? new ArrayList(mLruProcesses.size()) : null;
EventLog.writeEvent(EventLogTags.AM_LOW_MEMORY, mLruProcesses.size());
long now = SystemClock.uptimeMillis();
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
if (rec == dyingProc || rec.thread == null) {
continue;
}
if (doReport) {
memInfos.add(new ProcessMemInfo(rec.processName, rec.pid, rec.setAdj,
rec.setProcState, rec.adjType, rec.makeAdjReason()));
}
if ((rec.lastLowMemory+mConstants.GC_MIN_INTERVAL) <= now) {
// The low memory report is overriding any current
// state for a GC request. Make sure to do
// heavy/important/visible/foreground processes first.
if (rec.setAdj <= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
rec.lastRequestedGc = 0;
} else {
rec.lastRequestedGc = rec.lastLowMemory;
}
rec.reportLowMemory = true;
rec.lastLowMemory = now;
mProcessesToGc.remove(rec);
addProcessToGcListLocked(rec);
}
}
if (doReport) {
Message msg = mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos);
mHandler.sendMessage(msg);
}
scheduleAppGcsLocked();
}
}
@GuardedBy("this")
final void appDiedLocked(ProcessRecord app) {
appDiedLocked(app, app.pid, app.thread, false);
}
@GuardedBy("this")
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
boolean fromBinderDied) {
// First check if this ProcessRecord is actually active for the pid.
synchronized (mPidsSelfLocked) {
ProcessRecord curProc = mPidsSelfLocked.get(pid);
if (curProc != app) {
Slog.w(TAG, "Spurious death for " + app + ", curProc for " + pid + ": " + curProc);
return;
}
}
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
stats.noteProcessDiedLocked(app.info.uid, pid);
}
if (!app.killed) {
if (!fromBinderDied) {
killProcessQuiet(pid);
}
killProcessGroup(app.uid, pid);
app.killed = true;
}
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
boolean doLowMem = app.instr == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) {
reportUidInfoMessageLocked(TAG,
"Process " + app.processName + " (pid " + pid + ") has died: "
+ ProcessList.makeOomAdjString(app.setAdj)
+ ProcessList.makeProcStateString(app.setProcState), app.info.uid);
mAllowLowerMemLevel = true;
} else {
// Note that we always want to do oom adj to update our state with the
// new number of procs.
mAllowLowerMemLevel = false;
doLowMem = false;
}
EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName,
app.setAdj, app.setProcState);
if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
"Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder());
handleAppDiedLocked(app, false, true);
if (doOomAdj) {
updateOomAdjLocked();
}
if (doLowMem) {
doLowMemReportIfNeededLocked(app);
}
} else if (app.pid != pid) {
// A new process has already been started.
reportUidInfoMessageLocked(TAG,
"Process " + app.processName + " (pid " + pid
+ ") has died and restarted (pid " + app.pid + ").", app.info.uid);
EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName);
} else if (DEBUG_PROCESSES) {
Slog.d(TAG_PROCESSES, "Received spurious death notification for thread "
+ thread.asBinder());
}
// On the device which doesn't have Cgroup, log LmkStateChanged which is used as a signal
// for pulling memory stats of other running processes when this process died.
if (!hasMemcg()) {
StatsLog.write(StatsLog.APP_DIED, SystemClock.elapsedRealtime());
}
}
/**
* If a stack trace dump file is configured, dump process stack traces.
* @param clearTraces causes the dump file to be erased prior to the new
* traces being written, if true; when false, the new traces will be
* appended to any existing file content.
* @param firstPids of dalvik VM processes to dump stack traces for first
* @param lastPids of dalvik VM processes to dump stack traces for last
* @param nativePids optional list of native pids to dump stack crawls
*/
public static File dumpStackTraces(boolean clearTraces, ArrayList firstPids,
ProcessCpuTracker processCpuTracker, SparseArray lastPids,
ArrayList nativePids) {
ArrayList extraPids = null;
// Measure CPU usage as soon as we're called in order to get a realistic sampling
// of the top users at the time of the request.
if (processCpuTracker != null) {
processCpuTracker.init();
try {
Thread.sleep(200);
} catch (InterruptedException ignored) {
}
processCpuTracker.update();
// We'll take the stack crawls of just the top apps using CPU.
final int N = processCpuTracker.countWorkingStats();
extraPids = new ArrayList<>();
for (int i = 0; i < N && extraPids.size() < 5; i++) {
ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
if (lastPids.indexOfKey(stats.pid) >= 0) {
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
extraPids.add(stats.pid);
} else if (DEBUG_ANR) {
Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: "
+ stats.pid);
}
}
}
boolean useTombstonedForJavaTraces = false;
File tracesFile;
final String tracesDirProp = SystemProperties.get("dalvik.vm.stack-trace-dir", "");
if (tracesDirProp.isEmpty()) {
// When dalvik.vm.stack-trace-dir is not set, we are using the "old" trace
// dumping scheme. All traces are written to a global trace file (usually
// "/data/anr/traces.txt") so the code below must take care to unlink and recreate
// the file if requested.
//
// This mode of operation will be removed in the near future.
String globalTracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (globalTracesPath.isEmpty()) {
Slog.w(TAG, "dumpStackTraces: no trace path configured");
return null;
}
tracesFile = new File(globalTracesPath);
try {
if (clearTraces && tracesFile.exists()) {
tracesFile.delete();
}
tracesFile.createNewFile();
FileUtils.setPermissions(globalTracesPath, 0666, -1, -1); // -rw-rw-rw-
} catch (IOException e) {
Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesFile, e);
return null;
}
} else {
File tracesDir = new File(tracesDirProp);
// When dalvik.vm.stack-trace-dir is set, we use the "new" trace dumping scheme.
// Each set of ANR traces is written to a separate file and dumpstate will process
// all such files and add them to a captured bug report if they're recent enough.
maybePruneOldTraces(tracesDir);
// NOTE: We should consider creating the file in native code atomically once we've
// gotten rid of the old scheme of dumping and lot of the code that deals with paths
// can be removed.
tracesFile = createAnrDumpFile(tracesDir);
if (tracesFile == null) {
return null;
}
useTombstonedForJavaTraces = true;
}
dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids,
useTombstonedForJavaTraces);
return tracesFile;
}
@GuardedBy("ActivityManagerService.class")
private static SimpleDateFormat sAnrFileDateFormat;
private static synchronized File createAnrDumpFile(File tracesDir) {
if (sAnrFileDateFormat == null) {
sAnrFileDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
}
final String formattedDate = sAnrFileDateFormat.format(new Date());
final File anrFile = new File(tracesDir, "anr_" + formattedDate);
try {
if (anrFile.createNewFile()) {
FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw-------
return anrFile;
} else {
Slog.w(TAG, "Unable to create ANR dump file: createNewFile failed");
}
} catch (IOException ioe) {
Slog.w(TAG, "Exception creating ANR dump file:", ioe);
}
return null;
}
/**
* Prune all trace files that are more than a day old.
*
* NOTE: It might make sense to move this functionality to tombstoned eventually, along with a
* shift away from anr_XX and tombstone_XX to a more descriptive name. We do it here for now
* since it's the system_server that creates trace files for most ANRs.
*/
private static void maybePruneOldTraces(File tracesDir) {
final long now = System.currentTimeMillis();
final File[] traceFiles = tracesDir.listFiles();
if (traceFiles != null) {
for (File file : traceFiles) {
if ((now - file.lastModified()) > DAY_IN_MILLIS) {
if (!file.delete()) {
Slog.w(TAG, "Unable to prune stale trace file: " + file);
}
}
}
}
}
/**
* Legacy code, do not use. Existing users will be deleted.
*
* @deprecated
*/
@Deprecated
public static class DumpStackFileObserver extends FileObserver {
// Keep in sync with frameworks/native/cmds/dumpstate/utils.cpp
private static final int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
private final String mTracesPath;
private boolean mClosed;
public DumpStackFileObserver(String tracesPath) {
super(tracesPath, FileObserver.CLOSE_WRITE);
mTracesPath = tracesPath;
}
@Override
public synchronized void onEvent(int event, String path) {
mClosed = true;
notify();
}
public long dumpWithTimeout(int pid, long timeout) {
sendSignal(pid, SIGNAL_QUIT);
final long start = SystemClock.elapsedRealtime();
final long waitTime = Math.min(timeout, TRACE_DUMP_TIMEOUT_MS);
synchronized (this) {
try {
wait(waitTime); // Wait for traces file to be closed.
} catch (InterruptedException e) {
Slog.wtf(TAG, e);
}
}
// This avoids a corner case of passing a negative time to the native
// trace in case we've already hit the overall timeout.
final long timeWaited = SystemClock.elapsedRealtime() - start;
if (timeWaited >= timeout) {
return timeWaited;
}
if (!mClosed) {
Slog.w(TAG, "Didn't see close of " + mTracesPath + " for pid " + pid +
". Attempting native stack collection.");
final long nativeDumpTimeoutMs = Math.min(
NATIVE_DUMP_TIMEOUT_MS, timeout - timeWaited);
Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath,
(int) (nativeDumpTimeoutMs / 1000));
}
final long end = SystemClock.elapsedRealtime();
mClosed = false;
return (end - start);
}
}
/**
* Dump java traces for process {@code pid} to the specified file. If java trace dumping
* fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
* to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent
* attempting to obtain native traces in the case of a failure. Returns the total time spent
* capturing traces.
*/
private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs) {
final long timeStart = SystemClock.elapsedRealtime();
if (!Debug.dumpJavaBacktraceToFileTimeout(pid, fileName, (int) (timeoutMs / 1000))) {
Debug.dumpNativeBacktraceToFileTimeout(pid, fileName,
(NATIVE_DUMP_TIMEOUT_MS / 1000));
}
return SystemClock.elapsedRealtime() - timeStart;
}
private static void dumpStackTraces(String tracesFile, ArrayList firstPids,
ArrayList nativePids, ArrayList extraPids,
boolean useTombstonedForJavaTraces) {
// We don't need any sort of inotify based monitoring when we're dumping traces via
// tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
// control of all writes to the file in question.
final DumpStackFileObserver observer;
if (useTombstonedForJavaTraces) {
observer = null;
} else {
// Use a FileObserver to detect when traces finish writing.
// The order of traces is considered important to maintain for legibility.
observer = new DumpStackFileObserver(tracesFile);
}
// We must complete all stack dumps within 20 seconds.
long remainingTime = 20 * 1000;
try {
if (observer != null) {
observer.startWatching();
}
// First collect all of the stacks of the most important pids.
if (firstPids != null) {
int num = firstPids.size();
for (int i = 0; i < num; i++) {
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "
+ firstPids.get(i));
final long timeTaken;
if (useTombstonedForJavaTraces) {
timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, remainingTime);
} else {
timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);
}
remainingTime -= timeTaken;
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
"); deadline exceeded.");
return;
}
if (DEBUG_ANR) {
Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
}
}
}
// Next collect the stacks of the native pids
if (nativePids != null) {
for (int pid : nativePids) {
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
final long start = SystemClock.elapsedRealtime();
Debug.dumpNativeBacktraceToFileTimeout(
pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
final long timeTaken = SystemClock.elapsedRealtime() - start;
remainingTime -= timeTaken;
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
"); deadline exceeded.");
return;
}
if (DEBUG_ANR) {
Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
}
}
}
// Lastly, dump stacks for all extra PIDs from the CPU tracker.
if (extraPids != null) {
for (int pid : extraPids) {
if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);
final long timeTaken;
if (useTombstonedForJavaTraces) {
timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
} else {
timeTaken = observer.dumpWithTimeout(pid, remainingTime);
}
remainingTime -= timeTaken;
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
"); deadline exceeded.");
return;
}
if (DEBUG_ANR) {
Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
}
}
}
} finally {
if (observer != null) {
observer.stopWatching();
}
}
}
final void logAppTooSlow(ProcessRecord app, long startTime, String msg) {
if (true || Build.IS_USER) {
return;
}
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (tracesPath == null || tracesPath.length() == 0) {
return;
}
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
StrictMode.allowThreadDiskWrites();
try {
final File tracesFile = new File(tracesPath);
final File tracesDir = tracesFile.getParentFile();
final File tracesTmp = new File(tracesDir, "__tmp__");
try {
if (tracesFile.exists()) {
tracesTmp.delete();
tracesFile.renameTo(tracesTmp);
}
StringBuilder sb = new StringBuilder();
Time tobj = new Time();
tobj.set(System.currentTimeMillis());
sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
sb.append(": ");
TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
sb.append(" since ");
sb.append(msg);
FileOutputStream fos = new FileOutputStream(tracesFile);
fos.write(sb.toString().getBytes());
if (app == null) {
fos.write("\n*** No application process!".getBytes());
}
fos.close();
FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
} catch (IOException e) {
Slog.w(TAG, "Unable to prepare slow app traces file: " + tracesPath, e);
return;
}
if (app != null && app.pid > 0) {
ArrayList firstPids = new ArrayList();
firstPids.add(app.pid);
dumpStackTraces(tracesPath, firstPids, null, null, true /* useTombstoned */);
}
File lastTracesFile = null;
File curTracesFile = null;
for (int i=9; i>=0; i--) {
String name = String.format(Locale.US, "slow%02d.txt", i);
curTracesFile = new File(tracesDir, name);
if (curTracesFile.exists()) {
if (lastTracesFile != null) {
curTracesFile.renameTo(lastTracesFile);
} else {
curTracesFile.delete();
}
}
lastTracesFile = curTracesFile;
}
tracesFile.renameTo(curTracesFile);
if (tracesTmp.exists()) {
tracesTmp.renameTo(tracesFile);
}
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
@GuardedBy("this")
final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
if (!mLaunchWarningShown) {
mLaunchWarningShown = true;
mUiHandler.post(new Runnable() {
@Override
public void run() {
synchronized (ActivityManagerService.this) {
final Dialog d = new LaunchWarningWindow(mContext, cur, next);
d.show();
mUiHandler.postDelayed(new Runnable() {
@Override
public void run() {
synchronized (ActivityManagerService.this) {
d.dismiss();
mLaunchWarningShown = false;
}
}
}, 4000);
}
}
});
}
}
@Override
public boolean clearApplicationUserData(final String packageName, boolean keepState,
final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
final int resolvedUserId = mUserController.handleIncomingUser(pid, uid, userId, false,
ALLOW_FULL_ONLY, "clearApplicationUserData", null);
final ApplicationInfo appInfo;
final boolean isInstantApp;
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
synchronized(this) {
// Instant packages are not protected
if (getPackageManagerInternalLocked().isPackageDataProtected(
resolvedUserId, packageName)) {
throw new SecurityException(
"Cannot clear data for a protected package: " + packageName);
}
ApplicationInfo applicationInfo = null;
try {
applicationInfo = pm.getApplicationInfo(packageName,
MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
} catch (RemoteException e) {
/* ignore */
}
appInfo = applicationInfo;
final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid;
if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA,
pid, uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("PID " + pid + " does not have permission "
+ android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"
+ " of package " + packageName);
}
final boolean hasInstantMetadata = getPackageManagerInternalLocked()
.hasInstantApplicationMetadata(packageName, resolvedUserId);
final boolean isUninstalledAppWithoutInstantMetadata =
(appInfo == null && !hasInstantMetadata);
isInstantApp = (appInfo != null && appInfo.isInstantApp())
|| hasInstantMetadata;
final boolean canAccessInstantApps = checkComponentPermission(
permission.ACCESS_INSTANT_APPS, pid, uid, -1, true)
== PackageManager.PERMISSION_GRANTED;
if (isUninstalledAppWithoutInstantMetadata || (isInstantApp
&& !canAccessInstantApps)) {
Slog.w(TAG, "Invalid packageName: " + packageName);
if (observer != null) {
try {
observer.onRemoveCompleted(packageName, false);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
return false;
}
if (appInfo != null) {
forceStopPackageLocked(packageName, appInfo.uid, "clear data");
mRecentTasks.removeTasksByPackageName(packageName, resolvedUserId);
}
}
final IPackageDataObserver localObserver = new IPackageDataObserver.Stub() {
@Override
public void onRemoveCompleted(String packageName, boolean succeeded)
throws RemoteException {
if (appInfo != null) {
synchronized (ActivityManagerService.this) {
finishForceStopPackageLocked(packageName, appInfo.uid);
}
}
final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
Uri.fromParts("package", packageName, null));
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.putExtra(Intent.EXTRA_UID, (appInfo != null) ? appInfo.uid : -1);
intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
if (isInstantApp) {
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
null, null, permission.ACCESS_INSTANT_APPS, null, false, false,
resolvedUserId);
} else {
broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0,
null, null, null, null, false, false, resolvedUserId);
}
if (observer != null) {
observer.onRemoveCompleted(packageName, succeeded);
}
}
};
try {
// Clear application user data
pm.clearApplicationUserData(packageName, localObserver, resolvedUserId);
if (appInfo != null) {
// Restore already established notification state and permission grants,
// so it told us to keep those intact -- it's about to emplace app data
// that is appropriate for those bits of system state.
if (!keepState) {
synchronized (this) {
// Remove all permissions granted from/to this package
removeUriPermissionsForPackageLocked(packageName, resolvedUserId, true,
false);
}
// Reset notification state
INotificationManager inm = NotificationManager.getService();
inm.clearData(packageName, appInfo.uid, uid == appInfo.uid);
}
// Clear its scheduled jobs
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
js.cancelJobsForUid(appInfo.uid, "clear data");
// Clear its pending alarms
AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
ami.removeAlarmsForUid(appInfo.uid);
}
} catch (RemoteException e) {
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
return true;
}
@Override
public void killBackgroundProcesses(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED &&
checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: killBackgroundProcesses() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
final int[] userIds = mUserController.expandUserId(userId);
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
for (int targetUserId : userIds) {
int appId = -1;
try {
appId = UserHandle.getAppId(
pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
targetUserId));
} catch (RemoteException e) {
}
if (appId == -1) {
Slog.w(TAG, "Invalid packageName: " + packageName);
return;
}
synchronized (this) {
killPackageProcessesLocked(packageName, appId, targetUserId,
ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
}
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
@Override
public void killAllBackgroundProcesses() {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED) {
final String msg = "Permission Denial: killAllBackgroundProcesses() from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final ArrayList procs = new ArrayList<>();
final int NP = mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
final SparseArray apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
if (app.persistent) {
// We don't kill persistent processes.
continue;
}
if (app.removed) {
procs.add(app);
} else if (app.setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
app.removed = true;
procs.add(app);
}
}
}
final int N = procs.size();
for (int i = 0; i < N; i++) {
removeProcessLocked(procs.get(i), false, true, "kill all background");
}
mAllowLowerMemLevel = true;
updateOomAdjLocked();
doLowMemReportIfNeededLocked(null);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
/**
* Kills all background processes, except those matching any of the
* specified properties.
*
* @param minTargetSdk the target SDK version at or above which to preserve
* processes, or {@code -1} to ignore the target SDK
* @param maxProcState the process state at or below which to preserve
* processes, or {@code -1} to ignore the process state
*/
private void killAllBackgroundProcessesExcept(int minTargetSdk, int maxProcState) {
if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
!= PackageManager.PERMISSION_GRANTED) {
final String msg = "Permission Denial: killAllBackgroundProcessesExcept() from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final ArrayList procs = new ArrayList<>();
final int NP = mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
final SparseArray apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
if (app.removed) {
procs.add(app);
} else if ((minTargetSdk < 0 || app.info.targetSdkVersion < minTargetSdk)
&& (maxProcState < 0 || app.setProcState > maxProcState)) {
app.removed = true;
procs.add(app);
}
}
}
final int N = procs.size();
for (int i = 0; i < N; i++) {
removeProcessLocked(procs.get(i), false, true, "kill all background except");
}
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
@Override
public void forceStopPackage(final String packageName, int userId) {
if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: forceStopPackage() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final int callingPid = Binder.getCallingPid();
userId = mUserController.handleIncomingUser(callingPid, Binder.getCallingUid(),
userId, true, ALLOW_FULL_ONLY, "forceStopPackage", null);
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
synchronized(this) {
int[] users = userId == UserHandle.USER_ALL
? mUserController.getUsers() : new int[] { userId };
for (int user : users) {
if (getPackageManagerInternalLocked().isPackageStateProtected(
packageName, user)) {
Slog.w(TAG, "Ignoring request to force stop protected package "
+ packageName + " u" + user);
return;
}
int pkgUid = -1;
try {
pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
user);
} catch (RemoteException e) {
}
if (pkgUid == -1) {
Slog.w(TAG, "Invalid packageName: " + packageName);
continue;
}
try {
pm.setPackageStoppedState(packageName, true, user);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ packageName + ": " + e);
}
if (mUserController.isUserRunning(user, 0)) {
forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
finishForceStopPackageLocked(packageName, pkgUid);
}
}
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
@Override
public void addPackageDependency(String packageName) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
if (callingPid == myPid()) {
// Yeah, um, no.
return;
}
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(Binder.getCallingPid());
}
if (proc != null) {
if (proc.pkgDeps == null) {
proc.pkgDeps = new ArraySet(1);
}
proc.pkgDeps.add(packageName);
}
}
}
/*
* The pkg name and app id have to be specified.
*/
@Override
public void killApplication(String pkg, int appId, int userId, String reason) {
if (pkg == null) {
return;
}
// Make sure the uid is valid.
if (appId < 0) {
Slog.w(TAG, "Invalid appid specified for pkg : " + pkg);
return;
}
int callerUid = Binder.getCallingUid();
// Only the system server can kill an application
if (UserHandle.getAppId(callerUid) == SYSTEM_UID) {
// Post an aysnc message to kill the application
Message msg = mHandler.obtainMessage(KILL_APPLICATION_MSG);
msg.arg1 = appId;
msg.arg2 = userId;
Bundle bundle = new Bundle();
bundle.putString("pkg", pkg);
bundle.putString("reason", reason);
msg.obj = bundle;
mHandler.sendMessage(msg);
} else {
throw new SecurityException(callerUid + " cannot kill pkg: " +
pkg);
}
}
@Override
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
// Only allow this from foreground processes, so that background
// applications can't abuse it to prevent system UI from being shown.
if (uid >= FIRST_APPLICATION_UID) {
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
}
if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ " from background process " + proc);
return;
}
}
closeSystemDialogsLocked(reason);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@GuardedBy("this")
void closeSystemDialogsLocked(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
if (reason != null) {
intent.putExtra("reason", reason);
}
mWindowManager.closeSystemDialogs(reason);
mStackSupervisor.closeSystemDialogsLocked();
broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
OP_NONE, null, false, false,
-1, SYSTEM_UID, UserHandle.USER_ALL);
}
@Override
public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids) {
enforceNotIsolatedCaller("getProcessMemoryInfo");
Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
for (int i=pids.length-1; i>=0; i--) {
ProcessRecord proc;
int oomAdj;
synchronized (this) {
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pids[i]);
oomAdj = proc != null ? proc.setAdj : 0;
}
}
infos[i] = new Debug.MemoryInfo();
long startTime = SystemClock.currentThreadTimeMillis();
Debug.getMemoryInfo(pids[i], infos[i]);
long endTime = SystemClock.currentThreadTimeMillis();
if (proc != null) {
synchronized (this) {
if (proc.thread != null && proc.setAdj == oomAdj) {
// Record this for posterity if the process has been stable.
proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
infos[i].getTotalUss(), infos[i].getTotalRss(), false,
ProcessStats.ADD_PSS_EXTERNAL_SLOW, endTime-startTime,
proc.pkgList);
}
}
}
}
return infos;
}
@Override
public long[] getProcessPss(int[] pids) {
enforceNotIsolatedCaller("getProcessPss");
long[] pss = new long[pids.length];
for (int i=pids.length-1; i>=0; i--) {
ProcessRecord proc;
int oomAdj;
synchronized (this) {
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pids[i]);
oomAdj = proc != null ? proc.setAdj : 0;
}
}
long[] tmpUss = new long[3];
long startTime = SystemClock.currentThreadTimeMillis();
pss[i] = Debug.getPss(pids[i], tmpUss, null);
long endTime = SystemClock.currentThreadTimeMillis();
if (proc != null) {
synchronized (this) {
if (proc.thread != null && proc.setAdj == oomAdj) {
// Record this for posterity if the process has been stable.
proc.baseProcessTracker.addPss(pss[i], tmpUss[0], tmpUss[2], false,
ProcessStats.ADD_PSS_EXTERNAL, endTime-startTime, proc.pkgList);
}
}
}
}
return pss;
}
@Override
public void killApplicationProcess(String processName, int uid) {
if (processName == null) {
return;
}
int callerUid = Binder.getCallingUid();
// Only the system server can kill an application
if (callerUid == SYSTEM_UID) {
synchronized (this) {
ProcessRecord app = getProcessRecordLocked(processName, uid, true);
if (app != null && app.thread != null) {
try {
app.thread.scheduleSuicide();
} catch (RemoteException e) {
// If the other end already died, then our work here is done.
}
} else {
Slog.w(TAG, "Process/uid not found attempting kill of "
+ processName + " / " + uid);
}
}
} else {
throw new SecurityException(callerUid + " cannot kill app process: " +
processName);
}
}
@GuardedBy("this")
private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
false, true, false, false, UserHandle.getUserId(uid), reason);
}
@GuardedBy("this")
private void finishForceStopPackageLocked(final String packageName, int uid) {
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
if (!mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
}
intent.putExtra(Intent.EXTRA_UID, uid);
intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid));
}
@GuardedBy("this")
private final boolean killPackageProcessesLocked(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
boolean doit, boolean evenPersistent, String reason) {
ArrayList procs = new ArrayList<>();
// Remove all processes this package may have touched: all with the
// same UID (except for the system or root user), and all whose name
// matches the package name.
final int NP = mProcessNames.getMap().size();
for (int ip=0; ip apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia= 0 && UserHandle.getAppId(app.uid) != appId) {
continue;
}
// Package has been specified, we want to hit all processes
// that match it. We need to qualify this by the processes
// that are running under the specified app and user ID.
} else {
final boolean isDep = app.pkgDeps != null
&& app.pkgDeps.contains(packageName);
if (!isDep && UserHandle.getAppId(app.uid) != appId) {
continue;
}
if (userId != UserHandle.USER_ALL && app.userId != userId) {
continue;
}
if (!app.pkgList.containsKey(packageName) && !isDep) {
continue;
}
}
// Process has passed all conditions, kill it!
if (!doit) {
return true;
}
app.removed = true;
procs.add(app);
}
}
int N = procs.size();
for (int i=0; i 0;
}
private void cleanupDisabledPackageComponentsLocked(
String packageName, int userId, boolean killProcess, String[] changedClasses) {
Set disabledClasses = null;
boolean packageDisabled = false;
IPackageManager pm = AppGlobals.getPackageManager();
if (changedClasses == null) {
// Nothing changed...
return;
}
// Determine enable/disable state of the package and its components.
int enabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
for (int i = changedClasses.length - 1; i >= 0; i--) {
final String changedClass = changedClasses[i];
if (changedClass.equals(packageName)) {
try {
// Entire package setting changed
enabled = pm.getApplicationEnabledSetting(packageName,
(userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
} catch (Exception e) {
// No such package/component; probably racing with uninstall. In any
// event it means we have nothing further to do here.
return;
}
packageDisabled = enabled != PackageManager.COMPONENT_ENABLED_STATE_ENABLED
&& enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
if (packageDisabled) {
// Entire package is disabled.
// No need to continue to check component states.
disabledClasses = null;
break;
}
} else {
try {
enabled = pm.getComponentEnabledSetting(
new ComponentName(packageName, changedClass),
(userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
} catch (Exception e) {
// As above, probably racing with uninstall.
return;
}
if (enabled != PackageManager.COMPONENT_ENABLED_STATE_ENABLED
&& enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
if (disabledClasses == null) {
disabledClasses = new ArraySet<>(changedClasses.length);
}
disabledClasses.add(changedClass);
}
}
}
if (!packageDisabled && disabledClasses == null) {
// Nothing to do here...
return;
}
// Clean-up disabled activities.
if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
packageName, disabledClasses, true, false, userId) && mBooted) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mStackSupervisor.scheduleIdleLocked();
}
// Clean-up disabled tasks
mRecentTasks.cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId);
// Clean-up disabled services.
mServices.bringDownDisabledPackageServicesLocked(
packageName, disabledClasses, userId, false, killProcess, true);
// Clean-up disabled providers.
ArrayList providers = new ArrayList<>();
mProviderMap.collectPackageProvidersLocked(
packageName, disabledClasses, true, false, userId, providers);
for (int i = providers.size() - 1; i >= 0; i--) {
removeDyingProviderLocked(null, providers.get(i), true);
}
// Clean-up disabled broadcast receivers.
for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
packageName, disabledClasses, userId, true);
}
}
final boolean clearBroadcastQueueForUserLocked(int userId) {
boolean didSomething = false;
for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
null, null, userId, true);
}
return didSomething;
}
@GuardedBy("this")
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, int userId, String reason) {
int i;
if (userId == UserHandle.USER_ALL && packageName == null) {
Slog.w(TAG, "Can't force stop all processes of all users, that is insane!");
}
if (appId < 0 && packageName != null) {
try {
appId = UserHandle.getAppId(AppGlobals.getPackageManager()
.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, 0));
} catch (RemoteException e) {
}
}
if (doit) {
if (packageName != null) {
Slog.i(TAG, "Force stopping " + packageName + " appid=" + appId
+ " user=" + userId + ": " + reason);
} else {
Slog.i(TAG, "Force stopping u" + userId + ": " + reason);
}
mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId);
}
boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
packageName == null ? ("stop user " + userId) : ("stop " + packageName));
didSomething |= mActivityStartController.clearPendingActivityLaunches(packageName);
if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
packageName, null, doit, evenPersistent, userId)) {
if (!doit) {
return true;
}
didSomething = true;
}
if (mServices.bringDownDisabledPackageServicesLocked(
packageName, null, userId, evenPersistent, true, doit)) {
if (!doit) {
return true;
}
didSomething = true;
}
if (packageName == null) {
// Remove all sticky broadcasts from this user.
mStickyBroadcasts.remove(userId);
}
ArrayList providers = new ArrayList<>();
if (mProviderMap.collectPackageProvidersLocked(packageName, null, doit, evenPersistent,
userId, providers)) {
if (!doit) {
return true;
}
didSomething = true;
}
for (i = providers.size() - 1; i >= 0; i--) {
removeDyingProviderLocked(null, providers.get(i), true);
}
// Remove transient permissions granted from/to this package/user
removeUriPermissionsForPackageLocked(packageName, userId, false, false);
if (doit) {
for (i = mBroadcastQueues.length - 1; i >= 0; i--) {
didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
packageName, null, userId, doit);
}
}
if (packageName == null || uninstalling) {
// Remove pending intents. For now we only do this when force
// stopping users, because we have some problems when doing this
// for packages -- app widgets are not currently cleaned up for
// such packages, so they can be left with bad pending intents.
if (mIntentSenderRecords.size() > 0) {
Iterator> it
= mIntentSenderRecords.values().iterator();
while (it.hasNext()) {
WeakReference wpir = it.next();
if (wpir == null) {
it.remove();
continue;
}
PendingIntentRecord pir = wpir.get();
if (pir == null) {
it.remove();
continue;
}
if (packageName == null) {
// Stopping user, remove all objects for the user.
if (pir.key.userId != userId) {
// Not the same user, skip it.
continue;
}
} else {
if (UserHandle.getAppId(pir.uid) != appId) {
// Different app id, skip it.
continue;
}
if (userId != UserHandle.USER_ALL && pir.key.userId != userId) {
// Different user, skip it.
continue;
}
if (!pir.key.packageName.equals(packageName)) {
// Different package, skip it.
continue;
}
}
if (!doit) {
return true;
}
didSomething = true;
it.remove();
makeIntentSenderCanceledLocked(pir);
if (pir.key.activity != null && pir.key.activity.pendingResults != null) {
pir.key.activity.pendingResults.remove(pir.ref);
}
}
}
}
if (doit) {
if (purgeCache && packageName != null) {
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
ac.removePackage(packageName);
}
}
if (mBooted) {
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mStackSupervisor.scheduleIdleLocked();
}
}
return didSomething;
}
private final ProcessRecord removeProcessNameLocked(final String name, final int uid) {
return removeProcessNameLocked(name, uid, null);
}
private final ProcessRecord removeProcessNameLocked(final String name, final int uid,
final ProcessRecord expecting) {
ProcessRecord old = mProcessNames.get(name, uid);
// Only actually remove when the currently recorded value matches the
// record that we expected; if it doesn't match then we raced with a
// newly created process and we don't want to destroy the new one.
if ((expecting == null) || (old == expecting)) {
mProcessNames.remove(name, uid);
}
if (old != null && old.uidRecord != null) {
old.uidRecord.numProcs--;
if (old.uidRecord.numProcs == 0) {
// No more processes using this uid, tell clients it is gone.
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"No more processes in " + old.uidRecord);
enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
}
old.uidRecord = null;
}
mIsolatedProcesses.remove(uid);
return old;
}
private final void addProcessNameLocked(ProcessRecord proc) {
// We shouldn't already have a process under this name, but just in case we
// need to clean up whatever may be there now.
ProcessRecord old = removeProcessNameLocked(proc.processName, proc.uid);
if (old == proc && proc.persistent) {
// We are re-adding a persistent process. Whatevs! Just leave it there.
Slog.w(TAG, "Re-adding persistent process " + proc);
} else if (old != null) {
Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
}
UidRecord uidRec = mActiveUids.get(proc.uid);
if (uidRec == null) {
uidRec = new UidRecord(proc.uid);
// This is the first appearance of the uid, report it now!
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Creating new process uid: " + uidRec);
if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0
|| mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
uidRec.setWhitelist = uidRec.curWhitelist = true;
}
uidRec.updateHasInternetPermission();
mActiveUids.put(proc.uid, uidRec);
EventLogTags.writeAmUidRunning(uidRec.uid);
noteUidProcessState(uidRec.uid, uidRec.curProcState);
}
proc.uidRecord = uidRec;
// Reset render thread tid if it was already set, so new process can set it again.
proc.renderThreadTid = 0;
uidRec.numProcs++;
mProcessNames.put(proc.processName, proc.uid, proc);
if (proc.isolated) {
mIsolatedProcesses.put(proc.uid, proc);
}
}
@GuardedBy("this")
boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
final int uid = app.uid;
if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
"Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
ProcessRecord old = mProcessNames.get(name, uid);
if (old != app) {
// This process is no longer active, so nothing to do.
Slog.w(TAG, "Ignoring remove of inactive process: " + app);
return false;
}
removeProcessNameLocked(name, uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
boolean needRestart = false;
if ((app.pid > 0 && app.pid != MY_PID) || (app.pid == 0 && app.pendingStart)) {
int pid = app.pid;
if (pid > 0) {
synchronized (mPidsSelfLocked) {
mPidsSelfLocked.remove(pid);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
}
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
getPackageManagerInternalLocked().removeIsolatedUid(app.uid);
}
}
boolean willRestart = false;
if (app.persistent && !app.isolated) {
if (!callerWillRestart) {
willRestart = true;
} else {
needRestart = true;
}
}
app.kill(reason, true);
handleAppDiedLocked(app, willRestart, allowRestart);
if (willRestart) {
removeLruProcessLocked(app);
addAppLocked(app.info, null, false, null /* ABI override */);
}
} else {
mRemovedProcesses.add(app);
}
return needRestart;
}
@GuardedBy("this")
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
removeProcessLocked(app, false, true, "timeout publishing content providers");
}
private final void processStartTimedOutLocked(ProcessRecord app) {
final int pid = app.pid;
boolean gone = false;
synchronized (mPidsSelfLocked) {
ProcessRecord knownApp = mPidsSelfLocked.get(pid);
if (knownApp != null && knownApp.thread == null) {
mPidsSelfLocked.remove(pid);
gone = true;
}
}
if (gone) {
Slog.w(TAG, "Process " + app + " failed to attach");
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, app.userId,
pid, app.uid, app.processName);
removeProcessNameLocked(app.processName, app.uid);
if (mHeavyWeightProcess == app) {
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
}
mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
// Take care of any launching providers waiting for this process.
cleanupAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
mServices.processStartTimedOutLocked(app);
app.kill("start timeout", true);
if (app.isolated) {
mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
}
removeLruProcessLocked(app);
if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
Slog.w(TAG, "Unattached app died before backup, skipping");
mHandler.post(new Runnable() {
@Override
public void run(){
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
bm.agentDisconnected(app.info.packageName);
} catch (RemoteException e) {
// Can't happen; the backup manager is local
}
}
});
}
if (isPendingBroadcastProcessLocked(pid)) {
Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
skipPendingBroadcastLocked(pid);
}
} else {
Slog.w(TAG, "Spurious process start timeout - pid not known for " + app);
}
}
@GuardedBy("this")
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
// Find the application record that is being attached... either via
// the pid if we are running in multiple processes, or just pull the
// next app record if we are emulating process with anonymous threads.
ProcessRecord app;
long startTime = SystemClock.uptimeMillis();
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
} else {
app = null;
}
// It's possible that process called attachApplication before we got a chance to
// update the internal state.
if (app == null && startSeq > 0) {
final ProcessRecord pending = mPendingStarts.get(startSeq);
if (pending != null && pending.startUid == callingUid
&& handleProcessStartedLocked(pending, pid, pending.usingWrapper,
startSeq, true)) {
app = pending;
}
}
if (app == null) {
Slog.w(TAG, "No pending application record for pid " + pid
+ " (IApplicationThread " + thread + "); dropping process");
EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
if (pid > 0 && pid != MY_PID) {
killProcessQuiet(pid);
//TODO: killProcessGroup(app.info.uid, pid);
} else {
try {
thread.scheduleExit();
} catch (Exception e) {
// Ignore exceptions.
}
}
return false;
}
// If this application record is still attached to a previous
// process, clean it up now.
if (app.thread != null) {
handleAppDiedLocked(app, true, true);
}
// Tell the process all about itself.
if (DEBUG_ALL) Slog.v(
TAG, "Binding process pid " + pid + " to record " + app);
final String processName = app.processName;
try {
AppDeathRecipient adr = new AppDeathRecipient(
app, pid, thread);
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
app.resetPackageList(mProcessStats);
startProcessLocked(app, "link fail", processName);
return false;
}
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
app.debugging = false;
app.cached = false;
app.killedByAm = false;
app.killed = false;
// We carefully use the same state that PackageManager uses for
// filtering, since we use this flag to decide if we need to install
// providers when user is unlocked later
app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
checkTime(startTime, "attachApplicationLocked: before bindApplication");
if (!normalMode) {
Slog.i(TAG, "Launching preboot mode app: " + app);
}
if (DEBUG_ALL) Slog.v(
TAG, "New app record " + app
+ " thread=" + thread.asBinder() + " pid=" + pid);
try {
int testMode = ApplicationThreadConstants.DEBUG_OFF;
if (mDebugApp != null && mDebugApp.equals(processName)) {
testMode = mWaitForDebugger
? ApplicationThreadConstants.DEBUG_WAIT
: ApplicationThreadConstants.DEBUG_ON;
app.debugging = true;
if (mDebugTransient) {
mDebugApp = mOrigDebugApp;
mWaitForDebugger = mOrigWaitForDebugger;
}
}
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
enableTrackAllocation = true;
mTrackAllocationApp = null;
}
// If the app is being launched for restore or full backup, set it up specially
boolean isRestrictedBackupMode = false;
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
isRestrictedBackupMode = mBackupTarget.appInfo.uid >= FIRST_APPLICATION_UID
&& ((mBackupTarget.backupMode == BackupRecord.RESTORE)
|| (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
}
if (app.instr != null) {
notifyPackageUse(app.instr.mClass.getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
+ processName + " with config " + getGlobalConfiguration());
ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
ProfilerInfo profilerInfo = null;
String preBindAgent = null;
if (mProfileApp != null && mProfileApp.equals(processName)) {
mProfileProc = app;
if (mProfilerInfo != null) {
// Send a profiler info object to the app if either a file is given, or
// an agent should be loaded at bind-time.
boolean needsInfo = mProfilerInfo.profileFile != null
|| mProfilerInfo.attachAgentDuringBind;
profilerInfo = needsInfo ? new ProfilerInfo(mProfilerInfo) : null;
if (mProfilerInfo.agent != null) {
preBindAgent = mProfilerInfo.agent;
}
}
} else if (app.instr != null && app.instr.mProfileFile != null) {
profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
null, false);
}
if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
// We need to do a debuggable check here. See setAgentApp for why the check is
// postponed to here.
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
String agent = mAppAgentMap.get(processName);
// Do not overwrite already requested agent.
if (profilerInfo == null) {
profilerInfo = new ProfilerInfo(null, null, 0, false, false,
mAppAgentMap.get(processName), true);
} else if (profilerInfo.agent == null) {
profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
}
}
}
if (profilerInfo != null && profilerInfo.profileFd != null) {
profilerInfo.profileFd = profilerInfo.profileFd.dup();
if (TextUtils.equals(mProfileApp, processName) && mProfilerInfo != null) {
clearProfilerLocked();
}
}
// We deprecated Build.SERIAL and it is not accessible to
// apps that target the v2 security sandbox and to apps that
// target APIs higher than O MR1. Since access to the serial
// is now behind a permission we push down the value.
final String buildSerial = (appInfo.targetSandboxVersion < 2
&& appInfo.targetSdkVersion < Build.VERSION_CODES.P)
? sTheRealBuildSerial : Build.UNKNOWN;
// Check if this is a secondary process that should be incorporated into some
// currently active instrumentation. (Note we do this AFTER all of the profiling
// stuff above because profiling can currently happen only in the primary
// instrumentation process.)
if (mActiveInstrumentation.size() > 0 && app.instr == null) {
for (int i = mActiveInstrumentation.size() - 1; i >= 0 && app.instr == null; i--) {
ActiveInstrumentation aInstr = mActiveInstrumentation.get(i);
if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
if (aInstr.mTargetProcesses.length == 0) {
// This is the wildcard mode, where every process brought up for
// the target instrumentation should be included.
if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
app.instr = aInstr;
aInstr.mRunningProcesses.add(app);
}
} else {
for (String proc : aInstr.mTargetProcesses) {
if (proc.equals(app.processName)) {
app.instr = aInstr;
aInstr.mRunningProcesses.add(app);
break;
}
}
}
}
}
}
// If we were asked to attach an agent on startup, do so now, before we're binding
// application code.
if (preBindAgent != null) {
thread.attachAgent(preBindAgent);
}
// Figure out whether the app needs to run in autofill compat mode.
boolean isAutofillCompatEnabled = false;
if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) {
final AutofillManagerInternal afm = LocalServices.getService(
AutofillManagerInternal.class);
if (afm != null) {
isAutofillCompatEnabled = afm.isCompatibilityModeRequested(
app.info.packageName, app.info.versionCode, app.userId);
}
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
} else if (app.instr != null) {
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, isAutofillCompatEnabled);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, isAutofillCompatEnabled);
}
if (profilerInfo != null) {
profilerInfo.closeFd();
profilerInfo = null;
}
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
// todo: Yikes! What should we do? For now we will try to
// start another process, but that could easily get us in
// an infinite loop of restarting processes...
Slog.wtf(TAG, "Exception thrown during bind of " + app, e);
app.resetPackageList(mProcessStats);
app.unlinkDeathRecipient();
startProcessLocked(app, "bind fail", processName);
return false;
}
// Remove this record from the list of starting applications.
mPersistentStartingProcesses.remove(app);
if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
"Attach application locked removing on hold: " + app);
mProcessesOnHold.remove(app);
boolean badApp = false;
boolean didSomething = false;
// See if the top visible activity is waiting to run in this process...
if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) {
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
// Find any services that should be running in this process...
if (!badApp) {
try {
didSomething |= mServices.attachApplicationLocked(app, processName);
checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
badApp = true;
}
}
// Check if a next-broadcast receiver is in this process...
if (!badApp && isPendingBroadcastProcessLocked(pid)) {
try {
didSomething |= sendPendingBroadcastsLocked(app);
checkTime(startTime, "attachApplicationLocked: after sendPendingBroadcastsLocked");
} catch (Exception e) {
// If the app died trying to launch the receiver we declare it 'bad'
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
badApp = true;
}
}
// Check whether the next backup agent is in this process...
if (!badApp && mBackupTarget != null && mBackupTarget.app == app) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
"New app is backup target, launching agent for " + app);
notifyPackageUse(mBackupTarget.appInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
try {
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
mBackupTarget.backupMode);
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
badApp = true;
}
}
if (badApp) {
app.kill("error during init", true);
handleAppDiedLocked(app, false, true);
return false;
}
if (!didSomething) {
updateOomAdjLocked();
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
}
return true;
}
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
@Override
public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
ActivityRecord r =
mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
false /* processPausingActivities */, config);
if (stopProfiling) {
if ((mProfileProc == r.app) && mProfilerInfo != null) {
clearProfilerLocked();
}
}
}
}
Binder.restoreCallingIdentity(origId);
}
void postFinishBooting(boolean finishBooting, boolean enableScreen) {
mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
finishBooting ? 1 : 0, enableScreen ? 1 : 0));
}
void enableScreenAfterBoot() {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
mWindowManager.enableScreenAfterBoot();
synchronized (this) {
updateEventDispatchingLocked();
}
}
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
if (Binder.getCallingUid() != myUid()) {
throw new SecurityException();
}
mWindowManager.showBootMessage(msg, always);
}
@Override
public void keyguardGoingAway(int flags) {
enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
try {
synchronized (this) {
mKeyguardController.keyguardGoingAway(flags);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
* @return whther the keyguard is currently locked.
*/
boolean isKeyguardLocked() {
return mKeyguardController.isKeyguardLocked();
}
final void finishBooting() {
synchronized (this) {
if (!mBootAnimationComplete) {
mCallFinishBooting = true;
return;
}
mCallFinishBooting = false;
}
ArraySet completedIsas = new ArraySet();
for (String abi : Build.SUPPORTED_ABIS) {
zygoteProcess.establishZygoteConnectionForAbi(abi);
final String instructionSet = VMRuntime.getInstructionSet(abi);
if (!completedIsas.contains(instructionSet)) {
try {
mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi));
} catch (InstallerException e) {
Slog.w(TAG, "Unable to mark boot complete for abi: " + abi + " (" +
e.getMessage() +")");
}
completedIsas.add(instructionSet);
}
}
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
pkgFilter.addDataScheme("package");
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
if (pkgs != null) {
for (String pkg : pkgs) {
synchronized (ActivityManagerService.this) {
if (forceStopPackageLocked(pkg, -1, false, false, false, false, false,
0, "query restart")) {
setResultCode(Activity.RESULT_OK);
return;
}
}
}
}
}
}, pkgFilter);
IntentFilter dumpheapFilter = new IntentFilter();
dumpheapFilter.addAction(DumpHeapActivity.ACTION_DELETE_DUMPHEAP);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getBooleanExtra(DumpHeapActivity.EXTRA_DELAY_DELETE, false)) {
mHandler.sendEmptyMessageDelayed(POST_DUMP_HEAP_NOTIFICATION_MSG, 5*60*1000);
} else {
mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
}
}
}, dumpheapFilter);
// Let system services know.
mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED);
synchronized (this) {
// Ensure that any processes we had put on hold are now started
// up.
final int NP = mProcessesOnHold.size();
if (NP > 0) {
ArrayList procs =
new ArrayList(mProcessesOnHold);
for (int ip=0; ip= 1");
}
for (int i=0; i ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
if (rec != null) {
if (!cancelCurrent) {
if (updateCurrent) {
if (rec.key.requestIntent != null) {
rec.key.requestIntent.replaceExtras(intents != null ?
intents[intents.length - 1] : null);
}
if (intents != null) {
intents[intents.length-1] = rec.key.requestIntent;
rec.key.allIntents = intents;
rec.key.allResolvedTypes = resolvedTypes;
} else {
rec.key.allIntents = null;
rec.key.allResolvedTypes = null;
}
}
return rec;
}
makeIntentSenderCanceledLocked(rec);
mIntentSenderRecords.remove(key);
}
if (noCreate) {
return rec;
}
rec = new PendingIntentRecord(this, key, callingUid);
mIntentSenderRecords.put(key, rec.ref);
if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
if (activity.pendingResults == null) {
activity.pendingResults
= new HashSet>();
}
activity.pendingResults.add(rec.ref);
}
return rec;
}
@Override
public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code,
Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
if (target instanceof PendingIntentRecord) {
return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,
whitelistToken, finishedReceiver, requiredPermission, options);
} else {
if (intent == null) {
// Weird case: someone has given us their own custom IIntentSender, and now
// they have someone else trying to send to it but of course this isn't
// really a PendingIntent, so there is no base Intent, and the caller isn't
// supplying an Intent... but we never want to dispatch a null Intent to
// a receiver, so um... let's make something up.
Slog.wtf(TAG, "Can't use null intent with direct IIntentSender call");
intent = new Intent(Intent.ACTION_MAIN);
}
try {
target.send(code, intent, resolvedType, whitelistToken, null,
requiredPermission, options);
} catch (RemoteException e) {
}
// Platform code can rely on getting a result back when the send is done, but if
// this intent sender is from outside of the system we can't rely on it doing that.
// So instead we don't give it the result receiver, and instead just directly
// report the finish immediately.
if (finishedReceiver != null) {
try {
finishedReceiver.performReceive(intent, 0,
null, null, false, false, UserHandle.getCallingUserId());
} catch (RemoteException e) {
}
}
return 0;
}
}
@Override
public void cancelIntentSender(IIntentSender sender) {
if (!(sender instanceof PendingIntentRecord)) {
return;
}
synchronized(this) {
PendingIntentRecord rec = (PendingIntentRecord)sender;
try {
final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName,
MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId());
if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
String msg = "Permission Denial: cancelIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " is not allowed to cancel package "
+ rec.key.packageName;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
} catch (RemoteException e) {
throw new SecurityException(e);
}
cancelIntentSenderLocked(rec, true);
}
}
void cancelIntentSenderLocked(PendingIntentRecord rec, boolean cleanActivity) {
makeIntentSenderCanceledLocked(rec);
mIntentSenderRecords.remove(rec.key);
if (cleanActivity && rec.key.activity != null) {
rec.key.activity.pendingResults.remove(rec.ref);
}
}
void makeIntentSenderCanceledLocked(PendingIntentRecord rec) {
rec.canceled = true;
RemoteCallbackList callbacks = rec.detachCancelListenersLocked();
if (callbacks != null) {
mHandler.obtainMessage(DISPATCH_PENDING_INTENT_CANCEL_MSG, callbacks).sendToTarget();
}
}
@Override
public String getPackageForIntentSender(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return null;
}
try {
PendingIntentRecord res = (PendingIntentRecord)pendingResult;
return res.key.packageName;
} catch (ClassCastException e) {
}
return null;
}
@Override
public void registerIntentSenderCancelListener(IIntentSender sender, IResultReceiver receiver) {
if (!(sender instanceof PendingIntentRecord)) {
return;
}
boolean isCancelled;
synchronized(this) {
PendingIntentRecord pendingIntent = (PendingIntentRecord) sender;
isCancelled = pendingIntent.canceled;
if (!isCancelled) {
pendingIntent.registerCancelListenerLocked(receiver);
}
}
if (isCancelled) {
try {
receiver.send(Activity.RESULT_CANCELED, null);
} catch (RemoteException e) {
}
}
}
@Override
public void unregisterIntentSenderCancelListener(IIntentSender sender,
IResultReceiver receiver) {
if (!(sender instanceof PendingIntentRecord)) {
return;
}
synchronized(this) {
((PendingIntentRecord)sender).unregisterCancelListenerLocked(receiver);
}
}
@Override
public int getUidForIntentSender(IIntentSender sender) {
if (sender instanceof PendingIntentRecord) {
try {
PendingIntentRecord res = (PendingIntentRecord)sender;
return res.uid;
} catch (ClassCastException e) {
}
}
return -1;
}
@Override
public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) {
if (!(pendingResult instanceof PendingIntentRecord)) {
return false;
}
try {
PendingIntentRecord res = (PendingIntentRecord)pendingResult;
if (res.key.allIntents == null) {
return false;
}
for (int i=0; i {
// Only update the saved args from the args that are set
r.pictureInPictureArgs.copyOnlySet(params);
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
final List actions = r.pictureInPictureArgs.getActions();
// Adjust the source bounds by the insets for the transition down
final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
"enterPictureInPictureMode");
final PinnedActivityStack stack = r.getStack();
stack.setPictureInPictureAspectRatio(aspectRatio);
stack.setPictureInPictureActions(actions);
MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
logPictureInPictureArgs(params);
};
if (isKeyguardLocked()) {
// If the keyguard is showing or occluded, then try and dismiss it before
// entering picture-in-picture (this will prompt the user to authenticate if the
// device is currently locked).
try {
dismissKeyguard(token, new KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() throws RemoteException {
mHandler.post(enterPipRunnable);
}
}, null /* message */);
} catch (RemoteException e) {
// Local call
}
} else {
// Enter picture in picture immediately otherwise
enterPipRunnable.run();
}
return true;
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized(this) {
final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
"setPictureInPictureParams", token, params);
// Only update the saved args from the args that are set
r.pictureInPictureArgs.copyOnlySet(params);
if (r.inPinnedWindowingMode()) {
// If the activity is already in picture-in-picture, update the pinned stack now
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP
final PinnedActivityStack stack = r.getStack();
if (!stack.isAnimatingBoundsToFullscreen()) {
stack.setPictureInPictureAspectRatio(
r.pictureInPictureArgs.getAspectRatio());
stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
}
}
logPictureInPictureArgs(params);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public int getMaxNumPictureInPictureActions(IBinder token) {
// Currently, this is a static constant, but later, we may change this to be dependent on
// the context of the activity
return 3;
}
private void logPictureInPictureArgs(PictureInPictureParams params) {
if (params.hasSetActions()) {
MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
params.getActions().size());
}
if (params.hasSetAspectRatio()) {
LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
MetricsLogger.action(lm);
}
}
/**
* Checks the state of the system and the activity associated with the given {@param token} to
* verify that picture-in-picture is supported for that activity.
*
* @return the activity record for the given {@param token} if all the checks pass.
*/
private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
IBinder token, PictureInPictureParams params) {
if (!mSupportsPictureInPicture) {
throw new IllegalStateException(caller
+ ": Device doesn't support picture-in-picture mode.");
}
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
throw new IllegalStateException(caller
+ ": Can't find activity for token=" + token);
}
if (!r.supportsPictureInPicture()) {
throw new IllegalStateException(caller
+ ": Current activity does not support picture-in-picture.");
}
if (params.hasSetAspectRatio()
&& !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
params.getAspectRatio())) {
final float minAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
final float maxAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
throw new IllegalArgumentException(String.format(caller
+ ": Aspect ratio is too extreme (must be between %f and %f).",
minAspectRatio, maxAspectRatio));
}
// Truncate the number of actions if necessary
params.truncateActions(getMaxNumPictureInPictureActions(token));
return r;
}
// =========================================================
// PROCESS INFO
// =========================================================
static class ProcessInfoService extends IProcessInfoService.Stub {
final ActivityManagerService mActivityManagerService;
ProcessInfoService(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
public void getProcessStatesFromPids(/*in*/ int[] pids, /*out*/ int[] states) {
mActivityManagerService.getProcessStatesAndOomScoresForPIDs(
/*in*/ pids, /*out*/ states, null);
}
@Override
public void getProcessStatesAndOomScoresFromPids(
/*in*/ int[] pids, /*out*/ int[] states, /*out*/ int[] scores) {
mActivityManagerService.getProcessStatesAndOomScoresForPIDs(
/*in*/ pids, /*out*/ states, /*out*/ scores);
}
}
/**
* For each PID in the given input array, write the current process state
* for that process into the states array, or -1 to indicate that no
* process with the given PID exists. If scores array is provided, write
* the oom score for the process into the scores array, with INVALID_ADJ
* indicating the PID doesn't exist.
*/
public void getProcessStatesAndOomScoresForPIDs(
/*in*/ int[] pids, /*out*/ int[] states, /*out*/ int[] scores) {
if (scores != null) {
enforceCallingPermission(android.Manifest.permission.GET_PROCESS_STATE_AND_OOM_SCORE,
"getProcessStatesAndOomScoresForPIDs()");
}
if (pids == null) {
throw new NullPointerException("pids");
} else if (states == null) {
throw new NullPointerException("states");
} else if (pids.length != states.length) {
throw new IllegalArgumentException("pids and states arrays have different lengths!");
} else if (scores != null && pids.length != scores.length) {
throw new IllegalArgumentException("pids and scores arrays have different lengths!");
}
synchronized (mPidsSelfLocked) {
for (int i = 0; i < pids.length; i++) {
ProcessRecord pr = mPidsSelfLocked.get(pids[i]);
states[i] = (pr == null) ? ActivityManager.PROCESS_STATE_NONEXISTENT :
pr.curProcState;
if (scores != null) {
scores[i] = (pr == null) ? ProcessList.INVALID_ADJ : pr.curAdj;
}
}
}
}
// =========================================================
// PERMISSIONS
// =========================================================
static class PermissionController extends IPermissionController.Stub {
ActivityManagerService mActivityManagerService;
PermissionController(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
public boolean checkPermission(String permission, int pid, int uid) {
return mActivityManagerService.checkPermission(permission, pid,
uid) == PackageManager.PERMISSION_GRANTED;
}
@Override
public int noteOp(String op, int uid, String packageName) {
return mActivityManagerService.mAppOpsService
.noteOperation(AppOpsManager.strOpToOp(op), uid, packageName);
}
@Override
public String[] getPackagesForUid(int uid) {
return mActivityManagerService.mContext.getPackageManager()
.getPackagesForUid(uid);
}
@Override
public boolean isRuntimePermission(String permission) {
try {
PermissionInfo info = mActivityManagerService.mContext.getPackageManager()
.getPermissionInfo(permission, 0);
return (info.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS;
} catch (NameNotFoundException nnfe) {
Slog.e(TAG, "No such permission: "+ permission, nnfe);
}
return false;
}
@Override
public int getPackageUid(String packageName, int flags) {
try {
return mActivityManagerService.mContext.getPackageManager()
.getPackageUid(packageName, flags);
} catch (NameNotFoundException nnfe) {
return -1;
}
}
}
class IntentFirewallInterface implements IntentFirewall.AMSInterface {
@Override
public int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
return ActivityManagerService.this.checkComponentPermission(permission, pid, uid,
owningUid, exported);
}
@Override
public Object getAMSLock() {
return ActivityManagerService.this;
}
}
int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}
/**
* As the only public entry point for permissions checking, this method
* can enforce the semantic that requesting a check on a null global
* permission is automatically denied. (Internally a null permission
* string is used when calling {@link #checkComponentPermission} in cases
* when only uid-based security is needed.)
*
* This can be called with or without the global lock held.
*/
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
return checkComponentPermission(permission, pid, uid, -1, true);
}
@Override
public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
// We might be performing an operation on behalf of an indirect binder
// invocation, e.g. via {@link #openContentUri}. Check and adjust the
// client identity accordingly before proceeding.
Identity tlsIdentity = sCallerIdentity.get();
if (tlsIdentity != null && tlsIdentity.token == callerToken) {
Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
+ tlsIdentity.pid + "," + tlsIdentity.uid + "}");
uid = tlsIdentity.uid;
pid = tlsIdentity.pid;
}
return checkComponentPermission(permission, pid, uid, -1, true);
}
/**
* Binder IPC calls go through the public entry point.
* This can be called with or without the global lock held.
*/
int checkCallingPermission(String permission) {
return checkPermission(permission,
Binder.getCallingPid(),
UserHandle.getAppId(Binder.getCallingUid()));
}
/**
* This can be called with or without the global lock held.
*/
void enforceCallingPermission(String permission, String func) {
if (checkCallingPermission(permission)
== PackageManager.PERMISSION_GRANTED) {
return;
}
String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + permission;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
/**
* This can be called with or without the global lock held.
*/
void enforcePermission(String permission, int pid, int uid, String func) {
if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
return;
}
String msg = "Permission Denial: " + func + " from pid=" + pid + ", uid=" + uid
+ " requires " + permission;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
/**
* This can be called with or without the global lock held.
*/
void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
enforceCallingPermission(permission, func);
}
}
/**
* Determine if UID is holding permissions required to access {@link Uri} in
* the given {@link ProviderInfo}. Final permission checking is always done
* in {@link ContentProvider}.
*/
private final boolean checkHoldingPermissionsLocked(
IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
!= PERMISSION_GRANTED) {
return false;
}
}
return checkHoldingPermissionsInternalLocked(pm, pi, grantUri, uid, modeFlags, true);
}
private final boolean checkHoldingPermissionsInternalLocked(IPackageManager pm, ProviderInfo pi,
GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
if (pi.applicationInfo.uid == uid) {
return true;
} else if (!pi.exported) {
return false;
}
boolean readMet = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
try {
// check if target holds top-level permissions
if (!readMet && pi.readPermission != null && considerUidPermissions
&& (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
readMet = true;
}
if (!writeMet && pi.writePermission != null && considerUidPermissions
&& (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
writeMet = true;
}
// track if unprotected read/write is allowed; any denied
// below removes this ability
boolean allowDefaultRead = pi.readPermission == null;
boolean allowDefaultWrite = pi.writePermission == null;
// check if target holds any that match uri
final PathPermission[] pps = pi.pathPermissions;
if (pps != null) {
final String path = grantUri.uri.getPath();
int i = pps.length;
while (i > 0 && (!readMet || !writeMet)) {
i--;
PathPermission pp = pps[i];
if (pp.match(path)) {
if (!readMet) {
final String pprperm = pp.getReadPermission();
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Checking read perm for " + pprperm + " for " + pp.getPath()
+ ": match=" + pp.match(path)
+ " check=" + pm.checkUidPermission(pprperm, uid));
if (pprperm != null) {
if (considerUidPermissions && pm.checkUidPermission(pprperm, uid)
== PERMISSION_GRANTED) {
readMet = true;
} else {
allowDefaultRead = false;
}
}
}
if (!writeMet) {
final String ppwperm = pp.getWritePermission();
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Checking write perm " + ppwperm + " for " + pp.getPath()
+ ": match=" + pp.match(path)
+ " check=" + pm.checkUidPermission(ppwperm, uid));
if (ppwperm != null) {
if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid)
== PERMISSION_GRANTED) {
writeMet = true;
} else {
allowDefaultWrite = false;
}
}
}
}
}
}
// grant unprotected read/write, if not blocked by
// above
if (allowDefaultRead) readMet = true;
if (allowDefaultWrite) writeMet = true;
} catch (RemoteException e) {
return false;
}
return readMet && writeMet;
}
public boolean isAppStartModeDisabled(int uid, String packageName) {
synchronized (this) {
return getAppStartModeLocked(uid, packageName, 0, -1, false, true, false)
== ActivityManager.APP_START_MODE_DISABLED;
}
}
// Unified app-op and target sdk check
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
}
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
// ...and legacy apps get an AppOp check
int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
uid, packageName);
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
}
switch (appop) {
case AppOpsManager.MODE_ALLOWED:
// If force-background-check is enabled, restrict all apps that aren't whitelisted.
if (mForceBackgroundCheck &&
!UserHandle.isCore(uid) &&
!isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ true)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Force background check: " +
uid + "/" + packageName + " restricted");
}
return ActivityManager.APP_START_MODE_DELAYED;
}
return ActivityManager.APP_START_MODE_NORMAL;
case AppOpsManager.MODE_IGNORED:
return ActivityManager.APP_START_MODE_DELAYED;
default:
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
}
// Service launch is available to apps with run-in-background exemptions but
// some other background operations are not. If we're doing a check
// of service-launch policy, allow those callers to proceed unrestricted.
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
if (mPackageManagerInt.isPackagePersistent(packageName)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " is persistent; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Non-persistent but background whitelisted?
if (uidOnBackgroundWhitelist(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on background whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Is this app on the battery whitelist?
if (isOnDeviceIdleWhitelistLocked(uid, /*allowExceptIdleToo=*/ false)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on idle whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly, boolean forcedStandby) {
UidRecord uidRec = mActiveUids.get(uid);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+ packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
+ (uidRec != null ? uidRec.idle : false));
if (uidRec == null || alwaysRestrict || forcedStandby || uidRec.idle) {
boolean ephemeral;
if (uidRec == null) {
ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
UserHandle.getUserId(uid), packageName);
} else {
ephemeral = uidRec.ephemeral;
}
if (ephemeral) {
// We are hard-core about ephemeral apps not running in the background.
return ActivityManager.APP_START_MODE_DISABLED;
} else {
if (disabledOnly) {
// The caller is only interested in whether app starts are completely
// disabled for the given package (that is, it is an instant app). So
// we don't need to go further, which is all just seeing if we should
// apply a "delayed" mode for a regular app.
return ActivityManager.APP_START_MODE_NORMAL;
}
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
packageTargetSdk);
if (DEBUG_BACKGROUND_CHECK) {
Slog.d(TAG, "checkAllowBackground: uid=" + uid
+ " pkg=" + packageName + " startMode=" + startMode
+ " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid, false)
+ " onwhitelist(ei)=" + isOnDeviceIdleWhitelistLocked(uid, true));
}
if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
// This is an old app that has been forced into a "compatible as possible"
// mode of background check. To increase compatibility, we will allow other
// foreground apps to cause its services to start.
if (callingPid >= 0) {
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(callingPid);
}
if (proc != null &&
!ActivityManager.isProcStateBackground(proc.curProcState)) {
// Whoever is instigating this is in the foreground, so we will allow it
// to go through.
return ActivityManager.APP_START_MODE_NORMAL;
}
}
}
return startMode;
}
}
return ActivityManager.APP_START_MODE_NORMAL;
}
/**
* @return whether a UID is in the system, user or temp doze whitelist.
*/
boolean isOnDeviceIdleWhitelistLocked(int uid, boolean allowExceptIdleToo) {
final int appId = UserHandle.getAppId(uid);
final int[] whitelist = allowExceptIdleToo
? mDeviceIdleExceptIdleWhitelist
: mDeviceIdleWhitelist;
return Arrays.binarySearch(whitelist, appId) >= 0
|| Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0
|| mPendingTempWhitelist.indexOfKey(uid) >= 0;
}
private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) {
ProviderInfo pi = null;
ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userHandle);
if (cpr != null) {
pi = cpr.info;
} else {
try {
pi = AppGlobals.getPackageManager().resolveContentProvider(
authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags,
userHandle);
} catch (RemoteException ex) {
}
}
return pi;
}
void grantEphemeralAccessLocked(int userId, Intent intent,
int targetAppId, int ephemeralAppId) {
getPackageManagerInternalLocked().
grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
}
@GuardedBy("this")
private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
final ArrayMap targetUris = mGrantedUriPermissions.get(targetUid);
if (targetUris != null) {
return targetUris.get(grantUri);
}
return null;
}
@GuardedBy("this")
private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,
String targetPkg, int targetUid, GrantUri grantUri) {
ArrayMap targetUris = mGrantedUriPermissions.get(targetUid);
if (targetUris == null) {
targetUris = Maps.newArrayMap();
mGrantedUriPermissions.put(targetUid, targetUris);
}
UriPermission perm = targetUris.get(grantUri);
if (perm == null) {
perm = new UriPermission(sourcePkg, targetPkg, targetUid, grantUri);
targetUris.put(grantUri, perm);
}
return perm;
}
@GuardedBy("this")
private final boolean checkUriPermissionLocked(GrantUri grantUri, int uid,
final int modeFlags) {
final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE
: UriPermission.STRENGTH_OWNED;
// Root gets to do everything.
if (uid == 0) {
return true;
}
final ArrayMap perms = mGrantedUriPermissions.get(uid);
if (perms == null) return false;
// First look for exact match
final UriPermission exactPerm = perms.get(grantUri);
if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) {
return true;
}
// No exact match, look for prefixes
final int N = perms.size();
for (int i = 0; i < N; i++) {
final UriPermission perm = perms.valueAt(i);
if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri)
&& perm.getStrength(modeFlags) >= minStrength) {
return true;
}
}
return false;
}
/**
* @param uri This uri must NOT contain an embedded userId.
* @param userId The userId in which the uri is to be resolved.
*/
@Override
public int checkUriPermission(Uri uri, int pid, int uid,
final int modeFlags, int userId, IBinder callerToken) {
enforceNotIsolatedCaller("checkUriPermission");
// Another redirected-binder-call permissions check as in
// {@link checkPermissionWithToken}.
Identity tlsIdentity = sCallerIdentity.get();
if (tlsIdentity != null && tlsIdentity.token == callerToken) {
uid = tlsIdentity.uid;
pid = tlsIdentity.pid;
}
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
synchronized (this) {
return checkUriPermissionLocked(new GrantUri(userId, uri, false), uid, modeFlags)
? PackageManager.PERMISSION_GRANTED
: PackageManager.PERMISSION_DENIED;
}
}
/**
* Check if the targetPkg can be granted permission to access uri by
* the callingUid using the given modeFlags. Throws a security exception
* if callingUid is not allowed to do this. Returns the uid of the target
* if the URI permission grant should be performed; returns -1 if it is not
* needed (for example targetPkg already has permission to access the URI).
* If you already know the uid of the target, you can supply it in
* lastTargetUid else set that to -1.
*/
@GuardedBy("this")
int checkGrantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
final int modeFlags, int lastTargetUid) {
if (!Intent.isAccessUriMode(modeFlags)) {
return -1;
}
if (targetPkg != null) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Checking grant " + targetPkg + " permission to " + grantUri);
}
final IPackageManager pm = AppGlobals.getPackageManager();
// If this is not a content: uri, we can't do anything with it.
if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Can't grant URI permission for non-content URI: " + grantUri);
return -1;
}
// Bail early if system is trying to hand out permissions directly; it
// must always grant permissions on behalf of someone explicit.
final int callingAppId = UserHandle.getAppId(callingUid);
if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
// Exempted authority for
// 1. cropping user photos and sharing a generated license html
// file in Settings app
// 2. sharing a generated license html file in TvSettings app
} else {
Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
+ " grant to " + grantUri + "; use startActivityAsCaller() instead");
return -1;
}
}
final String authority = grantUri.uri.getAuthority();
final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId,
MATCH_DEBUG_TRIAGED_MISSING);
if (pi == null) {
Slog.w(TAG, "No content provider found for permission check: " +
grantUri.uri.toSafeString());
return -1;
}
int targetUid = lastTargetUid;
if (targetUid < 0 && targetPkg != null) {
try {
targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.getUserId(callingUid));
if (targetUid < 0) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Can't grant URI permission no uid for: " + targetPkg);
return -1;
}
} catch (RemoteException ex) {
return -1;
}
}
// If we're extending a persistable grant, then we always need to create
// the grant data structure so that take/release APIs work
if ((modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0) {
return targetUid;
}
if (targetUid >= 0) {
// First... does the target actually need this permission?
if (checkHoldingPermissionsLocked(pm, pi, grantUri, targetUid, modeFlags)) {
// No need to grant the target this permission.
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Target " + targetPkg + " already has full permission to " + grantUri);
return -1;
}
} else {
// First... there is no target package, so can anyone access it?
boolean allowed = pi.exported;
if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
if (pi.readPermission != null) {
allowed = false;
}
}
if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
if (pi.writePermission != null) {
allowed = false;
}
}
if (pi.pathPermissions != null) {
final int N = pi.pathPermissions.length;
for (int i=0; i {
final String targetPkg;
final int targetUid;
final int flags;
NeededUriGrants(String targetPkg, int targetUid, int flags) {
this.targetPkg = targetPkg;
this.targetUid = targetUid;
this.flags = flags;
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
proto.write(NeededUriGrantsProto.FLAGS, flags);
final int N = this.size();
for (int i=0; i 0) {
if (needed == null) {
needed = new NeededUriGrants(targetPkg, targetUid, mode);
}
needed.add(grantUri);
}
}
if (clip != null) {
for (int i=0; i 0) {
if (needed == null) {
needed = new NeededUriGrants(targetPkg, targetUid, mode);
}
needed.add(grantUri);
}
} else {
Intent clipIntent = clip.getItemAt(i).getIntent();
if (clipIntent != null) {
NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentLocked(
callingUid, targetPkg, clipIntent, mode, needed, targetUserId);
if (newNeeded != null) {
needed = newNeeded;
}
}
}
}
}
return needed;
}
/**
* Like grantUriPermissionUncheckedLocked, but takes an Intent.
*/
@GuardedBy("this")
void grantUriPermissionUncheckedFromIntentLocked(NeededUriGrants needed,
UriPermissionOwner owner) {
if (needed != null) {
for (int i=0; i perms = mGrantedUriPermissions.get(
perm.targetUid);
if (perms != null) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Removing " + perm.targetUid + " permission to " + perm.uri);
perms.remove(perm.uri);
if (perms.isEmpty()) {
mGrantedUriPermissions.remove(perm.targetUid);
}
}
}
}
@GuardedBy("this")
private void revokeUriPermissionLocked(String targetPackage, int callingUid, GrantUri grantUri,
final int modeFlags) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Revoking all granted permissions to " + grantUri);
final IPackageManager pm = AppGlobals.getPackageManager();
final String authority = grantUri.uri.getAuthority();
final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
if (pi == null) {
Slog.w(TAG, "No content provider found for permission revoke: "
+ grantUri.toSafeString());
return;
}
// Does the caller have this permission on the URI?
if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
// If they don't have direct access to the URI, then revoke any
// ownerless URI permissions that have been granted to them.
final ArrayMap perms = mGrantedUriPermissions.get(callingUid);
if (perms != null) {
boolean persistChanged = false;
for (int i = perms.size()-1; i >= 0; i--) {
final UriPermission perm = perms.valueAt(i);
if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
continue;
}
if (perm.uri.sourceUserId == grantUri.sourceUserId
&& perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Revoking non-owned " + perm.targetUid
+ " permission to " + perm.uri);
persistChanged |= perm.revokeModes(
modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
if (perm.modeFlags == 0) {
perms.removeAt(i);
}
}
}
if (perms.isEmpty()) {
mGrantedUriPermissions.remove(callingUid);
}
if (persistChanged) {
schedulePersistUriGrants();
}
}
return;
}
boolean persistChanged = false;
// Go through all of the permissions and remove any that match.
for (int i = mGrantedUriPermissions.size()-1; i >= 0; i--) {
final int targetUid = mGrantedUriPermissions.keyAt(i);
final ArrayMap perms = mGrantedUriPermissions.valueAt(i);
for (int j = perms.size()-1; j >= 0; j--) {
final UriPermission perm = perms.valueAt(j);
if (targetPackage != null && !targetPackage.equals(perm.targetPkg)) {
continue;
}
if (perm.uri.sourceUserId == grantUri.sourceUserId
&& perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Revoking " + perm.targetUid + " permission to " + perm.uri);
persistChanged |= perm.revokeModes(
modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
targetPackage == null);
if (perm.modeFlags == 0) {
perms.removeAt(j);
}
}
}
if (perms.isEmpty()) {
mGrantedUriPermissions.removeAt(i);
}
}
if (persistChanged) {
schedulePersistUriGrants();
}
}
/**
* @param uri This uri must NOT contain an embedded userId.
* @param userId The userId in which the uri is to be resolved.
*/
@Override
public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri,
final int modeFlags, int userId) {
enforceNotIsolatedCaller("revokeUriPermission");
synchronized(this) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller "
+ caller
+ " when revoking permission to uri " + uri);
}
if (uri == null) {
Slog.w(TAG, "revokeUriPermission: null uri");
return;
}
if (!Intent.isAccessUriMode(modeFlags)) {
return;
}
final String authority = uri.getAuthority();
final ProviderInfo pi = getProviderInfoLocked(authority, userId,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
if (pi == null) {
Slog.w(TAG, "No content provider found for permission revoke: "
+ uri.toSafeString());
return;
}
revokeUriPermissionLocked(targetPackage, r.uid, new GrantUri(userId, uri, false),
modeFlags);
}
}
/**
* Remove any {@link UriPermission} granted from or to the
* given package.
*
* @param packageName Package name to match, or {@code null} to apply to all
* packages.
* @param userHandle User to match, or {@link UserHandle#USER_ALL} to apply
* to all users.
* @param persistable If persistable grants should be removed.
* @param targetOnly When {@code true}, only remove permissions where the app is the target,
* not source.
*/
@GuardedBy("this")
private void removeUriPermissionsForPackageLocked(
String packageName, int userHandle, boolean persistable, boolean targetOnly) {
if (userHandle == UserHandle.USER_ALL && packageName == null) {
throw new IllegalArgumentException("Must narrow by either package or user");
}
boolean persistChanged = false;
int N = mGrantedUriPermissions.size();
for (int i = 0; i < N; i++) {
final int targetUid = mGrantedUriPermissions.keyAt(i);
final ArrayMap perms = mGrantedUriPermissions.valueAt(i);
// Only inspect grants matching user
if (userHandle == UserHandle.USER_ALL
|| userHandle == UserHandle.getUserId(targetUid)) {
for (Iterator it = perms.values().iterator(); it.hasNext();) {
final UriPermission perm = it.next();
// Only inspect grants matching package
if (packageName == null || (!targetOnly && perm.sourcePkg.equals(packageName))
|| perm.targetPkg.equals(packageName)) {
// Hacky solution as part of fixing a security bug; ignore
// grants associated with DownloadManager so we don't have
// to immediately launch it to regrant the permissions
if (Downloads.Impl.AUTHORITY.equals(perm.uri.uri.getAuthority())
&& !persistable) continue;
persistChanged |= perm.revokeModes(persistable
? ~0 : ~Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
// Only remove when no modes remain; any persisted grants
// will keep this alive.
if (perm.modeFlags == 0) {
it.remove();
}
}
}
if (perms.isEmpty()) {
mGrantedUriPermissions.remove(targetUid);
N--;
i--;
}
}
}
if (persistChanged) {
schedulePersistUriGrants();
}
}
@Override
public IBinder newUriPermissionOwner(String name) {
enforceNotIsolatedCaller("newUriPermissionOwner");
synchronized(this) {
UriPermissionOwner owner = new UriPermissionOwner(this, name);
return owner.getExternalTokenLocked();
}
}
@Override
public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
synchronized(this) {
ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
if (r == null) {
throw new IllegalArgumentException("Activity does not exist; token="
+ activityToken);
}
return r.getUriPermissionsLocked().getExternalTokenLocked();
}
}
/**
* @param uri This uri must NOT contain an embedded userId.
* @param sourceUserId The userId in which the uri is to be resolved.
* @param targetUserId The userId of the app that receives the grant.
*/
@Override
public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, Uri uri,
final int modeFlags, int sourceUserId, int targetUserId) {
targetUserId = mUserController.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), targetUserId, false, ALLOW_FULL_ONLY,
"grantUriPermissionFromOwner", null);
synchronized(this) {
UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
if (owner == null) {
throw new IllegalArgumentException("Unknown owner: " + token);
}
if (fromUid != Binder.getCallingUid()) {
if (Binder.getCallingUid() != myUid()) {
// Only system code can grant URI permissions on behalf
// of other users.
throw new SecurityException("nice try");
}
}
if (targetPkg == null) {
throw new IllegalArgumentException("null target");
}
if (uri == null) {
throw new IllegalArgumentException("null uri");
}
grantUriPermissionLocked(fromUid, targetPkg, new GrantUri(sourceUserId, uri, false),
modeFlags, owner, targetUserId);
}
}
/**
* @param uri This uri must NOT contain an embedded userId.
* @param userId The userId in which the uri is to be resolved.
*/
@Override
public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode, int userId) {
synchronized(this) {
UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token);
if (owner == null) {
throw new IllegalArgumentException("Unknown owner: " + token);
}
if (uri == null) {
owner.removeUriPermissionsLocked(mode);
} else {
final boolean prefix = (mode & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0;
owner.removeUriPermissionLocked(new GrantUri(userId, uri, prefix), mode);
}
}
}
private void schedulePersistUriGrants() {
if (!mHandler.hasMessages(PERSIST_URI_GRANTS_MSG)) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG),
10 * DateUtils.SECOND_IN_MILLIS);
}
}
private void writeGrantedUriPermissions() {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "writeGrantedUriPermissions()");
final long startTime = SystemClock.uptimeMillis();
// Snapshot permissions so we can persist without lock
ArrayList persist = Lists.newArrayList();
synchronized (this) {
final int size = mGrantedUriPermissions.size();
for (int i = 0; i < size; i++) {
final ArrayMap perms = mGrantedUriPermissions.valueAt(i);
for (UriPermission perm : perms.values()) {
if (perm.persistedModeFlags != 0) {
persist.add(perm.snapshot());
}
}
}
}
FileOutputStream fos = null;
try {
fos = mGrantFile.startWrite(startTime);
XmlSerializer out = new FastXmlSerializer();
out.setOutput(fos, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, TAG_URI_GRANTS);
for (UriPermission.Snapshot perm : persist) {
out.startTag(null, TAG_URI_GRANT);
writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
out.endTag(null, TAG_URI_GRANT);
}
out.endTag(null, TAG_URI_GRANTS);
out.endDocument();
mGrantFile.finishWrite(fos);
} catch (IOException e) {
if (fos != null) {
mGrantFile.failWrite(fos);
}
}
}
@GuardedBy("this")
private void readGrantedUriPermissionsLocked() {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "readGrantedUriPermissions()");
final long now = System.currentTimeMillis();
FileInputStream fis = null;
try {
fis = mGrantFile.openRead();
final XmlPullParser in = Xml.newPullParser();
in.setInput(fis, StandardCharsets.UTF_8.name());
int type;
while ((type = in.next()) != END_DOCUMENT) {
final String tag = in.getName();
if (type == START_TAG) {
if (TAG_URI_GRANT.equals(tag)) {
final int sourceUserId;
final int targetUserId;
final int userHandle = readIntAttribute(in,
ATTR_USER_HANDLE, UserHandle.USER_NULL);
if (userHandle != UserHandle.USER_NULL) {
// For backwards compatibility.
sourceUserId = userHandle;
targetUserId = userHandle;
} else {
sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
}
final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX);
final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
// Sanity check that provider still belongs to source package
// Both direct boot aware and unaware packages are fine as we
// will do filtering at query time to avoid multiple parsing.
final ProviderInfo pi = getProviderInfoLocked(
uri.getAuthority(), sourceUserId, MATCH_DIRECT_BOOT_AWARE
| MATCH_DIRECT_BOOT_UNAWARE);
if (pi != null && sourcePkg.equals(pi.packageName)) {
int targetUid = -1;
try {
targetUid = AppGlobals.getPackageManager().getPackageUid(
targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
} catch (RemoteException e) {
}
if (targetUid != -1) {
final UriPermission perm = findOrCreateUriPermissionLocked(
sourcePkg, targetPkg, targetUid,
new GrantUri(sourceUserId, uri, prefix));
perm.initPersistedModes(modeFlags, createdTime);
}
} else {
Slog.w(TAG, "Persisted grant for " + uri + " had source " + sourcePkg
+ " but instead found " + pi);
}
}
}
}
} catch (FileNotFoundException e) {
// Missing grants is okay
} catch (IOException e) {
Slog.wtf(TAG, "Failed reading Uri grants", e);
} catch (XmlPullParserException e) {
Slog.wtf(TAG, "Failed reading Uri grants", e);
} finally {
IoUtils.closeQuietly(fis);
}
}
/**
* @param uri This uri must NOT contain an embedded userId.
* @param toPackage Name of package whose uri is being granted to (if {@code null}, uses
* calling uid)
* @param userId The userId in which the uri is to be resolved.
*/
@Override
public void takePersistableUriPermission(Uri uri, final int modeFlags,
@Nullable String toPackage, int userId) {
final int uid;
if (toPackage != null) {
enforceCallingPermission(android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS,
"takePersistableUriPermission");
uid = mPackageManagerInt.getPackageUid(toPackage, 0, userId);
} else {
enforceNotIsolatedCaller("takePersistableUriPermission");
uid = Binder.getCallingUid();
}
Preconditions.checkFlagsArgument(modeFlags,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
synchronized (this) {
boolean persistChanged = false;
GrantUri grantUri = new GrantUri(userId, uri, false);
UriPermission exactPerm = findUriPermissionLocked(uid, grantUri);
UriPermission prefixPerm = findUriPermissionLocked(uid,
new GrantUri(userId, uri, true));
final boolean exactValid = (exactPerm != null)
&& ((modeFlags & exactPerm.persistableModeFlags) == modeFlags);
final boolean prefixValid = (prefixPerm != null)
&& ((modeFlags & prefixPerm.persistableModeFlags) == modeFlags);
if (!(exactValid || prefixValid)) {
throw new SecurityException("No persistable permission grants found for UID "
+ uid + " and Uri " + grantUri.toSafeString());
}
if (exactValid) {
persistChanged |= exactPerm.takePersistableModes(modeFlags);
}
if (prefixValid) {
persistChanged |= prefixPerm.takePersistableModes(modeFlags);
}
persistChanged |= maybePrunePersistedUriGrantsLocked(uid);
if (persistChanged) {
schedulePersistUriGrants();
}
}
}
/**
* @param uri This uri must NOT contain an embedded userId.
* @param toPackage Name of the target package whose uri is being released (if {@code null},
* uses calling uid)
* @param userId The userId in which the uri is to be resolved.
*/
@Override
public void releasePersistableUriPermission(Uri uri, final int modeFlags,
@Nullable String toPackage, int userId) {
final int uid;
if (toPackage != null) {
enforceCallingPermission(android.Manifest.permission.FORCE_PERSISTABLE_URI_PERMISSIONS,
"releasePersistableUriPermission");
uid = mPackageManagerInt.getPackageUid(toPackage, 0, userId);
} else {
enforceNotIsolatedCaller("releasePersistableUriPermission");
uid = Binder.getCallingUid();
}
Preconditions.checkFlagsArgument(modeFlags,
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
synchronized (this) {
boolean persistChanged = false;
UriPermission exactPerm = findUriPermissionLocked(uid,
new GrantUri(userId, uri, false));
UriPermission prefixPerm = findUriPermissionLocked(uid,
new GrantUri(userId, uri, true));
if (exactPerm == null && prefixPerm == null && toPackage == null) {
throw new SecurityException("No permission grants found for UID " + uid
+ " and Uri " + uri.toSafeString());
}
if (exactPerm != null) {
persistChanged |= exactPerm.releasePersistableModes(modeFlags);
removeUriPermissionIfNeededLocked(exactPerm);
}
if (prefixPerm != null) {
persistChanged |= prefixPerm.releasePersistableModes(modeFlags);
removeUriPermissionIfNeededLocked(prefixPerm);
}
if (persistChanged) {
schedulePersistUriGrants();
}
}
}
/**
* Prune any older {@link UriPermission} for the given UID until outstanding
* persisted grants are below {@link #MAX_PERSISTED_URI_GRANTS}.
*
* @return if any mutations occured that require persisting.
*/
@GuardedBy("this")
private boolean maybePrunePersistedUriGrantsLocked(int uid) {
final ArrayMap perms = mGrantedUriPermissions.get(uid);
if (perms == null) return false;
if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false;
final ArrayList persisted = Lists.newArrayList();
for (UriPermission perm : perms.values()) {
if (perm.persistedModeFlags != 0) {
persisted.add(perm);
}
}
final int trimCount = persisted.size() - MAX_PERSISTED_URI_GRANTS;
if (trimCount <= 0) return false;
Collections.sort(persisted, new UriPermission.PersistedTimeComparator());
for (int i = 0; i < trimCount; i++) {
final UriPermission perm = persisted.get(i);
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
"Trimming grant created at " + perm.persistedCreateTime);
perm.releasePersistableModes(~0);
removeUriPermissionIfNeededLocked(perm);
}
return true;
}
@Override
public ParceledListSlice getPersistedUriPermissions(
String packageName, boolean incoming) {
enforceNotIsolatedCaller("getPersistedUriPermissions");
Preconditions.checkNotNull(packageName, "packageName");
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final IPackageManager pm = AppGlobals.getPackageManager();
try {
final int packageUid = pm.getPackageUid(packageName,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUserId);
if (packageUid != callingUid) {
throw new SecurityException(
"Package " + packageName + " does not belong to calling UID " + callingUid);
}
} catch (RemoteException e) {
throw new SecurityException("Failed to verify package name ownership");
}
final ArrayList result = Lists.newArrayList();
synchronized (this) {
if (incoming) {
final ArrayMap perms = mGrantedUriPermissions.get(
callingUid);
if (perms == null) {
Slog.w(TAG, "No permission grants found for " + packageName);
} else {
for (int j = 0; j < perms.size(); j++) {
final UriPermission perm = perms.valueAt(j);
if (packageName.equals(perm.targetPkg) && perm.persistedModeFlags != 0) {
result.add(perm.buildPersistedPublicApiObject());
}
}
}
} else {
final int size = mGrantedUriPermissions.size();
for (int i = 0; i < size; i++) {
final ArrayMap perms =
mGrantedUriPermissions.valueAt(i);
for (int j = 0; j < perms.size(); j++) {
final UriPermission perm = perms.valueAt(j);
if (packageName.equals(perm.sourcePkg) && perm.persistedModeFlags != 0) {
result.add(perm.buildPersistedPublicApiObject());
}
}
}
}
}
return new ParceledListSlice(result);
}
@Override
public ParceledListSlice getGrantedUriPermissions(
@Nullable String packageName, int userId) {
enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS,
"getGrantedUriPermissions");
final List result = new ArrayList<>();
synchronized (this) {
final int size = mGrantedUriPermissions.size();
for (int i = 0; i < size; i++) {
final ArrayMap perms = mGrantedUriPermissions.valueAt(i);
for (int j = 0; j < perms.size(); j++) {
final UriPermission perm = perms.valueAt(j);
if ((packageName == null || packageName.equals(perm.targetPkg))
&& perm.targetUserId == userId
&& perm.persistedModeFlags != 0) {
result.add(perm.buildGrantedUriPermission());
}
}
}
}
return new ParceledListSlice<>(result);
}
@Override
public void clearGrantedUriPermissions(String packageName, int userId) {
enforceCallingPermission(android.Manifest.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS,
"clearGrantedUriPermissions");
synchronized(this) {
removeUriPermissionsForPackageLocked(packageName, userId, true, true);
}
}
@Override
public void showWaitingForDebugger(IApplicationThread who, boolean waiting) {
synchronized (this) {
ProcessRecord app =
who != null ? getRecordForAppLocked(who) : null;
if (app == null) return;
Message msg = Message.obtain();
msg.what = WAIT_FOR_DEBUGGER_UI_MSG;
msg.obj = app;
msg.arg1 = waiting ? 1 : 0;
mUiHandler.sendMessage(msg);
}
}
@Override
public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
outInfo.availMem = getFreeMemory();
outInfo.totalMem = getTotalMemory();
outInfo.threshold = homeAppMem;
outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
outInfo.hiddenAppThreshold = cachedAppMem;
outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
ProcessList.SERVICE_ADJ);
outInfo.visibleAppThreshold = mProcessList.getMemLevel(
ProcessList.VISIBLE_APP_ADJ);
outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
ProcessList.FOREGROUND_APP_ADJ);
}
// =========================================================
// TASK MANAGEMENT
// =========================================================
@Override
public List getAppTasks(String callingPackage) {
int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
synchronized(this) {
return mRecentTasks.getAppTasksList(callingUid, callingPackage);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public List getTasks(int maxNum) {
return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
}
@Override
public List getFilteredTasks(int maxNum, @ActivityType int ignoreActivityType,
@WindowingMode int ignoreWindowingMode) {
final int callingUid = Binder.getCallingUid();
ArrayList list = new ArrayList<>();
synchronized(this) {
if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
callingUid);
mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
ignoreWindowingMode, callingUid, allowed);
}
return list;
}
private boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) {
if (mRecentTasks.isCallerRecents(callingUid)) {
// Always allow the recents component to get tasks
return true;
}
boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS,
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
if (!allowed) {
if (checkPermission(android.Manifest.permission.GET_TASKS,
callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
// Temporary compatibility: some existing apps on the system image may
// still be requesting the old permission and not switched to the new
// one; if so, we'll still allow them full access. This means we need
// to see if they are holding the old permission and are a system app.
try {
if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) {
allowed = true;
if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+ " is using old GET_TASKS but privileged; allowing");
}
} catch (RemoteException e) {
}
}
}
if (!allowed) {
if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid
+ " does not hold REAL_GET_TASKS; limiting output");
}
return allowed;
}
@Override
public ParceledListSlice getRecentTasks(int maxNum, int flags,
int userId) {
final int callingUid = Binder.getCallingUid();
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
false, ALLOW_FULL_ONLY, "getRecentTasks", null);
final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
callingUid);
final boolean detailed = checkCallingPermission(
android.Manifest.permission.GET_DETAILED_TASKS)
== PackageManager.PERMISSION_GRANTED;
synchronized (this) {
return mRecentTasks.getRecentTasks(maxNum, flags, allowed, detailed, userId,
callingUid);
}
}
@Override
public ActivityManager.TaskDescription getTaskDescription(int id) {
synchronized (this) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (tr != null) {
return tr.lastTaskDescription;
}
}
return null;
}
@Override
public int addAppTask(IBinder activityToken, Intent intent,
ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException {
final int callingUid = Binder.getCallingUid();
final long callingIdent = Binder.clearCallingIdentity();
try {
synchronized (this) {
ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
if (r == null) {
throw new IllegalArgumentException("Activity does not exist; token="
+ activityToken);
}
ComponentName comp = intent.getComponent();
if (comp == null) {
throw new IllegalArgumentException("Intent " + intent
+ " must specify explicit component");
}
if (thumbnail.getWidth() != mThumbnailWidth
|| thumbnail.getHeight() != mThumbnailHeight) {
throw new IllegalArgumentException("Bad thumbnail size: got "
+ thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require "
+ mThumbnailWidth + "x" + mThumbnailHeight);
}
if (intent.getSelector() != null) {
intent.setSelector(null);
}
if (intent.getSourceBounds() != null) {
intent.setSourceBounds(null);
}
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) {
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) == 0) {
// The caller has added this as an auto-remove task... that makes no
// sense, so turn off auto-remove.
intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
}
}
final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp,
STOCK_PM_FLAGS, UserHandle.getUserId(callingUid));
if (ainfo.applicationInfo.uid != callingUid) {
throw new SecurityException(
"Can't add task for another application: target uid="
+ ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
}
final ActivityStack stack = r.getStack();
final TaskRecord task = stack.createTaskRecord(
mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
return INVALID_TASK_ID;
}
task.lastTaskDescription.copyFrom(description);
// TODO: Send the thumbnail to WM to store it.
return task.taskId;
}
} finally {
Binder.restoreCallingIdentity(callingIdent);
}
}
@Override
public Point getAppTaskThumbnailSize() {
synchronized (this) {
return new Point(mThumbnailWidth, mThumbnailHeight);
}
}
@Override
public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
synchronized (this) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
r.setTaskDescription(td);
final TaskRecord task = r.getTask();
task.updateTaskDescription();
mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
}
}
}
@Override
public void setTaskResizeable(int taskId, int resizeableMode) {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
return;
}
task.setResizeMode(resizeableMode);
}
}
@Override
public void resizeTask(int taskId, Rect bounds, int resizeMode) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
// Place the task in the right stack if it isn't there already based on
// the requested bounds.
// The stack transition logic is:
// - a null bounds on a freeform task moves that task to fullscreen
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
ActivityStack stack = task.getStack();
if (!task.getWindowConfiguration().canResizeTask()) {
throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
stack = stack.getDisplay().getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
} else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
stack = stack.getDisplay().getOrCreateStack(
WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
}
// Reparent the task to the right stack if necessary
boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
if (stack != task.getStack()) {
// Defer resume until the task is resized below
task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
DEFER_RESUME, "resizeTask");
preserveWindow = false;
}
// After reparenting (which only resizes the task to the stack bounds), resize the
// task to the actual bounds provided
task.resize(bounds, resizeMode, preserveWindow, !DEFER_RESUME);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public Rect getTaskBounds(int taskId) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
long ident = Binder.clearCallingIdentity();
Rect rect = new Rect();
try {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
return rect;
}
if (task.getStack() != null) {
// Return the bounds from window manager since it will be adjusted for various
// things like the presense of a docked stack for tasks that aren't resizeable.
task.getWindowContainerBounds(rect);
} else {
// Task isn't in window manager yet since it isn't associated with a stack.
// Return the persist value from activity manager
if (!task.matchParentBounds()) {
rect.set(task.getBounds());
} else if (task.mLastNonFullscreenBounds != null) {
rect.set(task.mLastNonFullscreenBounds);
}
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
return rect;
}
@Override
public void cancelTaskWindowTransition(int taskId) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"cancelTaskWindowTransition()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
return;
}
task.cancelWindowTransition();
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task;
synchronized (this) {
task = mStackSupervisor.anyTaskForIdLocked(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
return null;
}
}
// Don't call this while holding the lock as this operation might hit the disk.
return task.getSnapshot(reducedResolution);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null);
final File passedIconFile = new File(filePath);
final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
passedIconFile.getName());
if (!legitIconFile.getPath().equals(filePath)
|| !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
throw new IllegalArgumentException("Bad file path: " + filePath
+ " passed for userId " + userId);
}
return mRecentTasks.getTaskDescriptionIcon(filePath);
}
@Override
public void startInPlaceAnimationOnFrontMostApplication(Bundle opts)
throws RemoteException {
final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
final ActivityOptions activityOptions = safeOptions != null
? safeOptions.getOptions(mStackSupervisor)
: null;
if (activityOptions == null
|| activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
|| activityOptions.getCustomInPlaceResId() == 0) {
throw new IllegalArgumentException("Expected in-place ActivityOption " +
"with valid animation");
}
mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
activityOptions.getCustomInPlaceResId());
mWindowManager.executeAppTransition();
}
@Override
public void removeStack(int stackId) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
final ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "removeStack: No stack with id=" + stackId);
return;
}
if (!stack.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException(
"Removing non-standard stack is not allowed.");
}
mStackSupervisor.removeStack(stack);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
/**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
@Override
public void removeStacksInWindowingModes(int[] windowingModes) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"removeStacksInWindowingModes()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
mStackSupervisor.removeStacksInWindowingModes(windowingModes);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void removeStacksWithActivityTypes(int[] activityTypes) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"removeStacksWithActivityTypes()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void moveStackToDisplay(int stackId, int displayId) {
enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
+ " to displayId=" + displayId);
mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public boolean removeTask(int taskId) {
enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS,
"remove-task");
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
/**
* TODO: Add mController hook
*/
@Override
public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
synchronized(this) {
moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions),
false /* fromRecents */);
}
}
void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
boolean fromRecents) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), -1, -1, "Task to front")) {
SafeActivityOptions.abort(options);
return;
}
final long origId = Binder.clearCallingIdentity();
try {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
Slog.d(TAG, "Could not find task for id: "+ taskId);
return;
}
if (mLockTaskController.isLockTaskModeViolation(task)) {
Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
return;
}
ActivityOptions realOptions = options != null
? options.getOptions(mStackSupervisor)
: null;
mStackSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
false /* forceNonResizable */);
final ActivityRecord topActivity = task.getTopActivity();
if (topActivity != null) {
// We are reshowing a task, use a starting window to hide the initial draw delay
// so the transition can start earlier.
topActivity.showStartingWindow(null /* prev */, false /* newTask */,
true /* taskSwitch */, fromRecents);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
SafeActivityOptions.abort(options);
}
/**
* Attempts to move a task backwards in z-order (the order of activities within the task is
* unchanged).
*
* There are several possible results of this call:
* - if the task is locked, then we will show the lock toast
* - if there is a task behind the provided task, then that task is made visible and resumed as
* this task is moved to the back
* - otherwise, if there are no other tasks in the stack:
* - if this task is in the pinned stack, then we remove the stack completely, which will
* have the effect of moving the task to the top or bottom of the fullscreen stack
* (depending on whether it is visible)
* - otherwise, we simply return home and hide this task
*
* @param token A reference to the activity we wish to move
* @param nonRoot If false then this only works if the activity is the root
* of a task; if true it will work for any activity in a task.
* @return Returns true if the move completed, false if not.
*/
@Override
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
enforceNotIsolatedCaller("moveActivityTaskToBack");
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
try {
int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task != null) {
return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
return false;
}
@Override
public void moveTaskBackwards(int task) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskBackwards()");
synchronized(this) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), -1, -1, "Task backwards")) {
return;
}
final long origId = Binder.clearCallingIdentity();
moveTaskBackwardsLocked(task);
Binder.restoreCallingIdentity(origId);
}
}
private final void moveTaskBackwardsLocked(int task) {
Slog.e(TAG, "moveTaskBackwards not yet implemented!");
}
@Override
public int createStackOnDisplay(int displayId) throws RemoteException {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
synchronized (this) {
final ActivityDisplay display =
mStackSupervisor.getActivityDisplayOrCreateLocked(displayId);
if (display == null) {
return INVALID_STACK_ID;
}
// TODO(multi-display): Have the caller pass in the windowing mode and activity type.
final ActivityStack stack = display.createStack(
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
ON_TOP);
return (stack != null) ? stack.mStackId : INVALID_STACK_ID;
}
}
@Override
public int getActivityDisplayId(IBinder activityToken) throws RemoteException {
synchronized (this) {
final ActivityStack stack = ActivityRecord.getStackLocked(activityToken);
if (stack != null && stack.mDisplayId != INVALID_DISPLAY) {
return stack.mDisplayId;
}
return DEFAULT_DISPLAY;
}
}
@Override
public void exitFreeformMode(IBinder token) throws RemoteException {
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
throw new IllegalArgumentException(
"exitFreeformMode: No activity record matching token=" + token);
}
final ActivityStack stack = r.getStack();
if (stack == null || !stack.inFreeformWindowingMode()) {
throw new IllegalStateException(
"exitFreeformMode: You can only go fullscreen from freeform.");
}
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
toTop, ANIMATE, null /* initialBounds */, true /* showRecents */);
return;
}
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
return;
}
if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+ " to windowingMode=" + windowingMode + " toTop=" + toTop);
if (!task.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ " non-standard task " + taskId + " to windowing mode="
+ windowingMode);
}
final ActivityStack stack = task.getStack();
if (toTop) {
stack.moveToFront("setTaskWindowingMode", task);
}
stack.setWindowingMode(windowingMode);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
/**
* Moves the specified task to the primary-split-screen stack.
*
* @param taskId Id of task to move.
* @param createMode The mode the primary split screen stack should be created in if it doesn't
* exist already. See
* {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
* and
* {@link android.app.ActivityManager#SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
* @param toTop If the task and stack should be moved to the top.
* @param animate Whether we should play an animation for the moving the task.
* @param initialBounds If the primary stack gets created, it will use these bounds for the
* stack. Pass {@code null} to use default bounds.
* @param showRecents If the recents activity should be shown on the other side of the task
* going into split-screen mode.
*/
@Override
public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
boolean animate, Rect initialBounds, boolean showRecents) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"setTaskWindowingModeSplitScreenPrimary()");
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
return false;
}
if (DEBUG_STACK) Slog.d(TAG_STACK,
"setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
if (!task.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ " non-standard task " + taskId + " to split-screen windowing mode");
}
mWindowManager.setDockedStackCreateState(createMode, initialBounds);
final int windowingMode = task.getWindowingMode();
final ActivityStack stack = task.getStack();
if (toTop) {
stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
}
stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */);
return windowingMode != task.getWindowingMode();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
return;
}
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
final ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack == null) {
throw new IllegalStateException(
"moveTaskToStack: No stack for stackId=" + stackId);
}
if (!stack.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
+ taskId + " to stack " + stackId);
}
if (stack.inSplitScreenPrimaryWindowingMode()) {
mWindowManager.setDockedStackCreateState(
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
}
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
"moveTaskToStack");
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
/**
* Dismisses split-screen multi-window mode.
* @param toTop If true the current primary split-screen stack will be placed or left on top.
*/
@Override
public void dismissSplitScreenMode(boolean toTop) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
final ActivityStack stack =
mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
if (stack == null) {
Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
return;
}
if (toTop) {
// Caller wants the current split-screen primary stack to be the top stack after
// it goes fullscreen, so move it to the front.
stack.moveToFront("dismissSplitScreenMode");
} else if (mStackSupervisor.isFocusedStack(stack)) {
// In this case the current split-screen primary stack shouldn't be the top
// stack after it goes fullscreen, but it current has focus, so we move the
// focus to the top-most split-screen secondary stack next to it.
final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
if (otherStack != null) {
otherStack.moveToFront("dismissSplitScreenMode_other");
}
}
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Dismisses Pip
* @param animate True if the dismissal should be animated.
* @param animationDuration The duration of the resize animation in milliseconds or -1 if the
* default animation duration should be used.
*/
@Override
public void dismissPip(boolean animate, int animationDuration) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
final PinnedActivityStack stack =
mStackSupervisor.getDefaultDisplay().getPinnedStack();
if (stack == null) {
Slog.w(TAG, "dismissPip: pinned stack not found.");
return;
}
if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
throw new IllegalArgumentException("Stack: " + stack
+ " doesn't support animated resize.");
}
if (animate) {
stack.animateResizePinnedStack(null /* sourceHintBounds */,
null /* destBounds */, animationDuration, false /* fromFullscreen */);
} else {
mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Moves the top activity in the input stackId to the pinned stack.
*
* @param stackId Id of stack to move the top activity to pinned stack.
* @param bounds Bounds to use for pinned stack.
*
* @return True if the top activity of the input stack was successfully moved to the pinned
* stack.
*/
@Override
public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
"moveTopActivityToPinnedStack()");
synchronized (this) {
if (!mSupportsPictureInPicture) {
throw new IllegalStateException("moveTopActivityToPinnedStack:"
+ "Device doesn't support picture-in-picture mode");
}
long ident = Binder.clearCallingIdentity();
try {
return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
boolean preserveWindows, boolean animate, int animationDuration) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
if (animate) {
final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
return;
}
if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
throw new IllegalArgumentException("Stack: " + stackId
+ " doesn't support animated resize.");
}
stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
animationDuration, false /* fromFullscreen */);
} else {
final ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack == null) {
Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
return;
}
mStackSupervisor.resizeStackLocked(stack, destBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
allowResizeInDockedMode, !DEFER_RESUME);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds,
Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
PRESERVE_WINDOWS);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void setSplitScreenResizing(boolean resizing) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
mStackSupervisor.setSplitScreenResizing(resizing);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Try to place task to provided position. The final position might be different depending on
* current user and stacks state. The task will be moved to target stack if it's currently in
* different stack.
*/
@Override
public void positionTaskInStack(int taskId, int stackId, int position) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
+ taskId + " in stackId=" + stackId + " at position=" + position);
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task == null) {
throw new IllegalArgumentException("positionTaskInStack: no task for id="
+ taskId);
}
final ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack == null) {
throw new IllegalArgumentException("positionTaskInStack: no stack for id="
+ stackId);
}
if (!stack.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
+ " the position of task " + taskId + " in/to non-standard stack");
}
// TODO: Have the callers of this API call a separate reparent method if that is
// what they intended to do vs. having this method also do reparenting.
if (task.getStack() == stack) {
// Change position in current stack.
stack.positionChildAt(task, position);
} else {
// Reparent to new stack.
task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
!DEFER_RESUME, "positionTaskInStack");
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public List getAllStackInfos() {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
return mStackSupervisor.getAllStackInfosLocked();
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public StackInfo getStackInfo(int windowingMode, int activityType) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
return mStackSupervisor.getStackInfo(windowingMode, activityType);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public int getTaskForActivity(IBinder token, boolean onlyRoot) {
synchronized(this) {
return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
}
}
@Override
public void updateDeviceOwner(String packageName) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != SYSTEM_UID) {
throw new SecurityException("updateDeviceOwner called from non-system process");
}
synchronized (this) {
mDeviceOwnerName = packageName;
}
}
@Override
public void updateLockTaskPackages(int userId, String[] packages) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != SYSTEM_UID) {
enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
"updateLockTaskPackages()");
}
synchronized (this) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" +
Arrays.toString(packages));
mLockTaskController.updateLockTaskPackages(userId, packages);
}
}
@Override
public void updateLockTaskFeatures(int userId, int flags) {
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != SYSTEM_UID) {
enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
"updateLockTaskFeatures()");
}
synchronized (this) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
Integer.toHexString(flags));
mLockTaskController.updateLockTaskFeatures(userId, flags);
}
}
private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isSystemCaller) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
return;
}
final ActivityStack stack = mStackSupervisor.getFocusedStack();
if (stack == null || task != stack.topTask()) {
throw new IllegalArgumentException("Invalid task, not in foreground");
}
// {@code isSystemCaller} is used to distinguish whether this request is initiated by the
// system or a specific app.
// * System-initiated requests will only start the pinned mode (screen pinning)
// * App-initiated requests
// - will put the device in fully locked mode (LockTask), if the app is whitelisted
// - will start the pinned mode, otherwise
final int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
// When a task is locked, dismiss the pinned stack if it exists
mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
mLockTaskController.startLockTaskMode(task, isSystemCaller, callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void startLockTaskModeByToken(IBinder token) {
synchronized (this) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
return;
}
startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
}
}
@Override
public void startSystemLockTaskMode(int taskId) throws RemoteException {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
// This makes inner call to look as if it was initiated by system.
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
// When starting lock task mode the stack must be in front and focused
task.getStack().moveToFront("startSystemLockTaskMode");
startLockTaskModeLocked(task, true /* isSystemCaller */);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void stopLockTaskModeByToken(IBinder token) {
synchronized (this) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
return;
}
stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
}
}
/**
* This API should be called by SystemUI only when user perform certain action to dismiss
* lock task mode. We should only dismiss pinned lock task mode in this case.
*/
@Override
public void stopSystemLockTaskMode() throws RemoteException {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
stopLockTaskModeInternal(null, true /* isSystemCaller */);
}
private void stopLockTaskModeInternal(@Nullable TaskRecord task, boolean isSystemCaller) {
final int callingUid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
mLockTaskController.stopLockTaskMode(task, isSystemCaller, callingUid);
}
// Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
// task and jumping straight into a call in the case of emergency call back.
TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
if (tm != null) {
tm.showInCallScreen(false);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public boolean isInLockTaskMode() {
return getLockTaskModeState() != LOCK_TASK_MODE_NONE;
}
@Override
public int getLockTaskModeState() {
synchronized (this) {
return mLockTaskController.getLockTaskModeState();
}
}
@Override
public void showLockTaskEscapeMessage(IBinder token) {
synchronized (this) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
return;
}
mLockTaskController.showLockTaskToast();
}
}
@Override
public void setDisablePreviewScreenshots(IBinder token, boolean disable)
throws RemoteException {
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token="
+ token);
return;
}
final long origId = Binder.clearCallingIdentity();
try {
r.setDisablePreviewScreenshots(disable);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
// =========================================================
// CONTENT PROVIDERS
// =========================================================
private final List generateApplicationProvidersLocked(ProcessRecord app) {
List providers = null;
try {
providers = AppGlobals.getPackageManager()
.queryContentProviders(app.processName, app.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
| MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
.getList();
} catch (RemoteException ex) {
}
if (DEBUG_MU) Slog.v(TAG_MU,
"generateApplicationProvidersLocked, app.info.uid = " + app.uid);
int userId = app.userId;
if (providers != null) {
int N = providers.size();
app.pubProviders.ensureCapacity(N + app.pubProviders.size());
for (int i=0; i 0) {
i--;
PathPermission pp = pps[i];
String pprperm = pp.getReadPermission();
if (pprperm != null && checkComponentPermission(pprperm, callingPid, callingUid,
cpi.applicationInfo.uid, cpi.exported)
== PackageManager.PERMISSION_GRANTED) {
return null;
}
String ppwperm = pp.getWritePermission();
if (ppwperm != null && checkComponentPermission(ppwperm, callingPid, callingUid,
cpi.applicationInfo.uid, cpi.exported)
== PackageManager.PERMISSION_GRANTED) {
return null;
}
}
}
if (!checkedGrants && checkAuthorityGrants(callingUid, cpi, userId, checkUser)) {
return null;
}
final String suffix;
if (!cpi.exported) {
suffix = " that is not exported from UID " + cpi.applicationInfo.uid;
} else if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(cpi.readPermission)) {
suffix = " requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs";
} else {
suffix = " requires " + cpi.readPermission + " or " + cpi.writePermission;
}
final String msg = "Permission Denial: opening provider " + cpi.name
+ " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
+ ", uid=" + callingUid + ")" + suffix;
Slog.w(TAG, msg);
return msg;
}
/**
* Returns if the ContentProvider has granted a uri to callingUid
*/
boolean checkAuthorityGrants(int callingUid, ProviderInfo cpi, int userId, boolean checkUser) {
final ArrayMap perms = mGrantedUriPermissions.get(callingUid);
if (perms != null) {
for (int i=perms.size()-1; i>=0; i--) {
GrantUri grantUri = perms.keyAt(i);
if (grantUri.sourceUserId == userId || !checkUser) {
if (matchesProvider(grantUri.uri, cpi)) {
return true;
}
}
}
}
return false;
}
/**
* Returns true if the uri authority is one of the authorities specified in the provider.
*/
boolean matchesProvider(Uri uri, ProviderInfo cpi) {
String uriAuth = uri.getAuthority();
String cpiAuth = cpi.authority;
if (cpiAuth.indexOf(';') == -1) {
return cpiAuth.equals(uriAuth);
}
String[] cpiAuths = cpiAuth.split(";");
int length = cpiAuths.length;
for (int i = 0; i < length; i++) {
if (cpiAuths[i].equals(uriAuth)) return true;
}
return false;
}
ContentProviderConnection incProviderCountLocked(ProcessRecord r,
final ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
if (r != null) {
for (int i=0; i providers) {
if (providers == null) {
return;
}
enforceNotIsolatedCaller("publishContentProviders");
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when publishing content providers");
}
final long origId = Binder.clearCallingIdentity();
final int N = providers.size();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
dst.notifyAll();
}
updateOomAdjLocked(r, true);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}
Binder.restoreCallingIdentity(origId);
}
}
public boolean refContentProvider(IBinder connection, int stable, int unstable) {
ContentProviderConnection conn;
try {
conn = (ContentProviderConnection)connection;
} catch (ClassCastException e) {
String msg ="refContentProvider: " + connection
+ " not a ContentProviderConnection";
Slog.w(TAG, msg);
throw new IllegalArgumentException(msg);
}
if (conn == null) {
throw new NullPointerException("connection is null");
}
synchronized (this) {
if (stable > 0) {
conn.numStableIncs += stable;
}
stable = conn.stableCount + stable;
if (stable < 0) {
throw new IllegalStateException("stableCount < 0: " + stable);
}
if (unstable > 0) {
conn.numUnstableIncs += unstable;
}
unstable = conn.unstableCount + unstable;
if (unstable < 0) {
throw new IllegalStateException("unstableCount < 0: " + unstable);
}
if ((stable+unstable) <= 0) {
throw new IllegalStateException("ref counts can't go to zero here: stable="
+ stable + " unstable=" + unstable);
}
conn.stableCount = stable;
conn.unstableCount = unstable;
return !conn.dead;
}
}
public void unstableProviderDied(IBinder connection) {
ContentProviderConnection conn;
try {
conn = (ContentProviderConnection)connection;
} catch (ClassCastException e) {
String msg ="refContentProvider: " + connection
+ " not a ContentProviderConnection";
Slog.w(TAG, msg);
throw new IllegalArgumentException(msg);
}
if (conn == null) {
throw new NullPointerException("connection is null");
}
// Safely retrieve the content provider associated with the connection.
IContentProvider provider;
synchronized (this) {
provider = conn.provider.provider;
}
if (provider == null) {
// Um, yeah, we're way ahead of you.
return;
}
// Make sure the caller is being honest with us.
if (provider.asBinder().pingBinder()) {
// Er, no, still looks good to us.
synchronized (this) {
Slog.w(TAG, "unstableProviderDied: caller " + Binder.getCallingUid()
+ " says " + conn + " died, but we don't agree");
return;
}
}
// Well look at that! It's dead!
synchronized (this) {
if (conn.provider.provider != provider) {
// But something changed... good enough.
return;
}
ProcessRecord proc = conn.provider.proc;
if (proc == null || proc.thread == null) {
// Seems like the process is already cleaned up.
return;
}
// As far as we're concerned, this is just like receiving a
// death notification... just a bit prematurely.
reportUidInfoMessageLocked(TAG,
"Process " + proc.processName + " (pid " + proc.pid
+ ") early provider death",
proc.info.uid);
final long ident = Binder.clearCallingIdentity();
try {
appDiedLocked(proc);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void appNotRespondingViaProvider(IBinder connection) {
enforceCallingPermission(REMOVE_TASKS, "appNotRespondingViaProvider()");
final ContentProviderConnection conn = (ContentProviderConnection) connection;
if (conn == null) {
Slog.w(TAG, "ContentProviderConnection is null");
return;
}
final ProcessRecord host = conn.provider.proc;
if (host == null) {
Slog.w(TAG, "Failed to find hosting ProcessRecord");
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
mAppErrors.appNotResponding(host, null, null, false,
"ContentProvider not responding");
}
});
}
public final void installSystemProviders() {
List providers;
synchronized (this) {
ProcessRecord app = mProcessNames.get("system", SYSTEM_UID);
providers = generateApplicationProvidersLocked(app);
if (providers != null) {
for (int i=providers.size()-1; i>=0; i--) {
ProviderInfo pi = (ProviderInfo)providers.get(i);
if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
Slog.w(TAG, "Not installing system proc provider " + pi.name
+ ": not system .apk");
providers.remove(i);
}
}
}
}
if (providers != null) {
mSystemThread.installSystemProviders(providers);
}
synchronized (this) {
mSystemProvidersInstalled = true;
}
mConstants.start(mContext.getContentResolver());
mCoreSettingsObserver = new CoreSettingsObserver(this);
mFontScaleSettingObserver = new FontScaleSettingObserver();
mDevelopmentSettingsObserver = new DevelopmentSettingsObserver();
GlobalSettingsToPropertiesMapper.start(mContext.getContentResolver());
// Now that the settings provider is published we can consider sending
// in a rescue party.
RescueParty.onSettingsProviderPublished(mContext);
//mUsageStatsService.monitorPackages();
}
void startPersistentApps(int matchFlags) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;
synchronized (this) {
try {
final List apps = AppGlobals.getPackageManager()
.getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
for (ApplicationInfo app : apps) {
if (!"android".equals(app.packageName)) {
addAppLocked(app, null, false, null /* ABI override */);
}
}
} catch (RemoteException ex) {
}
}
}
/**
* When a user is unlocked, we need to install encryption-unaware providers
* belonging to any running apps.
*/
void installEncryptionUnawareProviders(int userId) {
// We're only interested in providers that are encryption unaware, and
// we don't care about uninstalled apps, since there's no way they're
// running at this point.
final int matchFlags = GET_PROVIDERS | MATCH_DIRECT_BOOT_UNAWARE;
synchronized (this) {
final int NP = mProcessNames.getMap().size();
for (int ip = 0; ip < NP; ip++) {
final SparseArray apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia = 0; ia < NA; ia++) {
final ProcessRecord app = apps.valueAt(ia);
if (app.userId != userId || app.thread == null || app.unlocked) continue;
final int NG = app.pkgList.size();
for (int ig = 0; ig < NG; ig++) {
try {
final String pkgName = app.pkgList.keyAt(ig);
final PackageInfo pkgInfo = AppGlobals.getPackageManager()
.getPackageInfo(pkgName, matchFlags, userId);
if (pkgInfo != null && !ArrayUtils.isEmpty(pkgInfo.providers)) {
for (ProviderInfo pi : pkgInfo.providers) {
// TODO: keep in sync with generateApplicationProvidersLocked
final boolean processMatch = Objects.equals(pi.processName,
app.processName) || pi.multiprocess;
final boolean userMatch = isSingleton(pi.processName,
pi.applicationInfo, pi.name, pi.flags)
? (app.userId == UserHandle.USER_SYSTEM) : true;
if (processMatch && userMatch) {
Log.v(TAG, "Installing " + pi);
app.thread.scheduleInstallProvider(pi);
} else {
Log.v(TAG, "Skipping " + pi);
}
}
}
} catch (RemoteException ignored) {
}
}
}
}
}
}
/**
* Allows apps to retrieve the MIME type of a URI.
* If an app is in the same user as the ContentProvider, or if it is allowed to interact across
* users, then it does not need permission to access the ContentProvider.
* Either, it needs cross-user uri grants.
*
* CTS tests for this functionality can be run with "runtest cts-appsecurity".
*
* Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/
* src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
*/
public String getProviderMimeType(Uri uri, int userId) {
enforceNotIsolatedCaller("getProviderMimeType");
final String name = uri.getAuthority();
int callingUid = Binder.getCallingUid();
int callingPid = Binder.getCallingPid();
long ident = 0;
boolean clearedIdentity = false;
userId = mUserController.unsafeConvertIncomingUser(userId);
if (canClearIdentity(callingPid, callingUid, userId)) {
clearedIdentity = true;
ident = Binder.clearCallingIdentity();
}
ContentProviderHolder holder = null;
try {
holder = getContentProviderExternalUnchecked(name, null, userId);
if (holder != null) {
return holder.provider.getType(uri);
}
} catch (RemoteException e) {
Log.w(TAG, "Content provider dead retrieving " + uri, e);
return null;
} catch (Exception e) {
Log.w(TAG, "Exception while determining type of " + uri, e);
return null;
} finally {
// We need to clear the identity to call removeContentProviderExternalUnchecked
if (!clearedIdentity) {
ident = Binder.clearCallingIdentity();
}
try {
if (holder != null) {
removeContentProviderExternalUnchecked(name, null, userId);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
return null;
}
private boolean canClearIdentity(int callingPid, int callingUid, int userId) {
if (UserHandle.getUserId(callingUid) == userId) {
return true;
}
if (checkComponentPermission(INTERACT_ACROSS_USERS, callingPid,
callingUid, -1, true) == PackageManager.PERMISSION_GRANTED
|| checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
return true;
}
return false;
}
// =========================================================
// GLOBAL MANAGEMENT
// =========================================================
@GuardedBy("this")
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
if (isolated) {
if (isolatedUid == 0) {
int stepsLeft = LAST_ISOLATED_UID - FIRST_ISOLATED_UID + 1;
while (true) {
if (mNextIsolatedProcessUid < FIRST_ISOLATED_UID
|| mNextIsolatedProcessUid > LAST_ISOLATED_UID) {
mNextIsolatedProcessUid = FIRST_ISOLATED_UID;
}
uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
mNextIsolatedProcessUid++;
if (mIsolatedProcesses.indexOfKey(uid) < 0) {
// No process for this uid, use it.
break;
}
stepsLeft--;
if (stepsLeft <= 0) {
return null;
}
}
} else {
// Special case for startIsolatedProcess (internal only), where
// the uid of the isolated process is specified by the caller.
uid = isolatedUid;
}
getPackageManagerInternalLocked().addIsolatedUid(uid, info.uid);
// Register the isolated UID with this application so BatteryStats knows to
// attribute resource usage to the application.
//
// NOTE: This is done here before addProcessNameLocked, which will tell BatteryStats
// about the process state of the isolated UID *before* it is registered with the
// owning application.
mBatteryStatsService.addIsolatedUid(uid, info.uid);
}
final ProcessRecord r = new ProcessRecord(this, stats, info, proc, uid);
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
// The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
r.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
r.persistent = true;
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (isolated && isolatedUid != 0) {
// Special case for startIsolatedProcess (internal only) - assume the process
// is required by the system server to prevent it being killed.
r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
}
addProcessNameLocked(r);
return r;
}
private boolean uidOnBackgroundWhitelist(final int uid) {
final int appId = UserHandle.getAppId(uid);
final int[] whitelist = mBackgroundAppIdWhitelist;
final int N = whitelist.length;
for (int i = 0; i < N; i++) {
if (appId == whitelist[i]) {
return true;
}
}
return false;
}
@Override
public boolean isBackgroundRestricted(String packageName) {
final int callingUid = Binder.getCallingUid();
final IPackageManager pm = AppGlobals.getPackageManager();
try {
final int packageUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.getUserId(callingUid));
if (packageUid != callingUid) {
throw new IllegalArgumentException("Uid " + callingUid
+ " cannot query restriction state for package " + packageName);
}
} catch (RemoteException exc) {
// Ignore.
}
return isBackgroundRestrictedNoCheck(callingUid, packageName);
}
boolean isBackgroundRestrictedNoCheck(final int uid, final String packageName) {
final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
uid, packageName);
return mode != AppOpsManager.MODE_ALLOWED;
}
@Override
public void backgroundWhitelistUid(final int uid) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Only the OS may call backgroundWhitelistUid()");
}
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Adding uid " + uid + " to bg uid whitelist");
}
synchronized (this) {
final int N = mBackgroundAppIdWhitelist.length;
int[] newList = new int[N+1];
System.arraycopy(mBackgroundAppIdWhitelist, 0, newList, 0, N);
newList[N] = UserHandle.getAppId(uid);
mBackgroundAppIdWhitelist = newList;
}
}
@GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
abiOverride);
}
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
boolean disableHiddenApiChecks, String abiOverride) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
info.uid, true);
} else {
app = null;
}
if (app == null) {
app = newProcessRecordLocked(info, customProcess, isolated, 0);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
// This package really, really can not be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
info.packageName, false, UserHandle.getUserId(app.uid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ info.packageName + ": " + e);
}
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application",
customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
abiOverride);
}
return app;
}
public void unhandledBack() {
enforceCallingPermission(android.Manifest.permission.FORCE_BACK,
"unhandledBack()");
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
try {
getFocusedStack().unhandledBackLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
public ParcelFileDescriptor openContentUri(String uriString) throws RemoteException {
enforceNotIsolatedCaller("openContentUri");
final int userId = UserHandle.getCallingUserId();
final Uri uri = Uri.parse(uriString);
String name = uri.getAuthority();
ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null, userId);
ParcelFileDescriptor pfd = null;
if (cph != null) {
// We record the binder invoker's uid in thread-local storage before
// going to the content provider to open the file. Later, in the code
// that handles all permissions checks, we look for this uid and use
// that rather than the Activity Manager's own uid. The effect is that
// we do the check against the caller's permissions even though it looks
// to the content provider like the Activity Manager itself is making
// the request.
Binder token = new Binder();
sCallerIdentity.set(new Identity(
token, Binder.getCallingPid(), Binder.getCallingUid()));
try {
pfd = cph.provider.openFile(null, uri, "r", null, token);
} catch (FileNotFoundException e) {
// do nothing; pfd will be returned null
} finally {
// Ensure that whatever happens, we clean up the identity state
sCallerIdentity.remove();
// Ensure we're done with the provider.
removeContentProviderExternalUnchecked(name, null, userId);
}
} else {
Slog.d(TAG, "Failed to get provider for authority '" + name + "'");
}
return pfd;
}
// Actually is sleeping or shutting down or whatever else in the future
// is an inactive state.
boolean isSleepingOrShuttingDownLocked() {
return isSleepingLocked() || mShuttingDown;
}
boolean isShuttingDownLocked() {
return mShuttingDown;
}
boolean isSleepingLocked() {
return mSleeping;
}
void reportGlobalUsageEventLocked(int event) {
mUsageStatsService.reportEvent("android", mUserController.getCurrentUserId(), event);
int[] profiles = mUserController.getCurrentProfileIds();
if (profiles != null) {
for (int i = profiles.length - 1; i >= 0; i--) {
mUsageStatsService.reportEvent((String)null, profiles[i], event);
}
}
}
void reportCurWakefulnessUsageEventLocked() {
reportGlobalUsageEventLocked(mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE
? UsageEvents.Event.SCREEN_INTERACTIVE
: UsageEvents.Event.SCREEN_NON_INTERACTIVE);
}
void reportCurKeyguardUsageEventLocked() {
reportGlobalUsageEventLocked(mKeyguardShown
? UsageEvents.Event.KEYGUARD_SHOWN
: UsageEvents.Event.KEYGUARD_HIDDEN);
}
void onWakefulnessChanged(int wakefulness) {
synchronized(this) {
boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
mWakefulness = wakefulness;
if (wasAwake != isAwake) {
// Also update state in a special way for running foreground services UI.
mServices.updateScreenStateLocked(isAwake);
reportCurWakefulnessUsageEventLocked();
mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
.sendToTarget();
}
updateOomAdjLocked();
}
}
@GuardedBy("this")
void finishRunningVoiceLocked() {
if (mRunningVoice != null) {
mRunningVoice = null;
mVoiceWakeLock.release();
updateSleepIfNeededLocked();
}
}
void startTimeTrackingFocusedActivityLocked() {
final ActivityRecord resumedActivity = mStackSupervisor.getResumedActivityLocked();
if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
mCurAppTimeTracker.start(resumedActivity.packageName);
}
}
@GuardedBy("this")
void updateSleepIfNeededLocked() {
final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
final boolean wasSleeping = mSleeping;
if (!shouldSleep) {
// If wasSleeping is true, we need to wake up activity manager state from when
// we started sleeping. In either case, we need to apply the sleep tokens, which
// will wake up stacks or put them to sleep as appropriate.
if (wasSleeping) {
mSleeping = false;
startTimeTrackingFocusedActivityLocked();
mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
mStackSupervisor.comeOutOfSleepIfNeededLocked();
}
mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
if (wasSleeping) {
updateOomAdjLocked();
}
} else if (!mSleeping && shouldSleep) {
mSleeping = true;
if (mCurAppTimeTracker != null) {
mCurAppTimeTracker.stop();
}
mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
mStackSupervisor.goingToSleepLocked();
updateResumedAppTrace(null /* resumed */);
updateOomAdjLocked();
}
}
/** Pokes the task persister. */
void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
mRecentTasks.notifyTaskPersisterLocked(task, flush);
}
/**
* Notifies all listeners when the pinned stack animation starts.
*/
@Override
public void notifyPinnedStackAnimationStarted() {
mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
}
/**
* Notifies all listeners when the pinned stack animation ends.
*/
@Override
public void notifyPinnedStackAnimationEnded() {
mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
}
@Override
public void notifyCleartextNetwork(int uid, byte[] firstPacket) {
mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget();
}
@Override
public boolean shutdown(int timeout) {
if (checkCallingPermission(android.Manifest.permission.SHUTDOWN)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SHUTDOWN);
}
boolean timedout = false;
synchronized(this) {
mShuttingDown = true;
mStackSupervisor.prepareForShutdownLocked();
updateEventDispatchingLocked();
timedout = mStackSupervisor.shutdownLocked(timeout);
}
mAppOpsService.shutdown();
if (mUsageStatsService != null) {
mUsageStatsService.prepareShutdown();
}
mBatteryStatsService.shutdown();
synchronized (this) {
mProcessStats.shutdownLocked();
notifyTaskPersisterLocked(null, true);
}
return timedout;
}
public final void activitySlept(IBinder token) {
if (DEBUG_ALL) Slog.v(TAG, "Activity slept: token=" + token);
final long origId = Binder.clearCallingIdentity();
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
mStackSupervisor.activitySleptLocked(r);
}
}
Binder.restoreCallingIdentity(origId);
}
@GuardedBy("this")
void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
Slog.d(TAG, "<<< startRunningVoiceLocked()");
mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
if (mRunningVoice == null || mRunningVoice.asBinder() != session.asBinder()) {
boolean wasRunningVoice = mRunningVoice != null;
mRunningVoice = session;
if (!wasRunningVoice) {
mVoiceWakeLock.acquire();
updateSleepIfNeededLocked();
}
}
}
private void updateEventDispatchingLocked() {
mWindowManager.setEventDispatching(mBooted && !mShuttingDown);
}
@Override
public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
int secondaryDisplayShowing) {
if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.DEVICE_POWER);
}
synchronized(this) {
long ident = Binder.clearCallingIdentity();
if (mKeyguardShown != keyguardShowing) {
mKeyguardShown = keyguardShowing;
reportCurKeyguardUsageEventLocked();
}
try {
mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
secondaryDisplayShowing);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, keyguardShowing ? 1 : 0, 0)
.sendToTarget();
}
@Override
public void notifyLockedProfile(@UserIdInt int userId) {
try {
if (!AppGlobals.getPackageManager().isUidPrivileged(Binder.getCallingUid())) {
throw new SecurityException("Only privileged app can call notifyLockedProfile");
}
} catch (RemoteException ex) {
throw new SecurityException("Fail to check is caller a privileged app", ex);
}
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
if (mUserController.shouldConfirmCredentials(userId)) {
if (mKeyguardController.isKeyguardLocked()) {
// Showing launcher to avoid user entering credential twice.
final int currentUserId = mUserController.getCurrentUserId();
startHomeActivityLocked(currentUserId, "notifyLockedProfile");
}
mStackSupervisor.lockAllProfileTasks(userId);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void startConfirmDeviceCredentialIntent(Intent intent, Bundle options) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startConfirmDeviceCredentialIntent");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
intent.addFlags(FLAG_ACTIVITY_NEW_TASK |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
FLAG_ACTIVITY_TASK_ON_HOME);
ActivityOptions activityOptions = options != null
? new ActivityOptions(options)
: ActivityOptions.makeBasic();
activityOptions.setLaunchTaskId(
mStackSupervisor.getHomeActivity().getTask().taskId);
mContext.startActivityAsUser(intent, activityOptions.toBundle(),
UserHandle.CURRENT);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
@Override
public void stopAppSwitches() {
enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "stopAppSwitches");
synchronized(this) {
mAppSwitchesAllowedTime = SystemClock.uptimeMillis()
+ APP_SWITCH_DELAY_TIME;
mDidAppSwitch = false;
mActivityStartController.schedulePendingActivityLaunches(APP_SWITCH_DELAY_TIME);
}
}
public void resumeAppSwitches() {
enforceCallerIsRecentsOrHasPermission(STOP_APP_SWITCHES, "resumeAppSwitches");
synchronized(this) {
// Note that we don't execute any pending app switches... we will
// let those wait until either the timeout, or the next start
// activity request.
mAppSwitchesAllowedTime = 0;
}
}
boolean checkAllowAppSwitchUid(int uid) {
ArrayMap types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid));
if (types != null) {
for (int i = types.size() - 1; i >= 0; i--) {
if (types.valueAt(i).intValue() == uid) {
return true;
}
}
}
return false;
}
boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid,
int callingPid, int callingUid, String name) {
if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) {
return true;
}
if (mRecentTasks.isCallerRecents(sourceUid)) {
return true;
}
int perm = checkComponentPermission(STOP_APP_SWITCHES, sourcePid, sourceUid, -1, true);
if (perm == PackageManager.PERMISSION_GRANTED) {
return true;
}
if (checkAllowAppSwitchUid(sourceUid)) {
return true;
}
// If the actual IPC caller is different from the logical source, then
// also see if they are allowed to control app switches.
if (callingUid != -1 && callingUid != sourceUid) {
perm = checkComponentPermission(STOP_APP_SWITCHES, callingPid, callingUid, -1, true);
if (perm == PackageManager.PERMISSION_GRANTED) {
return true;
}
if (checkAllowAppSwitchUid(callingUid)) {
return true;
}
}
Slog.w(TAG, name + " request from " + sourceUid + " stopped");
return false;
}
public void setDebugApp(String packageName, boolean waitForDebugger,
boolean persistent) {
enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
"setDebugApp()");
long ident = Binder.clearCallingIdentity();
try {
// Note that this is not really thread safe if there are multiple
// callers into it at the same time, but that's not a situation we
// care about.
if (persistent) {
final ContentResolver resolver = mContext.getContentResolver();
Settings.Global.putString(
resolver, Settings.Global.DEBUG_APP,
packageName);
Settings.Global.putInt(
resolver, Settings.Global.WAIT_FOR_DEBUGGER,
waitForDebugger ? 1 : 0);
}
synchronized (this) {
if (!persistent) {
mOrigDebugApp = mDebugApp;
mOrigWaitForDebugger = mWaitForDebugger;
}
mDebugApp = packageName;
mWaitForDebugger = waitForDebugger;
mDebugTransient = !persistent;
if (packageName != null) {
forceStopPackageLocked(packageName, -1, false, false, true, true,
false, UserHandle.USER_ALL, "set debug app");
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Set or remove an agent to be run whenever an app with the given process name starts.
*
* This method will not check whether the given process name matches a debuggable app. That
* would require scanning all current packages, and a rescan when new packages are installed
* or updated.
*
* Instead, do the check when an application is started and matched to a stored agent.
*
* @param packageName the process name of the app.
* @param agent the agent string to be used, or null to remove any previously set agent.
*/
@Override
public void setAgentApp(@NonNull String packageName, @Nullable String agent) {
synchronized (this) {
// note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
// its own permission.
if (checkCallingPermission(
android.Manifest.permission.SET_ACTIVITY_WATCHER) !=
PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(
"Requires permission " + android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
if (agent == null) {
if (mAppAgentMap != null) {
mAppAgentMap.remove(packageName);
if (mAppAgentMap.isEmpty()) {
mAppAgentMap = null;
}
}
} else {
if (mAppAgentMap == null) {
mAppAgentMap = new HashMap<>();
}
if (mAppAgentMap.size() >= 100) {
// Limit the size of the map, to avoid OOMEs.
Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+ "/" + agent);
return;
}
mAppAgentMap.put(packageName, agent);
}
}
}
void setTrackAllocationApp(ApplicationInfo app, String processName) {
synchronized (this) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
if (!isDebuggable) {
if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + app.packageName);
}
}
mTrackAllocationApp = processName;
}
}
void setProfileApp(ApplicationInfo app, String processName, ProfilerInfo profilerInfo) {
synchronized (this) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
if (!isDebuggable) {
if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + app.packageName);
}
}
mProfileApp = processName;
if (mProfilerInfo != null) {
if (mProfilerInfo.profileFd != null) {
try {
mProfilerInfo.profileFd.close();
} catch (IOException e) {
}
}
}
mProfilerInfo = new ProfilerInfo(profilerInfo);
mProfileType = 0;
}
}
void setNativeDebuggingAppLocked(ApplicationInfo app, String processName) {
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
if (!isDebuggable) {
if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + app.packageName);
}
}
mNativeDebuggingApp = processName;
}
@Override
public void setAlwaysFinish(boolean enabled) {
enforceCallingPermission(android.Manifest.permission.SET_ALWAYS_FINISH,
"setAlwaysFinish()");
long ident = Binder.clearCallingIdentity();
try {
Settings.Global.putInt(
mContext.getContentResolver(),
Settings.Global.ALWAYS_FINISH_ACTIVITIES, enabled ? 1 : 0);
synchronized (this) {
mAlwaysFinishActivities = enabled;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void setActivityController(IActivityController controller, boolean imAMonkey) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"setActivityController()");
synchronized (this) {
mController = controller;
mControllerIsAMonkey = imAMonkey;
Watchdog.getInstance().setActivityController(controller);
}
}
@Override
public void setUserIsMonkey(boolean userIsMonkey) {
synchronized (this) {
synchronized (mPidsSelfLocked) {
final int callingPid = Binder.getCallingPid();
ProcessRecord proc = mPidsSelfLocked.get(callingPid);
if (proc == null) {
throw new SecurityException("Unknown process: " + callingPid);
}
if (proc.instr == null || proc.instr.mUiAutomationConnection == null) {
throw new SecurityException("Only an instrumentation process "
+ "with a UiAutomation can call setUserIsMonkey");
}
}
mUserIsMonkey = userIsMonkey;
}
}
@Override
public boolean isUserAMonkey() {
synchronized (this) {
// If there is a controller also implies the user is a monkey.
return (mUserIsMonkey || (mController != null && mControllerIsAMonkey));
}
}
/**
* @deprecated This method is only used by a few internal components and it will soon be
* replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
* No new code should be calling it.
*/
@Deprecated
@Override
public void requestBugReport(int bugreportType) {
String extraOptions = null;
switch (bugreportType) {
case ActivityManager.BUGREPORT_OPTION_FULL:
// Default options.
break;
case ActivityManager.BUGREPORT_OPTION_INTERACTIVE:
extraOptions = "bugreportplus";
break;
case ActivityManager.BUGREPORT_OPTION_REMOTE:
extraOptions = "bugreportremote";
break;
case ActivityManager.BUGREPORT_OPTION_WEAR:
extraOptions = "bugreportwear";
break;
case ActivityManager.BUGREPORT_OPTION_TELEPHONY:
extraOptions = "bugreporttelephony";
break;
case ActivityManager.BUGREPORT_OPTION_WIFI:
extraOptions = "bugreportwifi";
break;
default:
throw new IllegalArgumentException("Provided bugreport type is not correct, value: "
+ bugreportType);
}
// Always log caller, even if it does not have permission to dump.
String type = extraOptions == null ? "bugreport" : extraOptions;
Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid());
enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
if (extraOptions != null) {
SystemProperties.set("dumpstate.options", extraOptions);
}
SystemProperties.set("ctl.start", "bugreport");
}
/**
* @deprecated This method is only used by a few internal components and it will soon be
* replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
* No new code should be calling it.
*/
@Deprecated
private void requestBugReportWithDescription(String shareTitle, String shareDescription,
int bugreportType) {
if (!TextUtils.isEmpty(shareTitle)) {
if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
String errorStr = "shareTitle should be less than " +
MAX_BUGREPORT_TITLE_SIZE + " characters";
throw new IllegalArgumentException(errorStr);
} else {
if (!TextUtils.isEmpty(shareDescription)) {
int length;
try {
length = shareDescription.getBytes("UTF-8").length;
} catch (UnsupportedEncodingException e) {
String errorStr = "shareDescription: UnsupportedEncodingException";
throw new IllegalArgumentException(errorStr);
}
if (length > SystemProperties.PROP_VALUE_MAX) {
String errorStr = "shareTitle should be less than " +
SystemProperties.PROP_VALUE_MAX + " bytes";
throw new IllegalArgumentException(errorStr);
} else {
SystemProperties.set("dumpstate.options.description", shareDescription);
}
}
SystemProperties.set("dumpstate.options.title", shareTitle);
}
}
Slog.d(TAG, "Bugreport notification title " + shareTitle
+ " description " + shareDescription);
requestBugReport(bugreportType);
}
/**
* @deprecated This method is only used by a few internal components and it will soon be
* replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
* No new code should be calling it.
*/
@Deprecated
@Override
public void requestTelephonyBugReport(String shareTitle, String shareDescription) {
requestBugReportWithDescription(shareTitle, shareDescription,
ActivityManager.BUGREPORT_OPTION_TELEPHONY);
}
/**
* @deprecated This method is only used by a few internal components and it will soon be
* replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
* No new code should be calling it.
*/
@Deprecated
@Override
public void requestWifiBugReport(String shareTitle, String shareDescription) {
requestBugReportWithDescription(shareTitle, shareDescription,
ActivityManager.BUGREPORT_OPTION_WIFI);
}
public static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
return r != null ? getInputDispatchingTimeoutLocked(r.app) : KEY_DISPATCHING_TIMEOUT;
}
public static long getInputDispatchingTimeoutLocked(ProcessRecord r) {
if (r != null && (r.instr != null || r.usingWrapper)) {
return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
}
return KEY_DISPATCHING_TIMEOUT;
}
@Override
public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.FILTER_EVENTS);
}
ProcessRecord proc;
long timeout;
synchronized (this) {
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
}
timeout = getInputDispatchingTimeoutLocked(proc);
}
if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
return -1;
}
return timeout;
}
/**
* Handle input dispatching timeouts.
* Returns whether input dispatching should be aborted or not.
*/
public boolean inputDispatchingTimedOut(final ProcessRecord proc,
final ActivityRecord activity, final ActivityRecord parent,
final boolean aboveSystem, String reason) {
if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.FILTER_EVENTS);
}
final String annotation;
if (reason == null) {
annotation = "Input dispatching timed out";
} else {
annotation = "Input dispatching timed out (" + reason + ")";
}
if (proc != null) {
synchronized (this) {
if (proc.debugging) {
return false;
}
if (proc.instr != null) {
Bundle info = new Bundle();
info.putString("shortMsg", "keyDispatchingTimedOut");
info.putString("longMsg", annotation);
finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
return true;
}
}
mHandler.post(new Runnable() {
@Override
public void run() {
mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
}
});
}
return true;
}
@Override
public Bundle getAssistContextExtras(int requestType) {
PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
null, null, true /* focused */, true /* newSessionId */,
UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
if (pae == null) {
return null;
}
synchronized (pae) {
while (!pae.haveResult) {
try {
pae.wait();
} catch (InterruptedException e) {
}
}
}
synchronized (this) {
buildAssistBundleLocked(pae, pae.result);
mPendingAssistExtras.remove(pae);
mUiHandler.removeCallbacks(pae);
}
return pae.extras;
}
@Override
public boolean isAssistDataAllowedOnCurrentActivity() {
int userId;
synchronized (this) {
final ActivityStack focusedStack = getFocusedStack();
if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
return false;
}
final ActivityRecord activity = focusedStack.getTopActivity();
if (activity == null) {
return false;
}
userId = activity.userId;
}
return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
}
@Override
public boolean showAssistFromActivity(IBinder token, Bundle args) {
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
ActivityRecord caller = ActivityRecord.forTokenLocked(token);
ActivityRecord top = getFocusedStack().getTopActivity();
if (top != caller) {
Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ " is not current top " + top);
return false;
}
if (!top.nowVisible) {
Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+ " is not visible");
return false;
}
}
return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null,
token);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
}
@Override
public boolean requestAutofillData(IAssistDataReceiver receiver, Bundle receiverExtras,
IBinder activityToken, int flags) {
return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null,
receiver, receiverExtras, activityToken, true, true, UserHandle.getCallingUserId(),
null, PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT, flags) != null;
}
private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
int flags) {
enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
"enqueueAssistContext()");
synchronized (this) {
ActivityRecord activity = getFocusedStack().getTopActivity();
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras failed: no top activity");
return null;
}
if (activity.app == null || activity.app.thread == null) {
Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
return null;
}
if (focused) {
if (activityToken != null) {
ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
if (activity != caller) {
Slog.w(TAG, "enqueueAssistContext failed: caller " + caller
+ " is not current top " + activity);
return null;
}
}
} else {
activity = ActivityRecord.forTokenLocked(activityToken);
if (activity == null) {
Slog.w(TAG, "enqueueAssistContext failed: activity for token=" + activityToken
+ " couldn't be found");
return null;
}
if (activity.app == null || activity.app.thread == null) {
Slog.w(TAG, "enqueueAssistContext failed: no process for " + activity);
return null;
}
}
PendingAssistExtras pae;
Bundle extras = new Bundle();
if (args != null) {
extras.putAll(args);
}
extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid);
pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
userHandle);
pae.isHome = activity.isActivityTypeHome();
// Increment the sessionId if necessary
if (newSessionId) {
mViSessionId++;
}
try {
activity.app.thread.requestAssistContextExtras(activity.appToken, pae, requestType,
mViSessionId, flags);
mPendingAssistExtras.add(pae);
mUiHandler.postDelayed(pae, timeout);
} catch (RemoteException e) {
Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
return null;
}
return pae;
}
}
void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
IAssistDataReceiver receiver;
synchronized (this) {
mPendingAssistExtras.remove(pae);
receiver = pae.receiver;
}
if (receiver != null) {
// Caller wants result sent back to them.
Bundle sendBundle = new Bundle();
// At least return the receiver extras
sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
try {
pae.receiver.onHandleAssistData(sendBundle);
} catch (RemoteException e) {
}
}
}
private void buildAssistBundleLocked(PendingAssistExtras pae, Bundle result) {
if (result != null) {
pae.extras.putBundle(Intent.EXTRA_ASSIST_CONTEXT, result);
}
if (pae.hint != null) {
pae.extras.putBoolean(pae.hint, true);
}
}
/** Called from an app when assist data is ready. */
@Override
public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
AssistContent content, Uri referrer) {
PendingAssistExtras pae = (PendingAssistExtras)token;
synchronized (pae) {
pae.result = extras;
pae.structure = structure;
pae.content = content;
if (referrer != null) {
pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
}
if (structure != null) {
structure.setHomeActivity(pae.isHome);
}
pae.haveResult = true;
pae.notifyAll();
if (pae.intent == null && pae.receiver == null) {
// Caller is just waiting for the result.
return;
}
}
// We are now ready to launch the assist activity.
IAssistDataReceiver sendReceiver = null;
Bundle sendBundle = null;
synchronized (this) {
buildAssistBundleLocked(pae, extras);
boolean exists = mPendingAssistExtras.remove(pae);
mUiHandler.removeCallbacks(pae);
if (!exists) {
// Timed out.
return;
}
if ((sendReceiver=pae.receiver) != null) {
// Caller wants result sent back to them.
sendBundle = new Bundle();
sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
sendBundle.putParcelable(ASSIST_KEY_STRUCTURE, pae.structure);
sendBundle.putParcelable(ASSIST_KEY_CONTENT, pae.content);
sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
}
}
if (sendReceiver != null) {
try {
sendReceiver.onHandleAssistData(sendBundle);
} catch (RemoteException e) {
}
return;
}
final long ident = Binder.clearCallingIdentity();
try {
if (TextUtils.equals(pae.intent.getAction(),
android.service.voice.VoiceInteractionService.SERVICE_INTERFACE)) {
pae.intent.putExtras(pae.extras);
mContext.startServiceAsUser(pae.intent, new UserHandle(pae.userHandle));
} else {
pae.intent.replaceExtras(pae.extras);
pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
closeSystemDialogs("assist");
try {
mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "No activity to handle assist action.", e);
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
Bundle args) {
return enqueueAssistContext(requestType, intent, hint, null, null, null,
true /* focused */, true /* newSessionId */, userHandle, args,
PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
}
public void registerProcessObserver(IProcessObserver observer) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerProcessObserver()");
synchronized (this) {
mProcessObservers.register(observer);
}
}
@Override
public void unregisterProcessObserver(IProcessObserver observer) {
synchronized (this) {
mProcessObservers.unregister(observer);
}
}
@Override
public int getUidProcessState(int uid, String callingPackage) {
if (!hasUsageStatsPermission(callingPackage)) {
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"getUidProcessState");
}
synchronized (this) {
UidRecord uidRec = mActiveUids.get(uid);
return uidRec != null ? uidRec.curProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
}
}
@Override
public void registerUidObserver(IUidObserver observer, int which, int cutpoint,
String callingPackage) {
if (!hasUsageStatsPermission(callingPackage)) {
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"registerUidObserver");
}
synchronized (this) {
mUidObservers.register(observer, new UidObserverRegistration(Binder.getCallingUid(),
callingPackage, which, cutpoint));
}
}
@Override
public void unregisterUidObserver(IUidObserver observer) {
synchronized (this) {
mUidObservers.unregister(observer);
}
}
@Override
public boolean isUidActive(int uid, String callingPackage) {
if (!hasUsageStatsPermission(callingPackage)) {
enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
"isUidActive");
}
synchronized (this) {
return isUidActiveLocked(uid);
}
}
boolean isUidActiveLocked(int uid) {
final UidRecord uidRecord = mActiveUids.get(uid);
return uidRecord != null && !uidRecord.setIdle;
}
@Override
public boolean convertFromTranslucent(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return false;
}
final boolean translucentChanged = r.changeWindowTranslucency(true);
if (translucentChanged) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
mWindowManager.setAppFullscreen(token, true);
return translucentChanged;
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public boolean convertToTranslucent(IBinder token, Bundle options) {
SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return false;
}
final TaskRecord task = r.getTask();
int index = task.mActivities.lastIndexOf(r);
if (index > 0) {
ActivityRecord under = task.mActivities.get(index - 1);
under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
}
final boolean translucentChanged = r.changeWindowTranslucency(false);
if (translucentChanged) {
r.getStack().convertActivityToTranslucent(r);
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mWindowManager.setAppFullscreen(token, false);
return translucentChanged;
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public Bundle getActivityOptions(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
final ActivityOptions activityOptions = r.takeOptionsLocked();
return activityOptions == null ? null : activityOptions.toBundle();
}
return null;
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
throw new IllegalArgumentException();
}
r.immersive = immersive;
// update associated state if we're frontmost
if (r == mStackSupervisor.getResumedActivityLocked()) {
if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
applyUpdateLockStateLocked(r);
}
}
}
@Override
public boolean isImmersive(IBinder token) {
synchronized (this) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
throw new IllegalArgumentException();
}
return r.immersive;
}
}
@Override
public void setVrThread(int tid) {
enforceSystemHasVrFeature();
synchronized (this) {
synchronized (mPidsSelfLocked) {
final int pid = Binder.getCallingPid();
final ProcessRecord proc = mPidsSelfLocked.get(pid);
mVrController.setVrThreadLocked(tid, pid, proc);
}
}
}
@Override
public void setPersistentVrThread(int tid) {
if (checkCallingPermission(permission.RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
final String msg = "Permission Denial: setPersistentVrThread() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + permission.RESTRICTED_VR_ACCESS;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
enforceSystemHasVrFeature();
synchronized (this) {
synchronized (mPidsSelfLocked) {
final int pid = Binder.getCallingPid();
final ProcessRecord proc = mPidsSelfLocked.get(pid);
mVrController.setPersistentVrThreadLocked(tid, pid, proc);
}
}
}
/**
* Schedule the given thread a normal scheduling priority.
*
* @param tid the tid of the thread to adjust the scheduling of.
* @param suppressLogs {@code true} if any error logging should be disabled.
*
* @return {@code true} if this succeeded.
*/
static boolean scheduleAsRegularPriority(int tid, boolean suppressLogs) {
try {
Process.setThreadScheduler(tid, Process.SCHED_OTHER, 0);
return true;
} catch (IllegalArgumentException e) {
if (!suppressLogs) {
Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
}
} catch (SecurityException e) {
if (!suppressLogs) {
Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
}
}
return false;
}
/**
* Schedule the given thread an FIFO scheduling priority.
*
* @param tid the tid of the thread to adjust the scheduling of.
* @param suppressLogs {@code true} if any error logging should be disabled.
*
* @return {@code true} if this succeeded.
*/
static boolean scheduleAsFifoPriority(int tid, boolean suppressLogs) {
try {
Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
return true;
} catch (IllegalArgumentException e) {
if (!suppressLogs) {
Slog.w(TAG, "Failed to set scheduling policy, thread does not exist:\n" + e);
}
} catch (SecurityException e) {
if (!suppressLogs) {
Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e);
}
}
return false;
}
/**
* Check that we have the features required for VR-related API calls, and throw an exception if
* not.
*/
private void enforceSystemHasVrFeature() {
if (!mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) {
throw new UnsupportedOperationException("VR mode not supported on this device!");
}
}
@Override
public void setRenderThread(int tid) {
synchronized (this) {
ProcessRecord proc;
int pid = Binder.getCallingPid();
if (pid == Process.myPid()) {
demoteSystemServerRenderThread(tid);
return;
}
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
// ensure the tid belongs to the process
if (!isThreadInProcess(pid, tid)) {
throw new IllegalArgumentException(
"Render thread does not belong to process");
}
proc.renderThreadTid = tid;
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
}
// promote to FIFO now
if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
if (mUseFifoUiScheduling) {
setThreadScheduler(proc.renderThreadTid,
SCHED_FIFO | SCHED_RESET_ON_FORK, 1);
} else {
setThreadPriority(proc.renderThreadTid, TOP_APP_PRIORITY_BOOST);
}
}
} else {
if (DEBUG_OOM_ADJ) {
Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? " +
"PID: " + pid + ", TID: " + tid + " FIFO: " +
mUseFifoUiScheduling);
}
}
}
}
}
/**
* We only use RenderThread in system_server to store task snapshots to the disk, which should
* happen in the background. Thus, demote render thread from system_server to a lower priority.
*
* @param tid the tid of the RenderThread
*/
private void demoteSystemServerRenderThread(int tid) {
setThreadPriority(tid, Process.THREAD_PRIORITY_BACKGROUND);
}
@Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
ActivityRecord r;
synchronized (this) {
r = ActivityRecord.isInStackLocked(token);
}
if (r == null) {
throw new IllegalArgumentException();
}
int err;
if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
VrManagerInternal.NO_ERROR) {
return err;
}
// Clear the binder calling uid since this path may call moveToTask().
final long callingId = Binder.clearCallingIdentity();
try {
synchronized(this) {
r.requestedVrComponent = (enabled) ? packageName : null;
// Update associated state if this activity is currently focused
if (r == mStackSupervisor.getResumedActivityLocked()) {
applyUpdateVrModeLocked(r);
}
return 0;
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
@Override
public boolean isVrModePackageEnabled(ComponentName packageName) {
enforceSystemHasVrFeature();
final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
return vrService.hasVrPackage(packageName, UserHandle.getCallingUserId()) ==
VrManagerInternal.NO_ERROR;
}
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("startActivity");
synchronized (this) {
ActivityRecord r = getFocusedStack().topRunningActivityLocked();
return (r != null) ? r.immersive : false;
}
}
/**
* @return whether the system should disable UI modes incompatible with VR mode.
*/
boolean shouldDisableNonVrUiLocked() {
return mVrController.shouldDisableNonVrUiLocked();
}
@Override
public boolean isTopOfTask(IBinder token) {
synchronized (this) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
throw new IllegalArgumentException();
}
return r.getTask().getTopActivity() == r;
}
}
@Override
public void setHasTopUi(boolean hasTopUi) throws RemoteException {
if (checkCallingPermission(permission.INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED) {
String msg = "Permission Denial: setHasTopUi() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
+ " requires " + permission.INTERNAL_SYSTEM_WINDOW;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final int pid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (this) {
boolean changed = false;
ProcessRecord pr;
synchronized (mPidsSelfLocked) {
pr = mPidsSelfLocked.get(pid);
if (pr == null) {
Slog.w(TAG, "setHasTopUi called on unknown pid: " + pid);
return;
}
if (pr.hasTopUi != hasTopUi) {
if (DEBUG_OOM_ADJ) {
Slog.d(TAG, "Setting hasTopUi=" + hasTopUi + " for pid=" + pid);
}
pr.hasTopUi = hasTopUi;
changed = true;
}
}
if (changed) {
updateOomAdjLocked(pr, true);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
if (pid == Process.myPid()) {
Slog.wtf(TAG, "system can't run remote animation");
return;
}
synchronized (ActivityManagerService.this) {
final ProcessRecord pr;
synchronized (mPidsSelfLocked) {
pr = mPidsSelfLocked.get(pid);
if (pr == null) {
Slog.w(TAG, "setRunningRemoteAnimation called on unknown pid: " + pid);
return;
}
}
if (pr.runningRemoteAnimation == runningRemoteAnimation) {
return;
}
pr.runningRemoteAnimation = runningRemoteAnimation;
if (DEBUG_OOM_ADJ) {
Slog.i(TAG, "Setting runningRemoteAnimation=" + pr.runningRemoteAnimation
+ " for pid=" + pid);
}
updateOomAdjLocked(pr, true);
}
}
public final void enterSafeMode() {
synchronized(this) {
// It only makes sense to do this before the system is ready
// and started launching other packages.
if (!mSystemReady) {
try {
AppGlobals.getPackageManager().enterSafeMode();
} catch (RemoteException e) {
}
}
mSafeMode = true;
}
}
public final void showSafeModeOverlay() {
View v = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.safe_mode, null);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.BOTTOM | Gravity.START;
lp.format = v.getBackground().getOpacity();
lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
((WindowManager)mContext.getSystemService(
Context.WINDOW_SERVICE)).addView(v, lp);
}
@Override
public void noteWakeupAlarm(IIntentSender sender, WorkSource workSource, int sourceUid,
String sourcePkg, String tag) {
if (workSource != null && workSource.isEmpty()) {
workSource = null;
}
if (sourceUid <= 0 && workSource == null) {
// Try and derive a UID to attribute things to based on the caller.
if (sender != null) {
if (!(sender instanceof PendingIntentRecord)) {
return;
}
final PendingIntentRecord rec = (PendingIntentRecord) sender;
final int callerUid = Binder.getCallingUid();
sourceUid = rec.uid == callerUid ? SYSTEM_UID : rec.uid;
} else {
// TODO(narayan): Should we throw an exception in this case ? It means that we
// haven't been able to derive a UID to attribute things to.
return;
}
}
if (DEBUG_POWER) {
Slog.w(TAG, "noteWakupAlarm[ sourcePkg=" + sourcePkg + ", sourceUid=" + sourceUid
+ ", workSource=" + workSource + ", tag=" + tag + "]");
}
mBatteryStatsService.noteWakupAlarm(sourcePkg, sourceUid, workSource, tag);
}
@Override
public void noteAlarmStart(IIntentSender sender, WorkSource workSource, int sourceUid,
String tag) {
if (workSource != null && workSource.isEmpty()) {
workSource = null;
}
if (sourceUid <= 0 && workSource == null) {
// Try and derive a UID to attribute things to based on the caller.
if (sender != null) {
if (!(sender instanceof PendingIntentRecord)) {
return;
}
final PendingIntentRecord rec = (PendingIntentRecord) sender;
final int callerUid = Binder.getCallingUid();
sourceUid = rec.uid == callerUid ? SYSTEM_UID : rec.uid;
} else {
// TODO(narayan): Should we throw an exception in this case ? It means that we
// haven't been able to derive a UID to attribute things to.
return;
}
}
if (DEBUG_POWER) {
Slog.w(TAG, "noteAlarmStart[sourceUid=" + sourceUid + ", workSource=" + workSource +
", tag=" + tag + "]");
}
mBatteryStatsService.noteAlarmStart(tag, workSource, sourceUid);
}
@Override
public void noteAlarmFinish(IIntentSender sender, WorkSource workSource, int sourceUid,
String tag) {
if (workSource != null && workSource.isEmpty()) {
workSource = null;
}
if (sourceUid <= 0 && workSource == null) {
// Try and derive a UID to attribute things to based on the caller.
if (sender != null) {
if (!(sender instanceof PendingIntentRecord)) {
return;
}
final PendingIntentRecord rec = (PendingIntentRecord) sender;
final int callerUid = Binder.getCallingUid();
sourceUid = rec.uid == callerUid ? SYSTEM_UID : rec.uid;
} else {
// TODO(narayan): Should we throw an exception in this case ? It means that we
// haven't been able to derive a UID to attribute things to.
return;
}
}
if (DEBUG_POWER) {
Slog.w(TAG, "noteAlarmFinish[sourceUid=" + sourceUid + ", workSource=" + workSource +
", tag=" + tag + "]");
}
mBatteryStatsService.noteAlarmFinish(tag, workSource, sourceUid);
}
public boolean killPids(int[] pids, String pReason, boolean secure) {
if (Binder.getCallingUid() != SYSTEM_UID) {
throw new SecurityException("killPids only available to the system");
}
String reason = (pReason == null) ? "Unknown" : pReason;
// XXX Note: don't acquire main activity lock here, because the window
// manager calls in with its locks held.
boolean killed = false;
synchronized (mPidsSelfLocked) {
int worstType = 0;
for (int i=0; i worstType) {
worstType = type;
}
}
}
// If the worst oom_adj is somewhere in the cached proc LRU range,
// then constrain it so we will kill all cached procs.
if (worstType < ProcessList.CACHED_APP_MAX_ADJ
&& worstType > ProcessList.CACHED_APP_MIN_ADJ) {
worstType = ProcessList.CACHED_APP_MIN_ADJ;
}
// If this is not a secure call, don't let it kill processes that
// are important.
if (!secure && worstType < ProcessList.SERVICE_ADJ) {
worstType = ProcessList.SERVICE_ADJ;
}
Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType);
for (int i=0; i= worstType && !proc.killedByAm) {
proc.kill(reason, true);
killed = true;
}
}
}
return killed;
}
@Override
public void killUid(int appId, int userId, String reason) {
enforceCallingPermission(Manifest.permission.KILL_UID, "killUid");
synchronized (this) {
final long identity = Binder.clearCallingIdentity();
try {
killPackageProcessesLocked(null, appId, userId,
ProcessList.PERSISTENT_PROC_ADJ, false, true, true, true,
reason != null ? reason : "kill uid");
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
@Override
public boolean killProcessesBelowForeground(String reason) {
if (Binder.getCallingUid() != SYSTEM_UID) {
throw new SecurityException("killProcessesBelowForeground() only available to system");
}
return killProcessesBelowAdj(ProcessList.FOREGROUND_APP_ADJ, reason);
}
private boolean killProcessesBelowAdj(int belowAdj, String reason) {
if (Binder.getCallingUid() != SYSTEM_UID) {
throw new SecurityException("killProcessesBelowAdj() only available to system");
}
boolean killed = false;
synchronized (mPidsSelfLocked) {
final int size = mPidsSelfLocked.size();
for (int i = 0; i < size; i++) {
final int pid = mPidsSelfLocked.keyAt(i);
final ProcessRecord proc = mPidsSelfLocked.valueAt(i);
if (proc == null) continue;
final int adj = proc.setAdj;
if (adj > belowAdj && !proc.killedByAm) {
proc.kill(reason, true);
killed = true;
}
}
}
return killed;
}
@Override
public void hang(final IBinder who, boolean allowRestart) {
if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
final IBinder.DeathRecipient death = new DeathRecipient() {
@Override
public void binderDied() {
synchronized (this) {
notifyAll();
}
}
};
try {
who.linkToDeath(death, 0);
} catch (RemoteException e) {
Slog.w(TAG, "hang: given caller IBinder is already dead.");
return;
}
synchronized (this) {
Watchdog.getInstance().setAllowRestart(allowRestart);
Slog.i(TAG, "Hanging system process at request of pid " + Binder.getCallingPid());
synchronized (death) {
while (who.isBinderAlive()) {
try {
death.wait();
} catch (InterruptedException e) {
}
}
}
Watchdog.getInstance().setAllowRestart(true);
}
}
@Override
public void restart() {
if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
Log.i(TAG, "Sending shutdown broadcast...");
BroadcastReceiver br = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
// Now the broadcast is done, finish up the low-level shutdown.
Log.i(TAG, "Shutting down activity manager...");
shutdown(10000);
Log.i(TAG, "Shutdown complete, restarting!");
killProcess(myPid());
System.exit(10);
}
};
// First send the high-level shut down broadcast.
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_SHUTDOWN_USERSPACE_ONLY, true);
/* For now we are not doing a clean shutdown, because things seem to get unhappy.
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
*/
br.onReceive(mContext, intent);
}
private long getLowRamTimeSinceIdle(long now) {
return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now-mLowRamStartTime) : 0);
}
@Override
public void performIdleMaintenance() {
if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
synchronized (this) {
final long now = SystemClock.uptimeMillis();
final long timeSinceLastIdle = now - mLastIdleTime;
final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now);
mLastIdleTime = now;
mLowRamTimeSinceLastIdle = 0;
if (mLowRamStartTime != 0) {
mLowRamStartTime = now;
}
StringBuilder sb = new StringBuilder(128);
sb.append("Idle maintenance over ");
TimeUtils.formatDuration(timeSinceLastIdle, sb);
sb.append(" low RAM for ");
TimeUtils.formatDuration(lowRamSinceLastIdle, sb);
Slog.i(TAG, sb.toString());
// If at least 1/3 of our time since the last idle period has been spent
// with RAM low, then we want to kill processes.
boolean doKilling = lowRamSinceLastIdle > (timeSinceLastIdle/3);
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord proc = mLruProcesses.get(i);
if (proc.notCachedSinceIdle) {
if (proc.setProcState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) {
if (doKilling && proc.initialIdlePss != 0
&& proc.lastPss > ((proc.initialIdlePss*3)/2)) {
sb = new StringBuilder(128);
sb.append("Kill");
sb.append(proc.processName);
sb.append(" in idle maint: pss=");
sb.append(proc.lastPss);
sb.append(", swapPss=");
sb.append(proc.lastSwapPss);
sb.append(", initialPss=");
sb.append(proc.initialIdlePss);
sb.append(", period=");
TimeUtils.formatDuration(timeSinceLastIdle, sb);
sb.append(", lowRamPeriod=");
TimeUtils.formatDuration(lowRamSinceLastIdle, sb);
Slog.wtfQuiet(TAG, sb.toString());
proc.kill("idle maint (pss " + proc.lastPss
+ " from " + proc.initialIdlePss + ")", true);
}
}
} else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME
&& proc.setProcState >= ActivityManager.PROCESS_STATE_PERSISTENT) {
proc.notCachedSinceIdle = true;
proc.initialIdlePss = 0;
proc.nextPssTime = ProcessList.computeNextPssTime(proc.setProcState, null,
mTestPssMode, isSleepingLocked(), now);
}
}
}
}
@Override
public void sendIdleJobTrigger() {
if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
final long ident = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(ACTION_TRIGGER_IDLE)
.setPackage("android")
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
broadcastIntent(null, intent, null, null, 0, null, null, null,
OP_NONE, null, false, false, UserHandle.USER_ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void retrieveSettings() {
final ContentResolver resolver = mContext.getContentResolver();
final boolean freeformWindowManagement =
mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
|| Settings.Global.getInt(
resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow(mContext);
final boolean supportsPictureInPicture = supportsMultiWindow &&
mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
final boolean supportsSplitScreenMultiWindow =
ActivityManager.supportsSplitScreenMultiWindow(mContext);
final boolean supportsMultiDisplay = mContext.getPackageManager()
.hasSystemFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
final boolean alwaysFinishActivities =
Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
final long waitForNetworkTimeoutMs = Settings.Global.getLong(resolver,
NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
final boolean supportsLeanbackOnly =
mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY);
mHiddenApiBlacklist.registerObserver();
// Transfer any global setting for forcing RTL layout, into a System Property
SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
final Configuration configuration = new Configuration();
Settings.System.getConfiguration(resolver, configuration);
if (forceRtl) {
// This will take care of setting the correct layout direction flags
configuration.setLayoutDirection(configuration.locale);
}
synchronized (this) {
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
mSupportsLeanbackOnly = supportsLeanbackOnly;
mForceResizableActivities = forceResizable;
final boolean multiWindowFormEnabled = freeformWindowManagement
|| supportsSplitScreenMultiWindow
|| supportsPictureInPicture
|| supportsMultiDisplay;
if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
mSupportsMultiWindow = true;
mSupportsFreeformWindowManagement = freeformWindowManagement;
mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
mSupportsPictureInPicture = supportsPictureInPicture;
mSupportsMultiDisplay = supportsMultiDisplay;
} else {
mSupportsMultiWindow = false;
mSupportsFreeformWindowManagement = false;
mSupportsSplitScreenMultiWindow = false;
mSupportsPictureInPicture = false;
mSupportsMultiDisplay = false;
}
mWindowManager.setForceResizableTasks(mForceResizableActivities);
mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
// This happens before any activities are started, so we can change global configuration
// in-place.
updateConfigurationLocked(configuration, null, true);
final Configuration globalConfig = getGlobalConfiguration();
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
// Load resources only after the current configuration has been set.
final Resources res = mContext.getResources();
mThumbnailWidth = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_height);
mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
com.android.internal.R.string.config_appsNotReportingCrashes));
mUserController.mUserSwitchUiEnabled = !res.getBoolean(
com.android.internal.R.bool.config_customUserSwitchUi);
mUserController.mMaxRunningUsers = res.getInteger(
com.android.internal.R.integer.config_multiuserMaxRunningUsers);
if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
mFullscreenThumbnailScale = (float) res
.getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
(float) globalConfig.screenWidthDp;
} else {
mFullscreenThumbnailScale = res.getFraction(
com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
}
mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs;
}
}
public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
traceLog.traceBegin("PhaseActivityManagerReady");
synchronized(this) {
if (mSystemReady) {
// If we're done calling all the receivers, run the next "boot phase" passed in
// by the SystemServer
if (goingCallback != null) {
goingCallback.run();
}
return;
}
mHasHeavyWeightFeature = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CANT_SAVE_STATE);
mLocalDeviceIdleController
= LocalServices.getService(DeviceIdleController.LocalService.class);
mAssistUtils = new AssistUtils(mContext);
mVrController.onSystemReady();
// Make sure we have the current profile info, since it is needed for security checks.
mUserController.onSystemReady();
mRecentTasks.onSystemReadyLocked();
mAppOpsService.systemReady();
mSystemReady = true;
}
try {
sTheRealBuildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE))
.getSerial();
} catch (RemoteException e) {}
ArrayList procsToKill = null;
synchronized(mPidsSelfLocked) {
for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
ProcessRecord proc = mPidsSelfLocked.valueAt(i);
if (!isAllowedWhileBooting(proc.info)){
if (procsToKill == null) {
procsToKill = new ArrayList();
}
procsToKill.add(proc);
}
}
}
synchronized(this) {
if (procsToKill != null) {
for (int i=procsToKill.size()-1; i>=0; i--) {
ProcessRecord proc = procsToKill.get(i);
Slog.i(TAG, "Removing system update proc: " + proc);
removeProcessLocked(proc, true, false, "system update done");
}
}
// Now that we have cleaned up any update processes, we
// are ready to start launching real processes and know that
// we won't trample on them any more.
mProcessesReady = true;
}
Slog.i(TAG, "System now ready");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY,
SystemClock.uptimeMillis());
synchronized(this) {
// Make sure we have no pre-ready processes sitting around.
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
ResolveInfo ri = mContext.getPackageManager()
.resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
STOCK_PM_FLAGS);
CharSequence errorMsg = null;
if (ri != null) {
ActivityInfo ai = ri.activityInfo;
ApplicationInfo app = ai.applicationInfo;
if ((app.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
mTopAction = Intent.ACTION_FACTORY_TEST;
mTopData = null;
mTopComponent = new ComponentName(app.packageName,
ai.name);
} else {
errorMsg = mContext.getResources().getText(
com.android.internal.R.string.factorytest_not_system);
}
} else {
errorMsg = mContext.getResources().getText(
com.android.internal.R.string.factorytest_no_action);
}
if (errorMsg != null) {
mTopAction = null;
mTopData = null;
mTopComponent = null;
Message msg = Message.obtain();
msg.what = SHOW_FACTORY_ERROR_UI_MSG;
msg.getData().putCharSequence("msg", errorMsg);
mUiHandler.sendMessage(msg);
}
}
}
retrieveSettings();
final int currentUserId = mUserController.getCurrentUserId();
synchronized (this) {
readGrantedUriPermissionsLocked();
}
final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
if (pmi != null) {
pmi.registerLowPowerModeObserver(ServiceType.FORCE_BACKGROUND_CHECK,
state -> updateForceBackgroundCheck(state.batterySaverEnabled));
updateForceBackgroundCheck(
pmi.getLowPowerState(ServiceType.FORCE_BACKGROUND_CHECK).batterySaverEnabled);
} else {
Slog.wtf(TAG, "PowerManagerInternal not found.");
}
if (goingCallback != null) goingCallback.run();
traceLog.traceBegin("ActivityManagerStartApps");
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
Integer.toString(currentUserId), currentUserId);
mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
Integer.toString(currentUserId), currentUserId);
mSystemServiceManager.startUser(currentUserId);
synchronized (this) {
// Only start up encryption-aware persistent apps; once user is
// unlocked we'll come back around and start unaware apps
startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
// Start up initial activity.
mBooting = true;
// Enable home activity for system user, so that the system can always boot. We don't
// do this when the system user is not setup since the setup wizard should be the one
// to handle home activity in this case.
if (UserManager.isSplitSystemUser() &&
Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) {
ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
try {
AppGlobals.getPackageManager().setComponentEnabledSetting(cName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0,
UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
startHomeActivityLocked(currentUserId, "systemReady");
try {
if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
+ " data partition or your device will be unstable.");
mUiHandler.obtainMessage(SHOW_UID_ERROR_UI_MSG).sendToTarget();
}
} catch (RemoteException e) {
}
if (!Build.isBuildConsistent()) {
Slog.e(TAG, "Build fingerprint is not consistent, warning user");
mUiHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_UI_MSG).sendToTarget();
}
long ident = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(Intent.ACTION_USER_STARTED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, OP_NONE,
null, false, false, MY_PID, SYSTEM_UID,
currentUserId);
intent = new Intent(Intent.ACTION_USER_STARTING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
broadcastIntentLocked(null, null, intent,
null, new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser)
throws RemoteException {
}
}, 0, null, null,
new String[] {INTERACT_ACROSS_USERS}, OP_NONE,
null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
} catch (Throwable t) {
Slog.wtf(TAG, "Failed sending first user broadcasts", t);
} finally {
Binder.restoreCallingIdentity(ident);
}
mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
BinderInternal.nSetBinderProxyCountEnabled(true);
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@Override
public void onLimitReached(int uid) {
Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ Process.myUid());
if (uid == Process.SYSTEM_UID) {
Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
} else {
killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
"Too many Binders sent to SYSTEM");
}
}
}, mHandler);
traceLog.traceEnd(); // ActivityManagerStartApps
traceLog.traceEnd(); // PhaseActivityManagerReady
}
}
private void updateForceBackgroundCheck(boolean enabled) {
synchronized (this) {
if (mForceBackgroundCheck != enabled) {
mForceBackgroundCheck = enabled;
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Force background check " + (enabled ? "enabled" : "disabled"));
}
if (mForceBackgroundCheck) {
// Stop background services for idle UIDs.
doStopUidForIdleUidsLocked();
}
}
}
}
void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) {
synchronized (this) {
mAppErrors.killAppAtUserRequestLocked(app, fromDialog);
}
}
void skipCurrentReceiverLocked(ProcessRecord app) {
for (BroadcastQueue queue : mBroadcastQueues) {
queue.skipCurrentReceiverLocked(app);
}
}
/**
* Used by {@link com.android.internal.os.RuntimeInit} to report when an application crashes.
* The application process will exit immediately after this call returns.
* @param app object of the crashing app, null for the system server
* @param crashInfo describing the exception
*/
public void handleApplicationCrash(IBinder app,
ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
ProcessRecord r = findAppProcess(app, "Crash");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
handleApplicationCrashInner("crash", r, processName, crashInfo);
}
/* Native crash reporting uses this inner version because it needs to be somewhat
* decoupled from the AM-managed cleanup lifecycle
*/
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
UserHandle.getUserId(Binder.getCallingUid()), processName,
r == null ? -1 : r.info.flags,
crashInfo.exceptionClassName,
crashInfo.exceptionMessage,
crashInfo.throwFileName,
crashInfo.throwLineNumber);
StatsLog.write(StatsLog.APP_CRASH_OCCURRED,
Binder.getCallingUid(),
eventType,
processName,
Binder.getCallingPid(),
(r != null && r.info != null) ? r.info.packageName : "",
(r != null && r.info != null) ? (r.info.isInstantApp()
? StatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
: StatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__FALSE)
: StatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__UNAVAILABLE,
r != null ? (r.isInterestingToUserLocked()
? StatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__FOREGROUND
: StatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__BACKGROUND)
: StatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN
);
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
mAppErrors.crashApplication(r, crashInfo);
}
public void handleApplicationStrictModeViolation(
IBinder app,
int violationMask,
StrictMode.ViolationInfo info) {
// We're okay if the ProcessRecord is missing; it probably means that
// we're reporting a violation from the system process itself.
final ProcessRecord r = findAppProcess(app, "StrictMode");
if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) {
Integer stackFingerprint = info.hashCode();
boolean logIt = true;
synchronized (mAlreadyLoggedViolatedStacks) {
if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) {
logIt = false;
// TODO: sub-sample into EventLog for these, with
// the info.durationMillis? Then we'd get
// the relative pain numbers, without logging all
// the stack traces repeatedly. We'd want to do
// likewise in the client code, which also does
// dup suppression, before the Binder call.
} else {
if (mAlreadyLoggedViolatedStacks.size() >= MAX_DUP_SUPPRESSED_STACKS) {
mAlreadyLoggedViolatedStacks.clear();
}
mAlreadyLoggedViolatedStacks.add(stackFingerprint);
}
}
if (logIt) {
logStrictModeViolationToDropBox(r, info);
}
}
if ((violationMask & StrictMode.PENALTY_DIALOG) != 0) {
AppErrorResult result = new AppErrorResult();
synchronized (this) {
final long origId = Binder.clearCallingIdentity();
Message msg = Message.obtain();
msg.what = SHOW_STRICT_MODE_VIOLATION_UI_MSG;
HashMap data = new HashMap();
data.put("result", result);
data.put("app", r);
data.put("violationMask", violationMask);
data.put("info", info);
msg.obj = data;
mUiHandler.sendMessage(msg);
Binder.restoreCallingIdentity(origId);
}
int res = result.get();
Slog.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
}
}
// Depending on the policy in effect, there could be a bunch of
// these in quick succession so we try to batch these together to
// minimize disk writes, number of dropbox entries, and maximize
// compression, by having more fewer, larger records.
private void logStrictModeViolationToDropBox(
ProcessRecord process,
StrictMode.ViolationInfo info) {
if (info == null) {
return;
}
final boolean isSystemApp = process == null ||
(process.info.flags & (ApplicationInfo.FLAG_SYSTEM |
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
final String processName = process == null ? "unknown" : process.processName;
final DropBoxManager dbox = (DropBoxManager)
mContext.getSystemService(Context.DROPBOX_SERVICE);
// Exit early if the dropbox isn't configured to accept this report type.
final String dropboxTag = processClass(process) + "_strictmode";
if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
final StringBuilder sb = new StringBuilder(1024);
synchronized (sb) {
appendDropBoxProcessHeaders(process, processName, sb);
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
sb.append("System-App: ").append(isSystemApp).append("\n");
sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n");
if (info.violationNumThisLoop != 0) {
sb.append("Loop-Violation-Number: ").append(info.violationNumThisLoop).append("\n");
}
if (info.numAnimationsRunning != 0) {
sb.append("Animations-Running: ").append(info.numAnimationsRunning).append("\n");
}
if (info.broadcastIntentAction != null) {
sb.append("Broadcast-Intent-Action: ").append(info.broadcastIntentAction).append("\n");
}
if (info.durationMillis != -1) {
sb.append("Duration-Millis: ").append(info.durationMillis).append("\n");
}
if (info.numInstances != -1) {
sb.append("Instance-Count: ").append(info.numInstances).append("\n");
}
if (info.tags != null) {
for (String tag : info.tags) {
sb.append("Span-Tag: ").append(tag).append("\n");
}
}
sb.append("\n");
sb.append(info.getStackTrace());
sb.append("\n");
if (info.getViolationDetails() != null) {
sb.append(info.getViolationDetails());
sb.append("\n");
}
}
final String res = sb.toString();
IoThread.getHandler().post(() -> {
dbox.addText(dropboxTag, res);
});
}
/**
* Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors.
* @param app object of the crashing app, null for the system server
* @param tag reported by the caller
* @param system whether this wtf is coming from the system
* @param crashInfo describing the context of the error
* @return true if the process should exit immediately (WTF is fatal)
*/
public boolean handleApplicationWtf(final IBinder app, final String tag, boolean system,
final ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
if (system) {
// If this is coming from the system, we could very well have low-level
// system locks held, so we want to do this all asynchronously. And we
// never want this to become fatal, so there is that too.
mHandler.post(new Runnable() {
@Override public void run() {
handleApplicationWtfInner(callingUid, callingPid, app, tag, crashInfo);
}
});
return false;
}
final ProcessRecord r = handleApplicationWtfInner(callingUid, callingPid, app, tag,
crashInfo);
final boolean isFatal = Build.IS_ENG || Settings.Global
.getInt(mContext.getContentResolver(), Settings.Global.WTF_IS_FATAL, 0) != 0;
final boolean isSystem = (r == null) || r.persistent;
if (isFatal && !isSystem) {
mAppErrors.crashApplication(r, crashInfo);
return true;
} else {
return false;
}
}
ProcessRecord handleApplicationWtfInner(int callingUid, int callingPid, IBinder app, String tag,
final ApplicationErrorReport.CrashInfo crashInfo) {
final ProcessRecord r = findAppProcess(app, "WTF");
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
EventLog.writeEvent(EventLogTags.AM_WTF, UserHandle.getUserId(callingUid), callingPid,
processName, r == null ? -1 : r.info.flags, tag, crashInfo.exceptionMessage);
StatsLog.write(StatsLog.WTF_OCCURRED, callingUid, tag, processName,
callingPid);
addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo);
return r;
}
/**
* @param app object of some object (as stored in {@link com.android.internal.os.RuntimeInit})
* @return the corresponding {@link ProcessRecord} object, or null if none could be found
*/
private ProcessRecord findAppProcess(IBinder app, String reason) {
if (app == null) {
return null;
}
synchronized (this) {
final int NP = mProcessNames.getMap().size();
for (int ip=0; ip apps = mProcessNames.getMap().valueAt(ip);
final int NA = apps.size();
for (int ia=0; ia 10 * DateUtils.SECOND_IN_MILLIS) {
mWtfClusterStart = now;
mWtfClusterCount = 1;
} else {
if (mWtfClusterCount++ >= 5) return;
}
final StringBuilder sb = new StringBuilder(1024);
appendDropBoxProcessHeaders(process, processName, sb);
if (process != null) {
sb.append("Foreground: ")
.append(process.isInterestingToUserLocked() ? "Yes" : "No")
.append("\n");
}
if (activity != null) {
sb.append("Activity: ").append(activity.shortComponentName).append("\n");
}
if (parent != null && parent.app != null && parent.app.pid != process.pid) {
sb.append("Parent-Process: ").append(parent.app.processName).append("\n");
}
if (parent != null && parent != activity) {
sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
}
if (subject != null) {
sb.append("Subject: ").append(subject).append("\n");
}
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
if (Debug.isDebuggerConnected()) {
sb.append("Debugger: Connected\n");
}
sb.append("\n");
// Do the rest in a worker thread to avoid blocking the caller on I/O
// (After this point, we shouldn't access AMS internal data structures.)
Thread worker = new Thread("Error dump: " + dropboxTag) {
@Override
public void run() {
if (report != null) {
sb.append(report);
}
String setting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
int lines = Settings.Global.getInt(mContext.getContentResolver(), setting, 0);
int maxDataFileSize = DROPBOX_MAX_SIZE - sb.length()
- lines * RESERVED_BYTES_PER_LOGCAT_LINE;
if (dataFile != null && maxDataFileSize > 0) {
try {
sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
"\n\n[[TRUNCATED]]"));
} catch (IOException e) {
Slog.e(TAG, "Error reading " + dataFile, e);
}
}
if (crashInfo != null && crashInfo.stackTrace != null) {
sb.append(crashInfo.stackTrace);
}
if (lines > 0) {
sb.append("\n");
// Merge several logcat streams, and take the last N lines
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder(
"/system/bin/timeout", "-k", "15s", "10s",
"/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
.redirectErrorStream(true).start();
try { logcat.getOutputStream().close(); } catch (IOException e) {}
try { logcat.getErrorStream().close(); } catch (IOException e) {}
input = new InputStreamReader(logcat.getInputStream());
int num;
char[] buf = new char[8192];
while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
} catch (IOException e) {
Slog.e(TAG, "Error running logcat", e);
} finally {
if (input != null) try { input.close(); } catch (IOException e) {}
}
}
dbox.addText(dropboxTag, sb.toString());
}
};
if (process == null) {
// If process is null, we are being called from some internal code
// and may be about to die -- run this synchronously.
final int oldMask = StrictMode.allowThreadDiskWritesMask();
try {
worker.run();
} finally {
StrictMode.setThreadPolicyMask(oldMask);
}
} else {
worker.start();
}
}
@Override
public List getProcessesInErrorState() {
enforceNotIsolatedCaller("getProcessesInErrorState");
// assume our apps are happy - lazy create the list
List errList = null;
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
int userId = UserHandle.getUserId(Binder.getCallingUid());
synchronized (this) {
// iterate across all processes
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!allUsers && app.userId != userId) {
continue;
}
if ((app.thread != null) && (app.crashing || app.notResponding)) {
// This one's in trouble, so we'll generate a report for it
// crashes are higher priority (in case there's a crash *and* an anr)
ActivityManager.ProcessErrorStateInfo report = null;
if (app.crashing) {
report = app.crashingReport;
} else if (app.notResponding) {
report = app.notRespondingReport;
}
if (report != null) {
if (errList == null) {
errList = new ArrayList(1);
}
errList.add(report);
} else {
Slog.w(TAG, "Missing app error report, app = " + app.processName +
" crashing = " + app.crashing +
" notResponding = " + app.notResponding);
}
}
}
}
return errList;
}
static int procStateToImportance(int procState, int memAdj,
ActivityManager.RunningAppProcessInfo currApp,
int clientTargetSdk) {
int imp = ActivityManager.RunningAppProcessInfo.procStateToImportanceForTargetSdk(
procState, clientTargetSdk);
if (imp == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
currApp.lru = memAdj;
} else {
currApp.lru = 0;
}
return imp;
}
private void fillInProcMemInfo(ProcessRecord app,
ActivityManager.RunningAppProcessInfo outInfo,
int clientTargetSdk) {
outInfo.pid = app.pid;
outInfo.uid = app.info.uid;
if (mHeavyWeightProcess == app) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE;
}
if (app.persistent) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
}
if (app.activities.size() > 0) {
outInfo.flags |= ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES;
}
outInfo.lastTrimLevel = app.trimMemoryLevel;
int adj = app.curAdj;
int procState = app.curProcState;
outInfo.importance = procStateToImportance(procState, adj, outInfo, clientTargetSdk);
outInfo.importanceReasonCode = app.adjTypeCode;
outInfo.processState = app.curProcState;
}
@Override
public List getRunningAppProcesses() {
enforceNotIsolatedCaller("getRunningAppProcesses");
final int callingUid = Binder.getCallingUid();
final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
// Lazy instantiation of list
List runList = null;
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
callingUid) == PackageManager.PERMISSION_GRANTED;
final int userId = UserHandle.getUserId(callingUid);
final boolean allUids = isGetTasksAllowed(
"getRunningAppProcesses", Binder.getCallingPid(), callingUid);
synchronized (this) {
// Iterate across all processes
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if ((!allUsers && app.userId != userId)
|| (!allUids && app.uid != callingUid)) {
continue;
}
if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
// Generate process state info for running application
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
app.pid, app.getPackageList());
fillInProcMemInfo(app, currApp, clientTargetSdk);
if (app.adjSource instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
currApp.importanceReasonImportance =
ActivityManager.RunningAppProcessInfo.procStateToImportance(
app.adjSourceProcState);
} else if (app.adjSource instanceof ActivityRecord) {
ActivityRecord r = (ActivityRecord)app.adjSource;
if (r.app != null) currApp.importanceReasonPid = r.app.pid;
}
if (app.adjTarget instanceof ComponentName) {
currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
}
//Slog.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
// + " lru=" + currApp.lru);
if (runList == null) {
runList = new ArrayList<>();
}
runList.add(currApp);
}
}
}
return runList;
}
@Override
public List getRunningExternalApplications() {
enforceNotIsolatedCaller("getRunningExternalApplications");
List runningApps = getRunningAppProcesses();
List retList = new ArrayList();
if (runningApps != null && runningApps.size() > 0) {
Set extList = new HashSet();
for (ActivityManager.RunningAppProcessInfo app : runningApps) {
if (app.pkgList != null) {
for (String pkg : app.pkgList) {
extList.add(pkg);
}
}
}
IPackageManager pm = AppGlobals.getPackageManager();
for (String pkg : extList) {
try {
ApplicationInfo info = pm.getApplicationInfo(pkg, 0, UserHandle.getCallingUserId());
if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
retList.add(info);
}
} catch (RemoteException e) {
}
}
}
return retList;
}
@Override
public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outState) {
if (outState == null) {
throw new IllegalArgumentException("outState is null");
}
enforceNotIsolatedCaller("getMyMemoryState");
final int callingUid = Binder.getCallingUid();
final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
synchronized (this) {
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(Binder.getCallingPid());
}
if (proc != null) {
fillInProcMemInfo(proc, outState, clientTargetSdk);
}
}
}
@Override
public int getMemoryTrimLevel() {
enforceNotIsolatedCaller("getMyMemoryState");
synchronized (this) {
return mLastMemoryLevel;
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new ActivityManagerShellCommand(this, false)).exec(
this, in, out, err, args, callback, resultReceiver);
}
SleepToken acquireSleepToken(String tag, int displayId) {
synchronized (this) {
final SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
updateSleepIfNeededLocked();
return token;
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
/**
* Wrapper function to print out debug data filtered by specified arguments.
*/
private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
boolean dumpAll = false;
boolean dumpClient = false;
boolean dumpCheckin = false;
boolean dumpCheckinFormat = false;
boolean dumpNormalPriority = false;
boolean dumpVisibleStacksOnly = false;
boolean dumpFocusedStackOnly = false;
String dumpPackage = null;
int opti = 0;
while (opti < args.length) {
String opt = args[opti];
if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
break;
}
opti++;
if ("-a".equals(opt)) {
dumpAll = true;
} else if ("-c".equals(opt)) {
dumpClient = true;
} else if ("-v".equals(opt)) {
dumpVisibleStacksOnly = true;
} else if ("-f".equals(opt)) {
dumpFocusedStackOnly = true;
} else if ("-p".equals(opt)) {
if (opti < args.length) {
dumpPackage = args[opti];
opti++;
} else {
pw.println("Error: -p option requires package argument");
return;
}
dumpClient = true;
} else if ("--checkin".equals(opt)) {
dumpCheckin = dumpCheckinFormat = true;
} else if ("-C".equals(opt)) {
dumpCheckinFormat = true;
} else if ("--normal-priority".equals(opt)) {
dumpNormalPriority = true;
} else if ("-h".equals(opt)) {
ActivityManagerShellCommand.dumpHelp(pw, true);
return;
} else {
pw.println("Unknown argument: " + opt + "; use -h for help");
}
}
long origId = Binder.clearCallingIdentity();
if (useProto) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
String cmd = opti < args.length ? args[opti] : "";
opti++;
if ("activities".equals(cmd) || "a".equals(cmd)) {
// output proto is ActivityManagerServiceDumpActivitiesProto
synchronized (this) {
writeActivitiesToProtoLocked(proto);
}
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
// output proto is ActivityManagerServiceDumpBroadcastsProto
synchronized (this) {
writeBroadcastsToProtoLocked(proto);
}
} else if ("provider".equals(cmd)) {
String[] newArgs;
String name;
if (opti >= args.length) {
name = null;
newArgs = EMPTY_STRING_ARRAY;
} else {
name = args[opti];
opti++;
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
}
if (!dumpProviderProto(fd, pw, name, newArgs)) {
pw.println("No providers match: " + name);
pw.println("Use -h for help.");
}
} else if ("service".equals(cmd)) {
// output proto is ActivityManagerServiceDumpServicesProto
mServices.writeToProto(proto, ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
} else if ("processes".equals(cmd) || "p".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
opti++;
}
// output proto is ProcessProto
synchronized (this) {
writeProcessesToProtoLocked(proto, dumpPackage);
}
} else {
// default option, dump everything, output is ActivityManagerServiceProto
synchronized (this) {
long activityToken = proto.start(ActivityManagerServiceProto.ACTIVITIES);
writeActivitiesToProtoLocked(proto);
proto.end(activityToken);
long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
writeBroadcastsToProtoLocked(proto);
proto.end(broadcastToken);
long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
mServices.writeToProto(proto, ActivityManagerServiceDumpServicesProto.ACTIVE_SERVICES);
proto.end(serviceToken);
long processToken = proto.start(ActivityManagerServiceProto.PROCESSES);
writeProcessesToProtoLocked(proto, dumpPackage);
proto.end(processToken);
}
}
proto.flush();
Binder.restoreCallingIdentity(origId);
return;
}
int dumpAppId = getAppId(dumpPackage);
boolean more = false;
// Is the caller requesting to dump a particular piece of data?
if (opti < args.length) {
String cmd = args[opti];
opti++;
if ("activities".equals(cmd) || "a".equals(cmd)) {
synchronized (this) {
dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
}
} else if ("lastanr".equals(cmd)) {
synchronized (this) {
dumpLastANRLocked(pw);
}
} else if ("starter".equals(cmd)) {
synchronized (this) {
dumpActivityStarterLocked(pw, dumpPackage);
}
} else if ("containers".equals(cmd)) {
synchronized (this) {
dumpActivityContainersLocked(pw);
}
} else if ("recents".equals(cmd) || "r".equals(cmd)) {
synchronized (this) {
if (mRecentTasks != null) {
mRecentTasks.dump(pw, true /* dumpAll */, dumpPackage);
}
}
} else if ("binder-proxies".equals(cmd)) {
if (opti >= args.length) {
dumpBinderProxiesCounts(pw, BinderInternal.nGetBinderProxyPerUidCounts(),
"Counts of Binder Proxies held by SYSTEM");
} else {
String uid = args[opti];
opti++;
// Ensure Binder Proxy Count is as up to date as possible
System.gc();
System.runFinalization();
System.gc();
pw.println(BinderInternal.nGetBinderProxyCount(Integer.parseInt(uid)));
}
} else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
opti++;
}
synchronized (this) {
dumpBroadcastsLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("broadcast-stats".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
opti++;
}
synchronized (this) {
if (dumpCheckinFormat) {
dumpBroadcastStatsCheckinLocked(fd, pw, args, opti, dumpCheckin,
dumpPackage);
} else {
dumpBroadcastStatsLocked(fd, pw, args, opti, true, dumpPackage);
}
}
} else if ("intents".equals(cmd) || "i".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
opti++;
}
synchronized (this) {
dumpPendingIntentsLocked(fd, pw, args, opti, true, dumpPackage);
}
} else if ("processes".equals(cmd) || "p".equals(cmd)) {
if (opti < args.length) {
dumpPackage = args[opti];
opti++;
}
synchronized (this) {
dumpProcessesLocked(fd, pw, args, opti, true, dumpPackage, dumpAppId);
}
} else if ("oom".equals(cmd) || "o".equals(cmd)) {
synchronized (this) {
dumpOomLocked(fd, pw, args, opti, true);
}
} else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
synchronized (this) {
dumpPermissionsLocked(fd, pw, args, opti, true, null);
}
} else if ("provider".equals(cmd)) {
String[] newArgs;
String name;
if (opti >= args.length) {
name = null;
newArgs = EMPTY_STRING_ARRAY;
} else {
name = args[opti];
opti++;
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti);
}
if (!dumpProvider(fd, pw, name, newArgs, 0, dumpAll)) {
pw.println("No providers match: " + name);
pw.println("Use -h for help.");
}
} else if ("providers".equals(cmd) || "prov".equals(cmd)) {
synchronized (this) {
dumpProvidersLocked(fd, pw, args, opti, true, null);
}
} else if ("service".equals(cmd)) {
String[] newArgs;
String name;
if (opti >= args.length) {
name = null;
newArgs = EMPTY_STRING_ARRAY;
} else {
name = args[opti];
opti++;
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
}
if (!mServices.dumpService(fd, pw, name, newArgs, 0, dumpAll)) {
pw.println("No services match: " + name);
pw.println("Use -h for help.");
}
} else if ("package".equals(cmd)) {
String[] newArgs;
if (opti >= args.length) {
pw.println("package: no package name specified");
pw.println("Use -h for help.");
} else {
dumpPackage = args[opti];
opti++;
newArgs = new String[args.length - opti];
if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
args.length - opti);
args = newArgs;
opti = 0;
more = true;
}
} else if ("associations".equals(cmd) || "as".equals(cmd)) {
synchronized (this) {
dumpAssociationsLocked(fd, pw, args, opti, true, dumpClient, dumpPackage);
}
} else if ("settings".equals(cmd)) {
synchronized (this) {
mConstants.dump(pw);
}
} else if ("services".equals(cmd) || "s".equals(cmd)) {
if (dumpClient) {
ActiveServices.ServiceDumper dumper;
synchronized (this) {
dumper = mServices.newServiceDumperLocked(fd, pw, args, opti, true,
dumpPackage);
}
dumper.dumpWithClient();
} else {
synchronized (this) {
mServices.newServiceDumperLocked(fd, pw, args, opti, true,
dumpPackage).dumpLocked();
}
}
} else if ("locks".equals(cmd)) {
LockGuard.dump(fd, pw, args);
} else {
// Dumping a single activity?
if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacksOnly,
dumpFocusedStackOnly)) {
ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
int res = shell.exec(this, null, fd, null, args, null,
new ResultReceiver(null));
if (res < 0) {
pw.println("Bad activity command, or no activities match: " + cmd);
pw.println("Use -h for help.");
}
}
}
if (!more) {
Binder.restoreCallingIdentity(origId);
return;
}
}
// No piece of data specified, dump everything.
if (dumpCheckinFormat) {
dumpBroadcastStatsCheckinLocked(fd, pw, args, opti, dumpCheckin, dumpPackage);
} else if (dumpClient) {
ActiveServices.ServiceDumper sdumper;
synchronized (this) {
mConstants.dump(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
if (dumpAll || dumpPackage != null) {
dumpBroadcastStatsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
}
dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
sdumper = mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll,
dumpPackage);
}
sdumper.dumpWithClient();
pw.println();
synchronized (this) {
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
if (mRecentTasks != null) {
mRecentTasks.dump(pw, dumpAll, dumpPackage);
}
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpLastANRLocked(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpActivityStarterLocked(pw, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpActivityContainersLocked(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
if (mAssociations.size() > 0) {
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
}
} else {
synchronized (this) {
mConstants.dump(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
if (dumpAll || dumpPackage != null) {
dumpBroadcastStatsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
}
dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpPermissionsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
mServices.newServiceDumperLocked(fd, pw, args, opti, dumpAll, dumpPackage)
.dumpLocked();
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
if (mRecentTasks != null) {
mRecentTasks.dump(pw, dumpAll, dumpPackage);
}
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpLastANRLocked(pw);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpActivityStarterLocked(pw, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpActivityContainersLocked(pw);
// Activities section is dumped as part of the Critical priority dump. Exclude the
// section if priority is Normal.
if (!dumpNormalPriority){
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
if (mAssociations.size() > 0) {
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
}
}
Binder.restoreCallingIdentity(origId);
}
private void writeActivitiesToProtoLocked(ProtoOutputStream proto) {
// The output proto of "activity --proto activities" is ActivityManagerServiceDumpActivitiesProto
mStackSupervisor.writeToProto(proto, ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
}
private void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
pw.println(" ");
} else {
pw.println(mLastANRState);
}
}
private void dumpActivityContainersLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
mStackSupervisor.dumpChildrenNames(pw, " ");
pw.println(" ");
}
private void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
mActivityStartController.dump(pw, "", dumpPackage);
}
void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage,
"ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)");
}
void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
pw.println(header);
boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
dumpPackage);
boolean needSep = printedAnything;
boolean printed = ActivityStackSupervisor.printThisActivity(pw,
mStackSupervisor.getResumedActivityLocked(),
dumpPackage, needSep, " ResumedActivity: ");
if (printed) {
printedAnything = true;
needSep = false;
}
if (dumpPackage == null) {
if (needSep) {
pw.println();
}
printedAnything = true;
mStackSupervisor.dump(pw, " ");
}
if (!printedAnything) {
pw.println(" (nothing)");
}
}
void dumpAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) {
pw.println("ACTIVITY MANAGER ASSOCIATIONS (dumpsys activity associations)");
int dumpUid = 0;
if (dumpPackage != null) {
IPackageManager pm = AppGlobals.getPackageManager();
try {
dumpUid = pm.getPackageUid(dumpPackage, MATCH_ANY_USER, 0);
} catch (RemoteException e) {
}
}
boolean printedAnything = false;
final long now = SystemClock.uptimeMillis();
for (int i1=0, N1=mAssociations.size(); i1>> targetComponents
= mAssociations.valueAt(i1);
for (int i2=0, N2=targetComponents.size(); i2> sourceUids
= targetComponents.valueAt(i2);
for (int i3=0, N3=sourceUids.size(); i3 sourceProcesses = sourceUids.valueAt(i3);
for (int i4=0, N4=sourceProcesses.size(); i4 0) {
dur += now - ass.mStartTime;
}
TimeUtils.formatDuration(dur, pw);
pw.print(" (");
pw.print(ass.mCount);
pw.print(" times)");
pw.print(" ");
for (int i=0; i 0) {
pw.print(" Currently active: ");
TimeUtils.formatDuration(now - ass.mStartTime, pw);
pw.println();
}
}
}
}
}
if (!printedAnything) {
pw.println(" (nothing)");
}
}
private int getAppId(String dumpPackage) {
if (dumpPackage != null) {
try {
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
dumpPackage, 0);
return UserHandle.getAppId(info.uid);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
return -1;
}
boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, SparseArray uids,
String header, boolean needSep) {
boolean printed = false;
for (int i=0; i procs = mProcessNames.getMap().valueAt(ip);
final int NA = procs.size();
for (int ia=0; ia 0) {
boolean printed = false;
for (int i=0; i 0) {
boolean printed = false;
for (int i=0; i 0) {
if (dumpUids(pw, dumpPackage, dumpAppId, mActiveUids, "UID states:", needSep)) {
needSep = true;
}
}
if (dumpAll) {
if (mValidateUids.size() > 0) {
if (dumpUids(pw, dumpPackage, dumpAppId, mValidateUids, "UID validation:",
needSep)) {
needSep = true;
}
}
}
if (mLruProcesses.size() > 0) {
if (needSep) {
pw.println();
}
pw.print(" Process LRU list (sorted by oom_adj, "); pw.print(mLruProcesses.size());
pw.print(" total, non-act at ");
pw.print(mLruProcesses.size()-mLruProcessActivityStart);
pw.print(", non-svc at ");
pw.print(mLruProcesses.size()-mLruProcessServiceStart);
pw.println("):");
dumpProcessOomList(pw, this, mLruProcesses, " ", "Proc", "PERS", false, dumpPackage);
needSep = true;
}
if (dumpAll || dumpPackage != null) {
synchronized (mPidsSelfLocked) {
boolean printed = false;
for (int i=0; i 0) {
synchronized (mPidsSelfLocked) {
boolean printed = false;
for (int i = 0; i< mImportantProcesses.size(); i++) {
ProcessRecord r = mPidsSelfLocked.get(
mImportantProcesses.valueAt(i).pid);
if (dumpPackage != null && (r == null
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
}
if (!printed) {
if (needSep) pw.println();
needSep = true;
pw.println(" Foreground Processes:");
printed = true;
}
pw.print(" PID #"); pw.print(mImportantProcesses.keyAt(i));
pw.print(": "); pw.println(mImportantProcesses.valueAt(i));
}
}
}
if (mPersistentStartingProcesses.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" Persisent processes that are starting:");
dumpProcessList(pw, this, mPersistentStartingProcesses, " ",
"Starting Norm", "Restarting PERS", dumpPackage);
}
if (mRemovedProcesses.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" Processes that are being removed:");
dumpProcessList(pw, this, mRemovedProcesses, " ",
"Removed Norm", "Removed PERS", dumpPackage);
}
if (mProcessesOnHold.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" Processes that are on old until the system is ready:");
dumpProcessList(pw, this, mProcessesOnHold, " ",
"OnHold Norm", "OnHold PERS", dumpPackage);
}
needSep = dumpProcessesToGc(pw, needSep, dumpPackage);
needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage);
if (dumpPackage == null) {
pw.println();
needSep = false;
mUserController.dump(pw, dumpAll);
}
if (mHomeProcess != null && (dumpPackage == null
|| mHomeProcess.pkgList.containsKey(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
pw.println(" mHomeProcess: " + mHomeProcess);
}
if (mPreviousProcess != null && (dumpPackage == null
|| mPreviousProcess.pkgList.containsKey(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
pw.println(" mPreviousProcess: " + mPreviousProcess);
}
if (dumpAll && (mPreviousProcess == null || dumpPackage == null
|| mPreviousProcess.pkgList.containsKey(dumpPackage))) {
StringBuilder sb = new StringBuilder(128);
sb.append(" mPreviousProcessVisibleTime: ");
TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
pw.println(sb);
}
if (mHeavyWeightProcess != null && (dumpPackage == null
|| mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) {
if (needSep) {
pw.println();
needSep = false;
}
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
}
if (dumpAll && mPendingStarts.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" mPendingStarts: ");
for (int i = 0, len = mPendingStarts.size(); i < len; ++i ) {
pw.println(" " + mPendingStarts.keyAt(i) + ": " + mPendingStarts.valueAt(i));
}
}
if (dumpPackage == null) {
pw.println(" mGlobalConfiguration: " + getGlobalConfiguration());
mStackSupervisor.dumpDisplayConfigs(pw, " ");
}
if (dumpAll) {
if (dumpPackage == null) {
pw.println(" mConfigWillChange: " + getFocusedStack().mConfigWillChange);
}
if (mCompatModePackages.getPackages().size() > 0) {
boolean printed = false;
for (Map.Entry entry
: mCompatModePackages.getPackages().entrySet()) {
String pkg = entry.getKey();
int mode = entry.getValue();
if (dumpPackage != null && !dumpPackage.equals(pkg)) {
continue;
}
if (!printed) {
pw.println(" mScreenCompatPackages:");
printed = true;
}
pw.print(" "); pw.print(pkg); pw.print(": ");
pw.print(mode); pw.println();
}
}
final int NI = mUidObservers.getRegisteredCallbackCount();
boolean printed = false;
for (int i=0; i 0) {
pw.println(" Mem watch processes:");
final ArrayMap>> procs
= mMemWatchProcesses.getMap();
for (int i=0; i> uids = procs.valueAt(i);
for (int j=0; j val = uids.valueAt(j);
sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
if (val.second != null) {
sb.append(", report to ").append(val.second);
}
pw.println(sb.toString());
}
}
pw.print(" mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName);
pw.print(" mMemWatchDumpFile="); pw.println(mMemWatchDumpFile);
pw.print(" mMemWatchDumpPid="); pw.print(mMemWatchDumpPid);
pw.print(" mMemWatchDumpUid="); pw.println(mMemWatchDumpUid);
}
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
if (needSep) {
pw.println();
needSep = false;
}
pw.println(" mTrackAllocationApp=" + mTrackAllocationApp);
}
}
if (mProfileApp != null || mProfileProc != null || (mProfilerInfo != null &&
(mProfilerInfo.profileFile != null || mProfilerInfo.profileFd != null))) {
if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
if (needSep) {
pw.println();
needSep = false;
}
pw.println(" mProfileApp=" + mProfileApp + " mProfileProc=" + mProfileProc);
if (mProfilerInfo != null) {
pw.println(" mProfileFile=" + mProfilerInfo.profileFile + " mProfileFd=" +
mProfilerInfo.profileFd);
pw.println(" mSamplingInterval=" + mProfilerInfo.samplingInterval +
" mAutoStopProfiler=" + mProfilerInfo.autoStopProfiler +
" mStreamingOutput=" + mProfilerInfo.streamingOutput);
pw.println(" mProfileType=" + mProfileType);
}
}
}
if (mNativeDebuggingApp != null) {
if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
if (needSep) {
pw.println();
needSep = false;
}
pw.println(" mNativeDebuggingApp=" + mNativeDebuggingApp);
}
}
if (mAllowAppSwitchUids.size() > 0) {
boolean printed = false;
for (int i = 0; i < mAllowAppSwitchUids.size(); i++) {
ArrayMap types = mAllowAppSwitchUids.valueAt(i);
for (int j = 0; j < types.size(); j++) {
if (dumpPackage == null ||
UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) {
if (needSep) {
pw.println();
needSep = false;
}
if (!printed) {
pw.println(" mAllowAppSwitchUids:");
printed = true;
}
pw.print(" User ");
pw.print(mAllowAppSwitchUids.keyAt(i));
pw.print(": Type ");
pw.print(types.keyAt(j));
pw.print(" = ");
UserHandle.formatUid(pw, types.valueAt(j).intValue());
pw.println();
}
}
}
}
if (dumpPackage == null) {
if (mAlwaysFinishActivities) {
pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities);
}
if (mController != null) {
pw.println(" mController=" + mController
+ " mControllerIsAMonkey=" + mControllerIsAMonkey);
}
if (dumpAll) {
pw.println(" Total persistent processes: " + numPers);
pw.println(" mProcessesReady=" + mProcessesReady
+ " mSystemReady=" + mSystemReady
+ " mBooted=" + mBooted
+ " mFactoryTest=" + mFactoryTest);
pw.println(" mBooting=" + mBooting
+ " mCallFinishBooting=" + mCallFinishBooting
+ " mBootAnimationComplete=" + mBootAnimationComplete);
pw.print(" mLastPowerCheckUptime=");
TimeUtils.formatDuration(mLastPowerCheckUptime, pw);
pw.println("");
pw.println(" mGoingToSleep=" + mStackSupervisor.mGoingToSleep);
pw.println(" mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity);
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
pw.println(" mNumNonCachedProcs=" + mNumNonCachedProcs
+ " (" + mLruProcesses.size() + " total)"
+ " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs
+ " mNumServiceProcs=" + mNumServiceProcs
+ " mNewNumServiceProcs=" + mNewNumServiceProcs);
pw.println(" mAllowLowerMemLevel=" + mAllowLowerMemLevel
+ " mLastMemoryLevel=" + mLastMemoryLevel
+ " mLastNumProcesses=" + mLastNumProcesses);
long now = SystemClock.uptimeMillis();
pw.print(" mLastIdleTime=");
TimeUtils.formatDuration(now, mLastIdleTime, pw);
pw.print(" mLowRamSinceLastIdle=");
TimeUtils.formatDuration(getLowRamTimeSinceIdle(now), pw);
pw.println();
pw.println();
pw.print(" mUidChangeDispatchCount=");
pw.print(mUidChangeDispatchCount);
pw.println();
pw.println(" Slow UID dispatches:");
final int N = mUidObservers.beginBroadcast();
for (int i = 0; i < N; i++) {
UidObserverRegistration r =
(UidObserverRegistration) mUidObservers.getBroadcastCookie(i);
pw.print(" ");
pw.print(mUidObservers.getBroadcastItem(i).getClass().getTypeName());
pw.print(": ");
pw.print(r.mSlowDispatchCount);
pw.print(" / Max ");
pw.print(r.mMaxDispatchTime);
pw.println("ms");
}
mUidObservers.finishBroadcast();
pw.println();
pw.println(" ServiceManager statistics:");
ServiceManager.sStatLogger.dump(pw, " ");
pw.println();
}
}
pw.println(" mForceBackgroundCheck=" + mForceBackgroundCheck);
}
@GuardedBy("this")
void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
int numPers = 0;
final int NP = mProcessNames.getMap().size();
for (int ip=0; ip procs = mProcessNames.getMap().valueAt(ip);
final int NA = procs.size();
for (int ia = 0; ia 0) {
long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS);
int total = mLruProcesses.size();
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.SIZE, total);
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_ACT_AT, total-mLruProcessActivityStart);
proto.write(ActivityManagerServiceDumpProcessesProto.LruProcesses.NON_SVC_AT, total-mLruProcessServiceStart);
writeProcessOomListToProto(proto, ActivityManagerServiceDumpProcessesProto.LruProcesses.LIST, this,
mLruProcesses,false, dumpPackage);
proto.end(lruToken);
}
if (dumpPackage != null) {
synchronized (mPidsSelfLocked) {
for (int i=0; i 0) {
synchronized (mPidsSelfLocked) {
for (int i=0; i entry : mCompatModePackages.getPackages().entrySet()) {
String pkg = entry.getKey();
int mode = entry.getValue();
if (dumpPackage == null || dumpPackage.equals(pkg)) {
long compatToken = proto.start(ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES);
proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE, pkg);
proto.write(ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE, mode);
proto.end(compatToken);
}
}
final int NI = mUidObservers.getRegisteredCallbackCount();
for (int i=0; i 0) {
for (int i=0; i < mPendingTempWhitelist.size(); i++) {
mPendingTempWhitelist.valueAt(i).writeToProto(proto,
ActivityManagerServiceDumpProcessesProto.PENDING_TEMP_WHITELIST);
}
}
if (dumpPackage == null) {
final long sleepToken = proto.start(ActivityManagerServiceDumpProcessesProto.SLEEP_STATUS);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.WAKEFULNESS,
PowerManagerInternal.wakefulnessToProtoEnum(mWakefulness));
for (SleepToken st : mStackSupervisor.mSleepTokens) {
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString());
}
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEPING, mSleeping);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SHUTTING_DOWN, mShuttingDown);
proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.TEST_PSS_MODE, mTestPssMode);
proto.end(sleepToken);
if (mRunningVoice != null) {
final long vrToken = proto.start(ActivityManagerServiceDumpProcessesProto.RUNNING_VOICE);
proto.write(ActivityManagerServiceDumpProcessesProto.Voice.SESSION, mRunningVoice.toString());
mVoiceWakeLock.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.Voice.WAKELOCK);
proto.end(vrToken);
}
mVrController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VR_CONTROLLER);
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
if (dumpPackage == null || dumpPackage.equals(mDebugApp)
|| dumpPackage.equals(mOrigDebugApp)) {
final long debugAppToken = proto.start(ActivityManagerServiceDumpProcessesProto.DEBUG);
proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.DEBUG_APP, mDebugApp);
proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.ORIG_DEBUG_APP, mOrigDebugApp);
proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.DEBUG_TRANSIENT, mDebugTransient);
proto.write(ActivityManagerServiceDumpProcessesProto.DebugApp.ORIG_WAIT_FOR_DEBUGGER, mOrigWaitForDebugger);
proto.end(debugAppToken);
}
}
if (mCurAppTimeTracker != null) {
mCurAppTimeTracker.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.CURRENT_TRACKER, true);
}
if (mMemWatchProcesses.getMap().size() > 0) {
final long token = proto.start(ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
ArrayMap>> procs = mMemWatchProcesses.getMap();
for (int i=0; i> uids = procs.valueAt(i);
final long ptoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME, proc);
for (int j=0; j val = uids.valueAt(j);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.SIZE,
DebugUtils.sizeValueToString(val.first, new StringBuilder()));
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
proto.end(utoken);
}
proto.end(ptoken);
}
final long dtoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME, mMemWatchDumpProcName);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.FILE, mMemWatchDumpFile);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID, mMemWatchDumpPid);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID, mMemWatchDumpUid);
proto.end(dtoken);
proto.end(token);
}
if (mTrackAllocationApp != null) {
if (dumpPackage == null || dumpPackage.equals(mTrackAllocationApp)) {
proto.write(ActivityManagerServiceDumpProcessesProto.TRACK_ALLOCATION_APP, mTrackAllocationApp);
}
}
if (mProfileApp != null || mProfileProc != null || (mProfilerInfo != null &&
(mProfilerInfo.profileFile != null || mProfilerInfo.profileFd != null))) {
if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME, mProfileApp);
mProfileProc.writeToProto(proto,ActivityManagerServiceDumpProcessesProto.Profile.PROC);
if (mProfilerInfo != null) {
mProfilerInfo.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.Profile.INFO);
proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE, mProfileType);
}
proto.end(token);
}
}
if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
proto.write(ActivityManagerServiceDumpProcessesProto.NATIVE_DEBUGGING_APP, mNativeDebuggingApp);
}
if (dumpPackage == null) {
proto.write(ActivityManagerServiceDumpProcessesProto.ALWAYS_FINISH_ACTIVITIES, mAlwaysFinishActivities);
if (mController != null) {
final long token = proto.start(ActivityManagerServiceDumpProcessesProto.CONTROLLER);
proto.write(ActivityManagerServiceDumpProcessesProto.Controller.CONTROLLER, mController.toString());
proto.write(ActivityManagerServiceDumpProcessesProto.Controller.IS_A_MONKEY, mControllerIsAMonkey);
proto.end(token);
}
proto.write(ActivityManagerServiceDumpProcessesProto.TOTAL_PERSISTENT_PROCS, numPers);
proto.write(ActivityManagerServiceDumpProcessesProto.PROCESSES_READY, mProcessesReady);
proto.write(ActivityManagerServiceDumpProcessesProto.SYSTEM_READY, mSystemReady);
proto.write(ActivityManagerServiceDumpProcessesProto.BOOTED, mBooted);
proto.write(ActivityManagerServiceDumpProcessesProto.FACTORY_TEST, mFactoryTest);
proto.write(ActivityManagerServiceDumpProcessesProto.BOOTING, mBooting);
proto.write(ActivityManagerServiceDumpProcessesProto.CALL_FINISH_BOOTING, mCallFinishBooting);
proto.write(ActivityManagerServiceDumpProcessesProto.BOOT_ANIMATION_COMPLETE, mBootAnimationComplete);
proto.write(ActivityManagerServiceDumpProcessesProto.LAST_POWER_CHECK_UPTIME_MS, mLastPowerCheckUptime);
mStackSupervisor.mGoingToSleep.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GOING_TO_SLEEP);
mStackSupervisor.mLaunchingActivity.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.LAUNCHING_ACTIVITY);
proto.write(ActivityManagerServiceDumpProcessesProto.ADJ_SEQ, mAdjSeq);
proto.write(ActivityManagerServiceDumpProcessesProto.LRU_SEQ, mLruSeq);
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_NON_CACHED_PROCS, mNumNonCachedProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NUM_SERVICE_PROCS, mNumServiceProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.NEW_NUM_SERVICE_PROCS, mNewNumServiceProcs);
proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL, mAllowLowerMemLevel);
proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
long now = SystemClock.uptimeMillis();
ProtoUtils.toDuration(proto, ActivityManagerServiceDumpProcessesProto.LAST_IDLE_TIME, mLastIdleTime, now);
proto.write(ActivityManagerServiceDumpProcessesProto.LOW_RAM_SINCE_LAST_IDLE_MS, getLowRamTimeSinceIdle(now));
}
}
void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
if (mProcessesToGc.size() > 0) {
long now = SystemClock.uptimeMillis();
for (int i=0; i 0) {
boolean printed = false;
long now = SystemClock.uptimeMillis();
for (int i=0; i= 0) {
pw.print(' ');
if (adj < 10) pw.print(' ');
} else {
if (adj > -10) pw.print(' ');
}
pw.print(adj);
pw.print(": ");
pw.print(name);
pw.print(" (");
pw.print(stringifySize(mProcessList.getMemLevel(adj), 1024));
pw.println(")");
}
boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll) {
boolean needSep = false;
if (mLruProcesses.size() > 0) {
if (needSep) pw.println();
needSep = true;
pw.println(" OOM levels:");
printOomLevel(pw, "SYSTEM_ADJ", ProcessList.SYSTEM_ADJ);
printOomLevel(pw, "PERSISTENT_PROC_ADJ", ProcessList.PERSISTENT_PROC_ADJ);
printOomLevel(pw, "PERSISTENT_SERVICE_ADJ", ProcessList.PERSISTENT_SERVICE_ADJ);
printOomLevel(pw, "FOREGROUND_APP_ADJ", ProcessList.FOREGROUND_APP_ADJ);
printOomLevel(pw, "VISIBLE_APP_ADJ", ProcessList.VISIBLE_APP_ADJ);
printOomLevel(pw, "PERCEPTIBLE_APP_ADJ", ProcessList.PERCEPTIBLE_APP_ADJ);
printOomLevel(pw, "BACKUP_APP_ADJ", ProcessList.BACKUP_APP_ADJ);
printOomLevel(pw, "HEAVY_WEIGHT_APP_ADJ", ProcessList.HEAVY_WEIGHT_APP_ADJ);
printOomLevel(pw, "SERVICE_ADJ", ProcessList.SERVICE_ADJ);
printOomLevel(pw, "HOME_APP_ADJ", ProcessList.HOME_APP_ADJ);
printOomLevel(pw, "PREVIOUS_APP_ADJ", ProcessList.PREVIOUS_APP_ADJ);
printOomLevel(pw, "SERVICE_B_ADJ", ProcessList.SERVICE_B_ADJ);
printOomLevel(pw, "CACHED_APP_MIN_ADJ", ProcessList.CACHED_APP_MIN_ADJ);
printOomLevel(pw, "CACHED_APP_MAX_ADJ", ProcessList.CACHED_APP_MAX_ADJ);
if (needSep) pw.println();
pw.print(" Process OOM control ("); pw.print(mLruProcesses.size());
pw.print(" total, non-act at ");
pw.print(mLruProcesses.size()-mLruProcessActivityStart);
pw.print(", non-svc at ");
pw.print(mLruProcesses.size()-mLruProcessServiceStart);
pw.println("):");
dumpProcessOomList(pw, this, mLruProcesses, " ", "Proc", "PERS", true, null);
needSep = true;
}
dumpProcessesToGc(pw, needSep, null);
pw.println();
pw.println(" mHomeProcess: " + mHomeProcess);
pw.println(" mPreviousProcess: " + mPreviousProcess);
if (mHeavyWeightProcess != null) {
pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
}
return true;
}
/**
* There are three ways to call this:
* - no provider specified: dump all the providers
* - a flattened component name that matched an existing provider was specified as the
* first arg: dump that one provider
* - the first arg isn't the flattened component name of an existing provider:
* dump all providers whose component contains the first arg as a substring
*/
protected boolean dumpProvider(FileDescriptor fd, PrintWriter pw, String name, String[] args,
int opti, boolean dumpAll) {
return mProviderMap.dumpProvider(fd, pw, name, args, opti, dumpAll);
}
/**
* Similar to the dumpProvider, but only dumps the first matching provider.
* The provider is responsible for dumping as proto.
*/
protected boolean dumpProviderProto(FileDescriptor fd, PrintWriter pw, String name,
String[] args) {
return mProviderMap.dumpProviderProto(fd, pw, name, args);
}
static class ItemMatcher {
ArrayList components;
ArrayList strings;
ArrayList objects;
boolean all;
ItemMatcher() {
all = true;
}
void build(String name) {
ComponentName componentName = ComponentName.unflattenFromString(name);
if (componentName != null) {
if (components == null) {
components = new ArrayList();
}
components.add(componentName);
all = false;
} else {
int objectId = 0;
// Not a '/' separated full component name; maybe an object ID?
try {
objectId = Integer.parseInt(name, 16);
if (objects == null) {
objects = new ArrayList();
}
objects.add(objectId);
all = false;
} catch (RuntimeException e) {
// Not an integer; just do string match.
if (strings == null) {
strings = new ArrayList();
}
strings.add(name);
all = false;
}
}
}
int build(String[] args, int opti) {
for (; opti activities;
synchronized (this) {
activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
dumpFocusedStackOnly);
}
if (activities.size() <= 0) {
return false;
}
String[] newArgs = new String[args.length - opti];
System.arraycopy(args, opti, newArgs, 0, args.length - opti);
TaskRecord lastTask = null;
boolean needSep = false;
for (int i=activities.size()-1; i>=0; i--) {
ActivityRecord r = activities.get(i);
if (needSep) {
pw.println();
}
needSep = true;
synchronized (this) {
final TaskRecord task = r.getTask();
if (lastTask != task) {
lastTask = task;
pw.print("TASK "); pw.print(lastTask.affinity);
pw.print(" id="); pw.print(lastTask.taskId);
pw.print(" userId="); pw.println(lastTask.userId);
if (dumpAll) {
lastTask.dump(pw, " ");
}
}
}
dumpActivity(" ", fd, pw, activities.get(i), newArgs, dumpAll);
}
return true;
}
/**
* Invokes IApplicationThread.dumpActivity() on the thread of the specified activity if
* there is a thread associated with the activity.
*/
private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw,
final ActivityRecord r, String[] args, boolean dumpAll) {
String innerPrefix = prefix + " ";
synchronized (this) {
pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName);
pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r)));
pw.print(" pid=");
if (r.app != null) pw.println(r.app.pid);
else pw.println("(not running)");
if (dumpAll) {
r.dump(pw, innerPrefix);
}
}
if (r.app != null && r.app.thread != null) {
// flush anything that is already in the PrintWriter since the thread is going
// to write to the file descriptor directly
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
r.app.thread.dumpActivity(tp.getWriteFd(),
r.appToken, innerPrefix, args);
tp.go(fd);
} finally {
tp.kill();
}
} catch (IOException e) {
pw.println(innerPrefix + "Failure while dumping the activity: " + e);
} catch (RemoteException e) {
pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
}
}
}
void writeBroadcastsToProtoLocked(ProtoOutputStream proto) {
if (mRegisteredReceivers.size() > 0) {
Iterator it = mRegisteredReceivers.values().iterator();
while (it.hasNext()) {
ReceiverList r = (ReceiverList)it.next();
r.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_LIST);
}
}
mReceiverResolver.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_RESOLVER);
for (BroadcastQueue q : mBroadcastQueues) {
q.writeToProto(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
}
for (int user=0; user> ent
: mStickyBroadcasts.valueAt(user).entrySet()) {
long actionToken = proto.start(StickyBroadcastProto.ACTIONS);
proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey());
for (Intent intent : ent.getValue()) {
intent.writeToProto(proto, StickyBroadcastProto.StickyAction.INTENTS,
false, true, true, false);
}
proto.end(actionToken);
}
proto.end(token);
}
long handlerToken = proto.start(ActivityManagerServiceDumpBroadcastsProto.HANDLER);
proto.write(ActivityManagerServiceDumpBroadcastsProto.MainHandler.HANDLER, mHandler.toString());
mHandler.getLooper().writeToProto(proto,
ActivityManagerServiceDumpBroadcastsProto.MainHandler.LOOPER);
proto.end(handlerToken);
}
void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
boolean onlyHistory = false;
boolean printedAnything = false;
if ("history".equals(dumpPackage)) {
if (opti < args.length && "-s".equals(args[opti])) {
dumpAll = false;
}
onlyHistory = true;
dumpPackage = null;
}
pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)");
if (!onlyHistory && dumpAll) {
if (mRegisteredReceivers.size() > 0) {
boolean printed = false;
Iterator it = mRegisteredReceivers.values().iterator();
while (it.hasNext()) {
ReceiverList r = (ReceiverList)it.next();
if (dumpPackage != null && (r.app == null ||
!dumpPackage.equals(r.app.info.packageName))) {
continue;
}
if (!printed) {
pw.println(" Registered Receivers:");
needSep = true;
printed = true;
printedAnything = true;
}
pw.print(" * "); pw.println(r);
r.dump(pw, " ");
}
}
if (mReceiverResolver.dump(pw, needSep ?
"\n Receiver Resolver Table:" : " Receiver Resolver Table:",
" ", dumpPackage, false, false)) {
needSep = true;
printedAnything = true;
}
}
for (BroadcastQueue q : mBroadcastQueues) {
needSep = q.dumpLocked(fd, pw, args, opti, dumpAll, dumpPackage, needSep);
printedAnything |= needSep;
}
needSep = true;
if (!onlyHistory && mStickyBroadcasts != null && dumpPackage == null) {
for (int user=0; user> ent
: mStickyBroadcasts.valueAt(user).entrySet()) {
pw.print(" * Sticky action "); pw.print(ent.getKey());
if (dumpAll) {
pw.println(":");
ArrayList intents = ent.getValue();
final int N = intents.size();
for (int i=0; i 0) {
boolean printed = false;
for (int i=mLaunchingProviders.size()-1; i>=0; i--) {
ContentProviderRecord r = mLaunchingProviders.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) {
continue;
}
if (!printed) {
if (needSep) pw.println();
needSep = true;
pw.println(" Launching content providers:");
printed = true;
printedAnything = true;
}
pw.print(" Launching #"); pw.print(i); pw.print(": ");
pw.println(r);
}
}
if (!printedAnything) {
pw.println(" (nothing)");
}
}
@GuardedBy("this")
void dumpPermissionsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
boolean printedAnything = false;
pw.println("ACTIVITY MANAGER URI PERMISSIONS (dumpsys activity permissions)");
if (mGrantedUriPermissions.size() > 0) {
boolean printed = false;
int dumpUid = -2;
if (dumpPackage != null) {
try {
dumpUid = mContext.getPackageManager().getPackageUidAsUser(dumpPackage,
MATCH_ANY_USER, 0);
} catch (NameNotFoundException e) {
dumpUid = -1;
}
}
for (int i=0; i= -1 && UserHandle.getAppId(uid) != dumpUid) {
continue;
}
final ArrayMap perms = mGrantedUriPermissions.valueAt(i);
if (!printed) {
if (needSep) pw.println();
needSep = true;
pw.println(" Granted Uri Permissions:");
printed = true;
printedAnything = true;
}
pw.print(" * UID "); pw.print(uid); pw.println(" holds:");
for (UriPermission perm : perms.values()) {
pw.print(" "); pw.println(perm);
if (dumpAll) {
perm.dump(pw, " ");
}
}
}
}
if (!printedAnything) {
pw.println(" (nothing)");
}
}
void dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean printed = false;
pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
if (mIntentSenderRecords.size() > 0) {
// Organize these by package name, so they are easier to read.
final ArrayMap> byPackage = new ArrayMap<>();
final ArrayList> weakRefs = new ArrayList<>();
final Iterator> it
= mIntentSenderRecords.values().iterator();
while (it.hasNext()) {
WeakReference ref = it.next();
PendingIntentRecord rec = ref != null ? ref.get() : null;
if (rec == null) {
weakRefs.add(ref);
continue;
}
if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) {
continue;
}
ArrayList list = byPackage.get(rec.key.packageName);
if (list == null) {
list = new ArrayList<>();
byPackage.put(rec.key.packageName, list);
}
list.add(rec);
}
for (int i = 0; i < byPackage.size(); i++) {
ArrayList intents = byPackage.valueAt(i);
printed = true;
pw.print(" * "); pw.print(byPackage.keyAt(i));
pw.print(": "); pw.print(intents.size()); pw.println(" items");
for (int j = 0; j < intents.size(); j++) {
pw.print(" #"); pw.print(j); pw.print(": "); pw.println(intents.get(j));
if (dumpAll) {
intents.get(j).dump(pw, " ");
}
}
}
if (weakRefs.size() > 0) {
printed = true;
pw.println(" * WEAK REFS:");
for (int i = 0; i < weakRefs.size(); i++) {
pw.print(" #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i));
}
}
}
if (!printed) {
pw.println(" (nothing)");
}
}
private static final int dumpProcessList(PrintWriter pw,
ActivityManagerService service, List list,
String prefix, String normalLabel, String persistentLabel,
String dumpPackage) {
int numPers = 0;
final int N = list.size()-1;
for (int i=N; i>=0; i--) {
ProcessRecord r = (ProcessRecord)list.get(i);
if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) {
continue;
}
pw.println(String.format("%s%s #%2d: %s",
prefix, (r.persistent ? persistentLabel : normalLabel),
i, r.toString()));
if (r.persistent) {
numPers++;
}
}
return numPers;
}
private static final ArrayList>
sortProcessOomList(List origList, String dumpPackage) {
ArrayList> list
= new ArrayList>(origList.size());
for (int i=0; i(origList.get(i), i));
}
Comparator> comparator
= new Comparator>() {
@Override
public int compare(Pair object1,
Pair object2) {
if (object1.first.setAdj != object2.first.setAdj) {
return object1.first.setAdj > object2.first.setAdj ? -1 : 1;
}
if (object1.first.setProcState != object2.first.setProcState) {
return object1.first.setProcState > object2.first.setProcState ? -1 : 1;
}
if (object1.second.intValue() != object2.second.intValue()) {
return object1.second.intValue() > object2.second.intValue() ? -1 : 1;
}
return 0;
}
};
Collections.sort(list, comparator);
return list;
}
private static final boolean writeProcessOomListToProto(ProtoOutputStream proto, long fieldId,
ActivityManagerService service, List origList,
boolean inclDetails, String dumpPackage) {
ArrayList> list = sortProcessOomList(origList, dumpPackage);
if (list.isEmpty()) return false;
final long curUptime = SystemClock.uptimeMillis();
for (int i = list.size() - 1; i >= 0; i--) {
ProcessRecord r = list.get(i).first;
long token = proto.start(fieldId);
String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
proto.write(ProcessOomProto.PERSISTENT, r.persistent);
proto.write(ProcessOomProto.NUM, (origList.size()-1)-list.get(i).second);
proto.write(ProcessOomProto.OOM_ADJ, oomAdj);
int schedGroup = ProcessOomProto.SCHED_GROUP_UNKNOWN;
switch (r.setSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
schedGroup = ProcessOomProto.SCHED_GROUP_BACKGROUND;
break;
case ProcessList.SCHED_GROUP_DEFAULT:
schedGroup = ProcessOomProto.SCHED_GROUP_DEFAULT;
break;
case ProcessList.SCHED_GROUP_TOP_APP:
schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP;
break;
case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
schedGroup = ProcessOomProto.SCHED_GROUP_TOP_APP_BOUND;
break;
}
if (schedGroup != ProcessOomProto.SCHED_GROUP_UNKNOWN) {
proto.write(ProcessOomProto.SCHED_GROUP, schedGroup);
}
if (r.foregroundActivities) {
proto.write(ProcessOomProto.ACTIVITIES, true);
} else if (r.foregroundServices) {
proto.write(ProcessOomProto.SERVICES, true);
}
proto.write(ProcessOomProto.STATE, ProcessList.makeProcStateProtoEnum(r.curProcState));
proto.write(ProcessOomProto.TRIM_MEMORY_LEVEL, r.trimMemoryLevel);
r.writeToProto(proto, ProcessOomProto.PROC);
proto.write(ProcessOomProto.ADJ_TYPE, r.adjType);
if (r.adjSource != null || r.adjTarget != null) {
if (r.adjTarget instanceof ComponentName) {
ComponentName cn = (ComponentName) r.adjTarget;
cn.writeToProto(proto, ProcessOomProto.ADJ_TARGET_COMPONENT_NAME);
} else if (r.adjTarget != null) {
proto.write(ProcessOomProto.ADJ_TARGET_OBJECT, r.adjTarget.toString());
}
if (r.adjSource instanceof ProcessRecord) {
ProcessRecord p = (ProcessRecord) r.adjSource;
p.writeToProto(proto, ProcessOomProto.ADJ_SOURCE_PROC);
} else if (r.adjSource != null) {
proto.write(ProcessOomProto.ADJ_SOURCE_OBJECT, r.adjSource.toString());
}
}
if (inclDetails) {
long detailToken = proto.start(ProcessOomProto.DETAIL);
proto.write(ProcessOomProto.Detail.MAX_ADJ, r.maxAdj);
proto.write(ProcessOomProto.Detail.CUR_RAW_ADJ, r.curRawAdj);
proto.write(ProcessOomProto.Detail.SET_RAW_ADJ, r.setRawAdj);
proto.write(ProcessOomProto.Detail.CUR_ADJ, r.curAdj);
proto.write(ProcessOomProto.Detail.SET_ADJ, r.setAdj);
proto.write(ProcessOomProto.Detail.CURRENT_STATE,
ProcessList.makeProcStateProtoEnum(r.curProcState));
proto.write(ProcessOomProto.Detail.SET_STATE,
ProcessList.makeProcStateProtoEnum(r.setProcState));
proto.write(ProcessOomProto.Detail.LAST_PSS, DebugUtils.sizeValueToString(
r.lastPss*1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.LAST_SWAP_PSS, DebugUtils.sizeValueToString(
r.lastSwapPss*1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.LAST_CACHED_PSS, DebugUtils.sizeValueToString(
r.lastCachedPss*1024, new StringBuilder()));
proto.write(ProcessOomProto.Detail.CACHED, r.cached);
proto.write(ProcessOomProto.Detail.EMPTY, r.empty);
proto.write(ProcessOomProto.Detail.HAS_ABOVE_CLIENT, r.hasAboveClient);
if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
if (r.lastCpuTime != 0) {
long uptimeSince = curUptime - service.mLastPowerCheckUptime;
long timeUsed = r.curCpuTime - r.lastCpuTime;
long cpuTimeToken = proto.start(ProcessOomProto.Detail.SERVICE_RUN_TIME);
proto.write(ProcessOomProto.Detail.CpuRunTime.OVER_MS, uptimeSince);
proto.write(ProcessOomProto.Detail.CpuRunTime.USED_MS, timeUsed);
proto.write(ProcessOomProto.Detail.CpuRunTime.ULTILIZATION,
(100.0*timeUsed)/uptimeSince);
proto.end(cpuTimeToken);
}
}
proto.end(detailToken);
}
proto.end(token);
}
return true;
}
private static final boolean dumpProcessOomList(PrintWriter pw,
ActivityManagerService service, List origList,
String prefix, String normalLabel, String persistentLabel,
boolean inclDetails, String dumpPackage) {
ArrayList> list = sortProcessOomList(origList, dumpPackage);
if (list.isEmpty()) return false;
final long curUptime = SystemClock.uptimeMillis();
final long uptimeSince = curUptime - service.mLastPowerCheckUptime;
for (int i=list.size()-1; i>=0; i--) {
ProcessRecord r = list.get(i).first;
String oomAdj = ProcessList.makeOomAdjString(r.setAdj);
char schedGroup;
switch (r.setSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
schedGroup = 'B';
break;
case ProcessList.SCHED_GROUP_DEFAULT:
schedGroup = 'F';
break;
case ProcessList.SCHED_GROUP_TOP_APP:
schedGroup = 'T';
break;
case ProcessList.SCHED_GROUP_RESTRICTED:
schedGroup = 'R';
break;
default:
schedGroup = '?';
break;
}
char foreground;
if (r.foregroundActivities) {
foreground = 'A';
} else if (r.foregroundServices) {
foreground = 'S';
} else {
foreground = ' ';
}
String procState = ProcessList.makeProcStateString(r.curProcState);
pw.print(prefix);
pw.print(r.persistent ? persistentLabel : normalLabel);
pw.print(" #");
int num = (origList.size()-1)-list.get(i).second;
if (num < 10) pw.print(' ');
pw.print(num);
pw.print(": ");
pw.print(oomAdj);
pw.print(' ');
pw.print(schedGroup);
pw.print('/');
pw.print(foreground);
pw.print('/');
pw.print(procState);
pw.print(" trm:");
if (r.trimMemoryLevel < 10) pw.print(' ');
pw.print(r.trimMemoryLevel);
pw.print(' ');
pw.print(r.toShortString());
pw.print(" (");
pw.print(r.adjType);
pw.println(')');
if (r.adjSource != null || r.adjTarget != null) {
pw.print(prefix);
pw.print(" ");
if (r.adjTarget instanceof ComponentName) {
pw.print(((ComponentName)r.adjTarget).flattenToShortString());
} else if (r.adjTarget != null) {
pw.print(r.adjTarget.toString());
} else {
pw.print("{null}");
}
pw.print("<=");
if (r.adjSource instanceof ProcessRecord) {
pw.print("Proc{");
pw.print(((ProcessRecord)r.adjSource).toShortString());
pw.println("}");
} else if (r.adjSource != null) {
pw.println(r.adjSource.toString());
} else {
pw.println("{null}");
}
}
if (inclDetails) {
pw.print(prefix);
pw.print(" ");
pw.print("oom: max="); pw.print(r.maxAdj);
pw.print(" curRaw="); pw.print(r.curRawAdj);
pw.print(" setRaw="); pw.print(r.setRawAdj);
pw.print(" cur="); pw.print(r.curAdj);
pw.print(" set="); pw.println(r.setAdj);
pw.print(prefix);
pw.print(" ");
pw.print("state: cur="); pw.print(ProcessList.makeProcStateString(r.curProcState));
pw.print(" set="); pw.print(ProcessList.makeProcStateString(r.setProcState));
pw.print(" lastPss="); DebugUtils.printSizeValue(pw, r.lastPss*1024);
pw.print(" lastSwapPss="); DebugUtils.printSizeValue(pw, r.lastSwapPss*1024);
pw.print(" lastCachedPss="); DebugUtils.printSizeValue(pw, r.lastCachedPss*1024);
pw.println();
pw.print(prefix);
pw.print(" ");
pw.print("cached="); pw.print(r.cached);
pw.print(" empty="); pw.print(r.empty);
pw.print(" hasAboveClient="); pw.println(r.hasAboveClient);
if (r.setProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
if (r.lastCpuTime != 0) {
long timeUsed = r.curCpuTime - r.lastCpuTime;
pw.print(prefix);
pw.print(" ");
pw.print("run cpu over ");
TimeUtils.formatDuration(uptimeSince, pw);
pw.print(" used ");
TimeUtils.formatDuration(timeUsed, pw);
pw.print(" (");
pw.print((timeUsed*100)/uptimeSince);
pw.println("%)");
}
}
}
}
return true;
}
ArrayList collectProcesses(PrintWriter pw, int start, boolean allPkgs,
String[] args) {
ArrayList procs;
synchronized (this) {
if (args != null && args.length > start
&& args[start].charAt(0) != '-') {
procs = new ArrayList();
int pid = -1;
try {
pid = Integer.parseInt(args[start]);
} catch (NumberFormatException e) {
}
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord proc = mLruProcesses.get(i);
if (proc.pid > 0 && proc.pid == pid) {
procs.add(proc);
} else if (allPkgs && proc.pkgList != null
&& proc.pkgList.containsKey(args[start])) {
procs.add(proc);
} else if (proc.processName.equals(args[start])) {
procs.add(proc);
}
}
if (procs.size() <= 0) {
return null;
}
} else {
procs = new ArrayList(mLruProcesses);
}
}
return procs;
}
final void dumpGraphicsHardwareUsage(FileDescriptor fd,
PrintWriter pw, String[] args) {
ArrayList procs = collectProcesses(pw, 0, false, args);
if (procs == null) {
pw.println("No process found for: " + args[0]);
return;
}
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
pw.println("Applications Graphics Acceleration Info:");
pw.println("Uptime: " + uptime + " Realtime: " + realtime);
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = procs.get(i);
if (r.thread != null) {
pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
r.thread.dumpGfxInfo(tp.getWriteFd(), args);
tp.go(fd);
} finally {
tp.kill();
}
} catch (IOException e) {
pw.println("Failure while dumping the app: " + r);
pw.flush();
} catch (RemoteException e) {
pw.println("Got a RemoteException while dumping the app " + r);
pw.flush();
}
}
}
}
final void dumpDbInfo(FileDescriptor fd, PrintWriter pw, String[] args) {
ArrayList procs = collectProcesses(pw, 0, false, args);
if (procs == null) {
pw.println("No process found for: " + args[0]);
return;
}
pw.println("Applications Database Info:");
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = procs.get(i);
if (r.thread != null) {
pw.println("\n** Database info for pid " + r.pid + " [" + r.processName + "] **");
pw.flush();
try {
TransferPipe tp = new TransferPipe();
try {
r.thread.dumpDbInfo(tp.getWriteFd(), args);
tp.go(fd);
} finally {
tp.kill();
}
} catch (IOException e) {
pw.println("Failure while dumping the app: " + r);
pw.flush();
} catch (RemoteException e) {
pw.println("Got a RemoteException while dumping the app " + r);
pw.flush();
}
}
}
}
final static class MemItem {
final boolean isProc;
final String label;
final String shortLabel;
final long pss;
final long swapPss;
final int id;
final boolean hasActivities;
ArrayList subitems;
public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id,
boolean _hasActivities) {
isProc = true;
label = _label;
shortLabel = _shortLabel;
pss = _pss;
swapPss = _swapPss;
id = _id;
hasActivities = _hasActivities;
}
public MemItem(String _label, String _shortLabel, long _pss, long _swapPss, int _id) {
isProc = false;
label = _label;
shortLabel = _shortLabel;
pss = _pss;
swapPss = _swapPss;
id = _id;
hasActivities = false;
}
}
private static void sortMemItems(List items) {
Collections.sort(items, new Comparator() {
@Override
public int compare(MemItem lhs, MemItem rhs) {
if (lhs.pss < rhs.pss) {
return 1;
} else if (lhs.pss > rhs.pss) {
return -1;
}
return 0;
}
});
}
static final void dumpMemItems(PrintWriter pw, String prefix, String tag,
ArrayList items, boolean sort, boolean isCompact, boolean dumpSwapPss) {
if (sort && !isCompact) {
sortMemItems(items);
}
for (int i=0; i items, boolean sort, boolean dumpSwapPss) {
if (sort) {
sortMemItems(items);
}
for (int i=0; i= 0) start++;
else start = 0;
int end = label.length();
for (int i=0; i