android核心剖析學習筆記:AMS(ActivityManagerService)內部原理和工作機制
(1)統一排程各應用程式的Activity
(2)記憶體管理
(3)程序管理
二、啟動一個Activity的方式有以下幾種:
(1)在應用程式中呼叫startActivity啟動指定的Activity
(2)在Home程式中單擊一個應用圖示,啟動新的Activity
(3)按“Back”鍵,結束當前Activity,返回到上一個Activity
(4)長按“Home”鍵,顯示出當前正在執行的程式列表,從中選擇一個啟動
這四種啟動方式的主體處理流程都會按照第一種啟動方式執行,後面三種方式只是在前端訊息處理上各有不同。
三、程序資料類ProcessRecord
該類的原始碼在~\frameworks\base\services\java\com\android\server\am路徑下。
一般情況下,一個APK檔案執行時會對應一個程序,ProcessRecord用來記錄一個程序中的相關資訊,主要包含的變數有:
(1)程序檔案資訊:與該程序對應的APK檔案的內部資訊,如
final ApplicationInfo info; // all about the first app in the process
final String processName; // name of the process
final ArrayMap<String, ProcessStats.ProcessState> pkgList
= new ArrayMap<String, ProcessStats.ProcessState>(); //儲存程序中所有APK檔案包名
(2)程序的記憶體狀態資訊:用於Linux系統的out of memory(OOM)情況的處理,當發生記憶體緊張時,Linux系統會根據程序的記憶體狀態資訊殺掉低優先順序的程序,包括的變數有
int maxAdj; // Maximum OOM adjustment for this process
int curRawAdj; // Current OOM unlimited adjustment for this process
int setRawAdj; // Last set OOM unlimited adjustment for this process
int curAdj; // Current OOM adjustment for this process
int setAdj; // Last set OOM adjustment for this process
變數中Adj的含義是調整值(adjustment)
(3)程序中包含的Activity、Provider、Service等,如下
final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
final ArraySet<ServiceRecord> services = new ArraySet<ServiceRecord>();
final ArraySet<ServiceRecord> executingServices = new ArraySet<ServiceRecord>();
final ArraySet<ConnectionRecord> connections = new ArraySet<ConnectionRecord>();
final ArraySet<ReceiverList> receivers = new ArraySet<ReceiverList>();
final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<String, ContentProviderRecord>();
final ArrayList<ContentProviderConnection> conProviders = new ArrayList<ContentProviderConnection>();
四、ActivityRecord資料類(Android 2.3以前版本叫HistoryRecord類)
ActivityManagerService使用ActivityRecord資料類來儲存每個Activity的資訊,ActivityRecord類基於IApplicationToken.Stub類,也是一個Binder,所以可以被IPC呼叫。
主要包含的變數有:
(1)環境資訊:Activity的工作環境,比如程序名稱、檔案路徑、資料路徑、圖示、主題等,這些資訊一般是固定的,比如以下變數
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String baseDir; // where activity source (resources etc) located
final String resDir; // where public activity source (public resources etc) located
final String dataDir; // where activity data should go
int theme; // resource identifier of activity's theme.
int realTheme; // actual theme resource we will use, never 0.
(2)執行狀態資料資訊:如idle、stop、finishing等,一般為boolean型別,如下
boolean haveState; // have we gotten the last activity state?
boolean stopped; // is activity pause finished?
boolean delayedResume; // not yet resumed because of stopped app switches?
boolean finishing; // activity in pending finish list?
boolean configDestroy; // need to destroy due to config change?
五、TaskRecord類
ActivityManagerService中使用任務的概念來確保Activity啟動和退出的順序。
TaskRecord中的幾個重要變數如下:
final int taskId; // 每個任務的標識.
Intent intent; // 建立該任務時對應的intent
int numActivities; //該任務中的Activity數目
final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>(); //按照出現的先後順序列出該任務中的所有Activity
六、ActivityManagerService中一些重要的與排程相關的變數
(1)記錄最近啟動的Activity,如果RAM容量較小,則記錄的最大值為10個,否則為20個,超過該值後,Ams會捨棄最早記錄的Activity
static final int MAX_RECENT_TASKS = ActivityManager.isLowRamDeviceStatic() ? 10 : 20;
(2)當Ams通知應用程式啟動(Launch)某個Activity時,如果超過10s,Ams就會放棄
static final int PROC_START_TIMEOUT = 10*1000;
(3)當Ams啟動某個客戶程序後,客戶程序必須在10s之內報告Ams自己已經啟動,否則Ams會認為指定的客戶程序不存在
static final int PROC_START_TIMEOUT = 10*1000;
(4)等待序列:
當Ams內部還沒有準備好時,如果客戶程序請求啟動某個Activity,那麼會被暫時儲存到該變數中,
final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
= new ArrayList<PendingActivityLaunch>();
(5)優先啟動,其次再停止。程序A1包含兩個Activity,啟動順序為A1->A2,當用戶請求啟動A2時,如果A1正在執行,Ams會先暫停A1,然後啟動A2,當A2啟動後再停止A1。
private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
七、startActivity()的流程
當用戶單擊某個應用圖示後,執行程式會在該圖示的onClick()事件中呼叫startActivity()方法,該方法會呼叫startActivityForResult(),在這個方法內部會呼叫Instrumentation物件的executeStartActivity()方法,每個Activity內部都有一個Instrumentation物件的引用,它就是一個管家,ActivityThread要建立或者暫停某個Activity都是通過它實現的。
流程圖如下所示:
下面附上ActivityManagerService的完整原始碼,有興趣的童鞋可以深入研究。
/*
* Copyright (C) 2006-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
import android.app.AppOpsManager;
import android.appwidget.AppWidgetManager;
import android.util.ArrayMap;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.ProcessStats;
import com.android.internal.app.ResolverActivity;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
import com.android.internal.app.ProcessMap;
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.UserManagerService;
import com.android.server.wm.AppTransition;
import com.android.server.wm.StackBox;
import com.android.server.wm.WindowManagerService;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import dalvik.system.Zygote;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackBoxInfo;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.AppGlobals;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IActivityController;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
import android.app.IThumbnailReceiver;
import android.app.IUiAutomationConnection;
import android.app.IUserSwitchObserver;
import android.app.Instrumentation;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.backup.IBackupManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.IContentProvider;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Proxy;
import android.net.ProxyProperties;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.IPermissionController;
import android.os.IRemoteCallback;
import android.os.IUserManager;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
private static final String USER_DATA_DIR = "/data/user/";
static final String TAG = "ActivityManager";
static final String TAG_MU = "ActivityManagerServiceMU";
static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG;
static final boolean DEBUG_BACKUP = localLOGV || false;
static final boolean DEBUG_BROADCAST = localLOGV || false;
static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false;
static final boolean DEBUG_BACKGROUND_BROADCAST = DEBUG_BROADCAST || false;
static final boolean DEBUG_CLEANUP = localLOGV || false;
static final boolean DEBUG_CONFIGURATION = localLOGV || false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_IMMERSIVE = localLOGV || false;
static final boolean DEBUG_MU = localLOGV || false;
static final boolean DEBUG_OOM_ADJ = localLOGV || false;
static final boolean DEBUG_PAUSE = localLOGV || false;
static final boolean DEBUG_POWER = localLOGV || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
static final boolean DEBUG_PROCESS_OBSERVERS = localLOGV || false;
static final boolean DEBUG_PROCESSES = localLOGV || false;
static final boolean DEBUG_PROVIDER = localLOGV || false;
static final boolean DEBUG_RESULTS = localLOGV || false;
static final boolean DEBUG_SERVICE = localLOGV || false;
static final boolean DEBUG_SERVICE_EXECUTING = localLOGV || false;
static final boolean DEBUG_STACK = localLOGV || false;
static final boolean DEBUG_SWITCH = localLOGV || false;
static final boolean DEBUG_TASKS = localLOGV || false;
static final boolean DEBUG_THUMBNAILS = localLOGV || false;
static final boolean DEBUG_TRANSITION = localLOGV || false;
static final boolean DEBUG_URI_PERMISSION = localLOGV || false;
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
static final boolean DEBUG_VISBILITY = localLOGV || false;
static final boolean DEBUG_PSS = localLOGV || false;
static final boolean DEBUG_LOCKSCREEN = localLOGV || false;
static final boolean VALIDATE_TOKENS = false;
static final boolean SHOW_ACTIVITY_START_TIME = true;
// Control over CPU and battery monitoring.
static final long BATTERY_STATS_TIME = 30*60*1000; // write battery stats every 30 minutes.
static final boolean MONITOR_CPU_USAGE = true;
static final long MONITOR_CPU_MIN_TIME = 5*1000; // don't sample cpu less than every 5 seconds.
static final long MONITOR_CPU_MAX_TIME = 0x0fffffff; // wait possibly forever for next cpu sample.
static final boolean MONITOR_THREAD_CPU_USAGE = false;
// The flags that are set for all calls we make to the package manager.
static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES;
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
// Maximum number of recent tasks that we can remember.
static final int MAX_RECENT_TASKS = ActivityManager.isLowRamDeviceStatic() ? 10 : 20;
// Amount of time after a call to stopAppSwitches() during which we will
// prevent further untrusted switches from happening.
static final long APP_SWITCH_DELAY_TIME = 5*1000;
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
// could take much longer than usual.
static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000;
// How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000;
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
// The minimum amount of time between successive PSS requests for a process.
static final int FULL_PSS_MIN_INTERVAL = 10*60*1000;
// The minimum amount of time between successive PSS requests for a process
// when the request is due to the memory state being lowered.
static final int FULL_PSS_LOWERED_INTERVAL = 2*60*1000;
// The rate at which we check for apps using excessive power -- 15 mins.
static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000;
// The minimum sample duration we will allow before deciding we have
// enough data on wake locks to start killing things.
static final int WAKE_LOCK_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
// The minimum sample duration we will allow before deciding we have
// enough data on CPU usage to start killing things.
static final int CPU_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000;
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
// Amount of time we wait for observers to handle a user switch before
// giving up on them and unfreezing the screen.
static final int USER_SWITCH_TIMEOUT = 2*1000;
// Maximum number of users we allow to be running at a time.
static final int MAX_RUNNING_USERS = 3;
// How long to wait in getAssistContextExtras for the activity and foreground services
// to respond with the result.
static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
// Maximum number of persisted Uri grants a package is allowed
static final int MAX_PERSISTED_URI_GRANTS = 128;
static final int MY_PID = Process.myPid();
static final String[] EMPTY_STRING_ARRAY = new String[0];
/** Run all ActivityStacks through this */
ActivityStackSupervisor mStackSupervisor;
public IntentFirewall mIntentFirewall;
private final boolean mHeadless;
// Whether we should show our dialogs (ANR, crash, etc) or just perform their
// default actuion automatically. Important for devices without direct input
// devices.
private boolean mShowDialogs = true;
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
*/
static class PendingActivityLaunch {
final ActivityRecord r;
final ActivityRecord sourceRecord;
final int startFlags;
final ActivityStack stack;
PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
int _startFlags, ActivityStack _stack) {
r = _r;
sourceRecord = _sourceRecord;
startFlags = _startFlags;
stack = _stack;
}
}
final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
= new ArrayList<PendingActivityLaunch>();
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
// so that dispatch of foreground broadcasts gets precedence.
final BroadcastQueue[] mBroadcastQueues = new BroadcastQueue[2];
BroadcastQueue broadcastQueueForIntent(Intent intent) {
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
if (DEBUG_BACKGROUND_BROADCAST) {
Slog.i(TAG, "Broadcast intent " + intent + " on "
+ (isFg ? "foreground" : "background")
+ " queue");
}
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
BroadcastRecord broadcastRecordForReceiverLocked(IBinder receiver) {
for (BroadcastQueue queue : mBroadcastQueues) {
BroadcastRecord r = queue.getMatchingOrderedReceiver(receiver);
if (r != null) {
return r;
}
}
return null;
}
/**
* Activity we have told the window manager to have key focus.
*/
ActivityRecord mFocusedActivity = null;
/**
* List of intents that were used to start the most recent tasks.
*/
private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public boolean haveResult = false;
public Bundle result = null;
public PendingAssistExtras(ActivityRecord _activity) {
activity = _activity;
}
@Override
public void run() {
Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from " + activity);
synchronized (this) {
haveResult = true;
notifyAll();
}
}
}
final ArrayList<PendingAssistExtras> mPendingAssistExtras
= new ArrayList<PendingAssistExtras>();
/**
* Process management.
*/
final ProcessList mProcessList = new ProcessList();
/**
* All of the applications we currently have running organized by name.
* The keys are strings of the application package name (as
* returned by the package manager), and the keys are ApplicationRecord
* objects.
*/
final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
/**
* Tracking long-term execution of processes to look for abuse and other
* bad app behavior.
*/
final ProcessStatsService mProcessStats;
/**
* The currently running isolated processes.
*/
final SparseArray<ProcessRecord> mIsolatedProcesses = new SparseArray<ProcessRecord>();
/**
* Counter for assigning isolated process uids, to avoid frequently reusing the
* same ones.
*/
int mNextIsolatedProcessUid = 0;
/**
* The currently running heavy-weight process, if any.
*/
ProcessRecord mHeavyWeightProcess = null;
/**
* The last time that various processes have crashed.
*/
final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
/**
* Set of applications that we consider to be bad, and will reject
* incoming broadcasts from (which the user has no control over).
* Processes are added to this set when they have crashed twice within
* a minimum amount of time; they are removed from it when they are
* later restarted (hopefully due to some user action). The value is the
* time it was added to the list.
*/
final ProcessMap<Long> mBadProcesses = new ProcessMap<Long>();
/**
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
* <p>NOTE: This object is protected by its own lock, NOT the global
* activity manager lock!
*/
final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
/**
* All of the processes that have been forced to be foreground. The key
* is the pid of the caller who requested it (we hold a death
* link on it).
*/
abstract class ForegroundToken implements IBinder.DeathRecipient {
int pid;
IBinder token;
}
final SparseArray<ForegroundToken> mForegroundProcesses = new SparseArray<ForegroundToken>();
/**
* 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<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();
/**
* List of persistent applications that are in the process
* of being started.
*/
final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
/**
* Processes that are being forcibly torn down.
*/
final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
/**
* List of running applications, sorted by recent usage.
* The first entry in the list is the least recently used.
*/
final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
/**
* 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<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
/**
* Processes we want to collect PSS data from.
*/
final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
/**
* Last time we requested PSS data of all processes.
*/
long mLastFullPssTime = SystemClock.uptimeMillis();
/**
* 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;
/**
* Which uses have been started, so are allowed to run code.
*/
final SparseArray<UserStartedState> mStartedUsers = new SparseArray<UserStartedState>();
/**
* LRU list of history of current users. Most recently current is at the end.
*/
final ArrayList<Integer> mUserLru = new ArrayList<Integer>();
/**
* Constant array of the users that are currently started.
*/
int[] mStartedUserArray = new int[] { 0 };
/**
* Registered observers of the user switching mechanics.
*/
final RemoteCallbackList<IUserSwitchObserver> mUserSwitchObservers
= new RemoteCallbackList<IUserSwitchObserver>();
/**
* Currently active user switch.
*/
Object mCurUserSwitchCallback;
/**
* 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<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
= new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
/**
* 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<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>();
private static final int MAX_DUP_SUPPRESSED_STACKS = 5000;
/**
* Strict Mode background batched logging state.
*
* The string buffer is guarded by itself, and its lock is also
* used to determine if another batched write is already
* in-flight.
*/
private final StringBuilder mStrictModeBuffer = new StringBuilder();
/**
* Keeps track of all IIntentReceivers that have been registered for
* broadcasts. Hash keys are the receiver IBinder, hash value is
* a ReceiverList.
*/
final HashMap<IBinder, ReceiverList> mRegisteredReceivers =
new HashMap<IBinder, ReceiverList>();
/**
* Resolver for broadcast intents to registered receivers.
* Holds BroadcastFilter (subclass of IntentFilter).
*/
final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
= new IntentResolver<BroadcastFilter, BroadcastFilter>() {
@Override
protected boolean allowFilterResult(
BroadcastFilter filter, List<BroadcastFilter> 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<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
new SparseArray<ArrayMap<String, ArrayList<Intent>>>();
final ActiveServices mServices;
/**
* Backup/restore process management
*/
String mBackupAppName = null;
BackupRecord mBackupTarget = null;
/**
* List of PendingThumbnailsRecord objects of clients who are still
* waiting to receive all of the thumbnails for a task.
*/
final ArrayList<PendingThumbnailsRecord> mPendingThumbnails =
new ArrayList<PendingThumbnailsRecord>();
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<ContentProviderRecord> mLaunchingProviders
= new ArrayList<ContentProviderRecord>();
/**
* 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_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";
/**
* 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<ArrayMap<Uri, UriPermission>>
mGrantedUriPermissions = new SparseArray<ArrayMap<Uri, UriPermission>>();
CoreSettingsObserver mCoreSettingsObserver;
/**
* Thread-local storage used to carry caller permissions over through
* indirect content-provider access.
*/
private class Identity {
public int pid;
public int uid;
Identity(int _pid, int _uid) {
pid = _pid;
uid = _uid;
}
}
private static ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
/**
* 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
*/
final UsageStatsService mUsageStatsService;
/**
* Information about and control over application operations
*/
final AppOpsService mAppOpsService;
/**
* Current configuration information. HistoryRecord objects are given
* a reference to this object to indicate which configuration they are
* currently running in, so this object must be kept immutable.
*/
Configuration mConfiguration = new Configuration();
/**
* Current sequencing integer of the configuration, for skipping old
* configurations.
*/
int mConfigurationSeq = 0;
/**
* 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.
*/
HashMap<String, IBinder> mAppBindArgs;
/**
* Temporary to avoid allocations. Protected by main lock.
*/
final StringBuilder mStringBuilder = new StringBuilder(256);
/**
* Used to control how we initialize the service.
*/
boolean mStartRunning = false;
ComponentName mTopComponent;
String mTopAction;
String mTopData;
boolean mProcessesReady = false;
boolean mSystemReady = false;
boolean mBooting = false;
boolean mWaitingUpdate = false;
boolean mDidUpdate = false;
boolean mOnBattery = false;
boolean mLaunchWarningShown = false;
Context mContext;
int mFactoryTest;
boolean mCheckedForSetup;
/**
* 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 realtime) at which we checked for power usage.
*/
long mLastPowerCheckRealtime;
/**
* 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.
*/
boolean mSleeping = false;
/**
* State of external calls telling us if the device is asleep.
*/
boolean mWentToSleep = false;
/**
* State of external call telling us if the lock screen is shown.
*/
boolean mLockScreenShown = 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 situatin started.
*/
long mLowRamStartTime = 0;
/**
* This is set if we had to do a delayed dexopt of an app before launching
* it, to increasing the ANR timeouts in that case.
*/
boolean mDidDexOpt;
String mDebugApp = null;
boolean mWaitForDebugger = false;
boolean mDebugTransient = false;
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
IActivityController mController = null;
String mProfileApp = null;
ProcessRecord mProfileProc = null;
String mProfileFile;
ParcelFileDescriptor mProfileFd;
int mProfileType = 0;
boolean mAutoStopProfiler = false;
String mOpenGlTraceApp = null;
static class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
static final int CHANGE_IMPORTANCE= 1<<1;
int changes;
int uid;
int pid;
int importance;
boolean foregroundActivities;
}
final RemoteCallbackList<IProcessObserver> mProcessObservers
= new RemoteCallbackList<IProcessObserver>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
final ArrayList<ProcessChangeItem> mPendingProcessChanges
= new ArrayList<ProcessChangeItem>();
final ArrayList<ProcessChangeItem> mAvailProcessChanges
= new ArrayList<ProcessChangeItem>();
/**
* Runtime CPU use collection thread. This object's lock is used to
* protect all related state.
*/
final Thread mProcessCpuThread;
/**
* Used to collect process stats when showing not responding dialog.
* Protected by mProcessCpuThread.
*/
final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
MONITOR_THREAD_CPU_USAGE);
final AtomicLong mLastCpuTime = new AtomicLong(0);
final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
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;
int mProcessLimit = ProcessList.MAX_CACHED_APPS;
int mProcessLimitOverride = -1;
WindowManagerService mWindowManager;
static ActivityManagerService mSelf;
static ActivityThread mSystemThread;
int mCurrentUserId = 0;
private UserManagerService mUserManager;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
if (localLOGV) Slog.v(
TAG, "New death recipient " + this
+ " for thread " + thread.asBinder());
mApp = app;
mPid = pid;
mAppThread = thread;
}
@Override
public void binderDied() {
if (localLOGV) Slog.v(
TAG, "Death received in " + this
+ " for thread " + mAppThread.asBinder());
synchronized(ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread);
}
}
}
static final int SHOW_ERROR_MSG = 1;
static final int SHOW_NOT_RESPONDING_MSG = 2;
static final int SHOW_FACTORY_ERROR_MSG = 3;
static final int UPDATE_CONFIGURATION_MSG = 4;
static final int GC_BACKGROUND_PROCESSES_MSG = 5;
static final int WAIT_FOR_DEBUGGER_MSG = 6;
static final int SERVICE_TIMEOUT_MSG = 12;
static final int UPDATE_TIME_ZONE = 13;
static final int SHOW_UID_ERROR_MSG = 14;
static final int IM_FEELING_LUCKY_MSG = 15;
static final int PROC_START_TIMEOUT_MSG = 20;
static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
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_MSG = 26;
static final int CHECK_EXCESSIVE_WAKE_LOCKS_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_MSG = 30;
static final int DISPATCH_PROCESSES_CHANGED = 31;
static final int DISPATCH_PROCESS_DIED = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
static final int REPORT_USER_SWITCH_MSG = 34;
static final int CONTINUE_USER_SWITCH_MSG = 35;
static final int USER_SWITCH_TIMEOUT_MSG = 36;
static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int PERSIST_URI_GRANTS_MSG = 38;
static final int REQUEST_ALL_PSS_MSG = 39;
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;
AlertDialog mUidAlert;
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;
final Handler mHandler = new Handler() {
//public Handler() {
// if (localLOGV) Slog.v(TAG, "Handler started!");
//}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ERROR_MSG: {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
synchronized (ActivityManagerService.this) {
ProcessRecord proc = (ProcessRecord)data.get("app");
AppErrorResult res = (AppErrorResult) data.get("result");
if (proc != null && proc.crashDialog != null) {
Slog.e(TAG, "App already has crash dialog: " + proc);
if (res != null) {
res.set(0);
}
return;
}
if (!showBackground && UserHandle.getAppId(proc.uid)
>= Process.FIRST_APPLICATION_UID && proc.userId != mCurrentUserId
&& proc.pid != MY_PID) {
Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
if (res != null) {
res.set(0);
}
return;
}
if (mShowDialogs && !mSleeping && !mShuttingDown) {
Dialog d = new AppErrorDialog(mContext,
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".
if (res != null) {
res.set(0);
}
}
}
ensureBootCompleted();
} break;
case SHOW_NOT_RESPONDING_MSG: {
synchronized (ActivityManagerService.this) {
HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
ProcessRecord proc = (ProcessRecord)data.get("app");
if (proc != null && proc.anrDialog != null) {
Slog.e(TAG, "App already has anr dialog: " + proc);
return;
}
Intent intent = new Intent("android.intent.action.ANR");
if (!mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
}
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
if (mShowDialogs) {
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
mContext, proc, (ActivityRecord)data.get("activity"),
msg.arg1 != 0);
d.show();
proc.anrDialog = d;
} else {
// Just kill the app if there is no dialog to be shown.
killAppAtUsersRequest(proc, null);
}
}
ensureBootCompleted();
} break;
case SHOW_STRICT_MODE_VIOLATION_MSG: {
HashMap<String, Object> data = (HashMap<String, Object>) 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(mContext,
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_MSG: {
Dialog d = new FactoryErrorDialog(
mContext, msg.getData().getCharSequence("msg"));
d.show();
ensureBootCompleted();
} break;
case UPDATE_CONFIGURATION_MSG: {
final ContentResolver resolver = mContext.getContentResolver();
Settings.System.putConfiguration(resolver, (Configuration)msg.obj);
} break;
case GC_BACKGROUND_PROCESSES_MSG: {
synchronized (ActivityManagerService.this) {
performAppGcsIfAppropriateLocked();
}
} break;
case WAIT_FOR_DEBUGGER_MSG: {
synchronized (ActivityManagerService.this) {
ProcessRecord app = (ProcessRecord)msg.obj;
if (msg.arg1 != 0) {
if (!app.waitedForDebugger) {
Dialog d = new AppWaitingForDebuggerDialog(
ActivityManagerService.this,
mContext, app);
app.waitDialog = d;
app.waitedForDebugger = true;
d.show();
}
} else {
if (app.waitDialog != null) {
app.waitDialog.dismiss();
app.waitDialog = null;
}
}
}
} break;
case SERVICE_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
nmsg.obj = msg.obj;
mHandler.sendMessageDelayed(nmsg, ActiveServices.SERVICE_TIMEOUT);
return;
}
mServices.serviceTimeout((ProcessRecord)msg.obj);
} 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: {
ProxyProperties proxy = (ProxyProperties)msg.obj;
String host = "";
String port = "";
String exclList = "";
String pacFileUrl = null;
if (proxy != null) {
host = proxy.getHost();
port = Integer.toString(proxy.getPort());
exclList = proxy.getExclusionList();
pacFileUrl = proxy.getPacFileUrl();
}
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = mLruProcesses.get(i);
if (r.thread != null) {
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 SHOW_UID_ERROR_MSG: {
String title = "System UIDs Inconsistent";
String text = "UIDs on the system are inconsistent, you need to wipe your"
+ " data partition or your device will be unstable.";
Log.e(TAG, title + ": " + text);
if (mShowDialogs) {
// XXX This is a temporary dialog, no need to localize.
AlertDialog d = new BaseErrorDialog(mContext);
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
d.setCancelable(false);
d.setTitle(title);
d.setMessage(text);
d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky",
mHandler.obtainMessage(IM_FEELING_LUCKY_MSG));
mUidAlert = d;
d.show();
}
} break;
case IM_FEELING_LUCKY_MSG: {
if (mUidAlert != null) {
mUidAlert.dismiss();
mUidAlert = null;
}
} break;
case PROC_START_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
nmsg.obj = msg.obj;
mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT);
return;
}
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processStartTimedOutLocked(app);
}
} break;
case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {
synchronized (ActivityManagerService.this) {
doPendingActivityLaunchesLocked(true);
}
} break;
case KILL_APPLICATION_MSG: {
synchronized (ActivityManagerService.this) {
int appid = msg.arg1;
boolean restart = (msg.arg2 == 1);
Bundle bundle = (Bundle)msg.obj;
String pkg = bundle.getString("pkg");
String reason = bundle.getString("reason");
forceStopPackageLocked(pkg, appid, restart, false, true, false,
UserHandle.USER_ALL, 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();
notification.icon = com.android.internal.R.drawable.stat_sys_adb; //context.getApplicationInfo().icon;
notification.when = 0;
notification.flags = Notification.FLAG_ONGOING_EVENT;
notification.tickerText = text;
notification.defaults = 0; // please be quiet
notification.sound = null;
notification.vibrate = null;
notification.setLatestEventInfo(context, text,
mContext.getText(R.string.heavy_weight_notification_detail),
PendingIntent.getActivityAsUser(mContext, 0, root.intent,
PendingIntent.FLAG_CANCEL_CURRENT, null,
new UserHandle(root.userId)));
try {
int[] outId = new int[1];
inm.enqueueNotificationWithTag("android", "android", null,
R.string.heavy_weight_notification,
notification, outId, 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,
R.string.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_WAKE_LOCKS_MSG: {
synchronized (ActivityManagerService.this) {
checkExcessivePowerUsageLocked(true);
removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG);
sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
}
} break;
case SHOW_COMPAT_MODE_DIALOG_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, mContext,
ar.info.applicationInfo);
mCompatModeDialog.show();
}
}
}
}
break;
}
case DISPATCH_PROCESSES_CHANGED: {
dispatchProcessesChanged();
break;
}
case DISPATCH_PROCESS_DIED: {
final int pid = msg.arg1;
final int uid = msg.arg2;
dispatchProcessDied(pid, uid);
break;
}
case REPORT_MEM_USAGE_MSG: {
final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj;
Thread thread = new Thread() {
@Override public void run() {