| /* |
| * 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.Manifest.permission.CHANGE_CONFIGURATION; |
| import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; |
| import static android.Manifest.permission.INTERACT_ACROSS_USERS; |
| import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; |
| import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; |
| import static android.Manifest.permission.REMOVE_TASKS; |
| import static android.Manifest.permission.STOP_APP_SWITCHES; |
| import static android.app.ActivityThread.PROC_START_SEQ_IDENT; |
| import static android.app.AppOpsManager.OP_NONE; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; |
| import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; |
| import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; |
| import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; |
| import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT; |
| import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; |
| import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; |
| import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY; |
| import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; |
| import static android.content.pm.PackageManager.GET_PROVIDERS; |
| import static android.content.pm.PackageManager.MATCH_ANY_USER; |
| import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; |
| import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; |
| import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; |
| import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; |
| import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; |
| import static android.content.pm.PackageManager.PERMISSION_GRANTED; |
| import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION; |
| import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; |
| import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; |
| import static android.os.Build.VERSION_CODES.N; |
| import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; |
| import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; |
| import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; |
| import static android.os.IServiceManager.DUMP_FLAG_PROTO; |
| import static android.os.Process.BLUETOOTH_UID; |
| import static android.os.Process.FIRST_APPLICATION_UID; |
| import static android.os.Process.FIRST_ISOLATED_UID; |
| import static android.os.Process.LAST_ISOLATED_UID; |
| import static android.os.Process.NFC_UID; |
| import static android.os.Process.PHONE_UID; |
| import static android.os.Process.PROC_CHAR; |
| import static android.os.Process.PROC_OUT_LONG; |
| import static android.os.Process.PROC_PARENS; |
| import static android.os.Process.PROC_SPACE_TERM; |
| import static android.os.Process.ProcessStartResult; |
| import static android.os.Process.ROOT_UID; |
| import static android.os.Process.SCHED_FIFO; |
| import static android.os.Process.SCHED_OTHER; |
| import static android.os.Process.SCHED_RESET_ON_FORK; |
| import static android.os.Process.SE_UID; |
| import static android.os.Process.SHELL_UID; |
| import static android.os.Process.SIGNAL_QUIT; |
| import static android.os.Process.SIGNAL_USR1; |
| import static android.os.Process.SYSTEM_UID; |
| import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE; |
| import static android.os.Process.THREAD_GROUP_DEFAULT; |
| import static android.os.Process.THREAD_GROUP_RESTRICTED; |
| import static android.os.Process.THREAD_GROUP_TOP_APP; |
| import static android.os.Process.THREAD_PRIORITY_BACKGROUND; |
| import static android.os.Process.THREAD_PRIORITY_FOREGROUND; |
| import static android.os.Process.getFreeMemory; |
| import static android.os.Process.getTotalMemory; |
| import static android.os.Process.isThreadInProcess; |
| import static android.os.Process.killProcess; |
| import static android.os.Process.killProcessQuiet; |
| import static android.os.Process.myPid; |
| import static android.os.Process.myUid; |
| import static android.os.Process.readProcFile; |
| import static android.os.Process.removeAllProcessGroups; |
| import static android.os.Process.sendSignal; |
| import static android.os.Process.setProcessGroup; |
| import static android.os.Process.setThreadPriority; |
| import static android.os.Process.setThreadScheduler; |
| import static android.os.Process.startWebView; |
| import static android.os.Process.zygoteProcess; |
| import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; |
| import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; |
| import static android.provider.Settings.Global.DEBUG_APP; |
| import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; |
| import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; |
| import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; |
| import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS; |
| import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS; |
| import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER; |
| import static android.provider.Settings.System.FONT_SCALE; |
| import static android.text.format.DateUtils.DAY_IN_MILLIS; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| import static android.view.Display.INVALID_DISPLAY; |
| import static com.android.internal.util.XmlUtils.readBooleanAttribute; |
| import static com.android.internal.util.XmlUtils.readIntAttribute; |
| import static com.android.internal.util.XmlUtils.readLongAttribute; |
| import static com.android.internal.util.XmlUtils.writeBooleanAttribute; |
| import static com.android.internal.util.XmlUtils.writeIntAttribute; |
| import static com.android.internal.util.XmlUtils.writeLongAttribute; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ_REASON; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESS_OBSERVERS; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROVIDER; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; |
| import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_NETWORK; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_OOM_ADJ; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_POWER; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESSES; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROCESS_OBSERVERS; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PROVIDER; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION; |
| import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY; |
| import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; |
| import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; |
| import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; |
| import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; |
| import static com.android.server.am.MemoryStatUtil.hasMemcg; |
| import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; |
| import static android.view.WindowManager.TRANSIT_NONE; |
| import static android.view.WindowManager.TRANSIT_TASK_OPEN; |
| import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; |
| import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; |
| import static org.xmlpull.v1.XmlPullParser.START_TAG; |
| |
| import android.Manifest; |
| import android.Manifest.permission; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.UserIdInt; |
| import android.app.Activity; |
| import android.app.ActivityManager; |
| import android.app.ActivityManager.RunningTaskInfo; |
| import android.app.ActivityManager.StackInfo; |
| import android.app.ActivityManager.TaskSnapshot; |
| import android.app.ActivityManagerInternal; |
| import android.app.ActivityTaskManagerInternal.ScreenObserver; |
| import android.app.ActivityTaskManagerInternal.SleepToken; |
| import android.app.ActivityManagerProto; |
| import android.app.ActivityOptions; |
| import android.app.ActivityTaskManager; |
| import android.app.ActivityThread; |
| import android.app.AlertDialog; |
| import android.app.AppGlobals; |
| import android.app.AppOpsManager; |
| import android.app.ApplicationErrorReport; |
| import android.app.ApplicationThreadConstants; |
| import android.app.BroadcastOptions; |
| import android.app.ContentProviderHolder; |
| import android.app.Dialog; |
| import android.app.GrantedUriPermission; |
| import android.app.IActivityController; |
| import android.app.IActivityManager; |
| import android.app.IApplicationThread; |
| import android.app.IAssistDataReceiver; |
| import android.app.IInstrumentationWatcher; |
| import android.app.INotificationManager; |
| import android.app.IProcessObserver; |
| import android.app.IServiceConnection; |
| import android.app.IStopUserCallback; |
| import android.app.ITaskStackListener; |
| import android.app.IUiAutomationConnection; |
| import android.app.IUidObserver; |
| import android.app.IUserSwitchObserver; |
| import android.app.Instrumentation; |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.app.ProcessMemoryState; |
| import android.app.ProfilerInfo; |
| import android.app.WindowConfiguration.ActivityType; |
| import android.app.WindowConfiguration.WindowingMode; |
| import android.app.backup.IBackupManager; |
| import android.app.servertransaction.ConfigurationChangeItem; |
| import android.app.usage.UsageEvents; |
| import android.app.usage.UsageStatsManagerInternal; |
| import android.appwidget.AppWidgetManager; |
| 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.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy; |
| 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.PackageManager.NameNotFoundException; |
| import android.content.pm.PackageManagerInternal; |
| import android.content.pm.ParceledListSlice; |
| import android.content.pm.PathPermission; |
| import android.content.pm.PermissionInfo; |
| import android.content.pm.ProviderInfo; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.SELinuxUtil; |
| import android.content.pm.ServiceInfo; |
| import android.content.pm.UserInfo; |
| import android.content.res.CompatibilityInfo; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.database.ContentObserver; |
| import android.graphics.Rect; |
| import android.hardware.display.DisplayManagerInternal; |
| import android.location.LocationManager; |
| import android.media.audiofx.AudioEffect; |
| import android.net.Proxy; |
| import android.net.ProxyInfo; |
| import android.net.Uri; |
| import android.os.BatteryStats; |
| 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.FactoryTest; |
| import android.os.FileObserver; |
| import android.os.FileUtils; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.IDeviceIdentifiersPolicyService; |
| import android.os.IPermissionController; |
| import android.os.IProcessInfoService; |
| import android.os.IProgressListener; |
| import android.os.LocaleList; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Parcel; |
| import android.os.ParcelFileDescriptor; |
| import android.os.PowerManager; |
| import android.os.PowerManager.ServiceType; |
| import android.os.PowerManagerInternal; |
| import android.os.Process; |
| import android.os.RemoteCallback; |
| import android.os.RemoteCallbackList; |
| import android.os.RemoteException; |
| import android.os.ResultReceiver; |
| import android.os.ServiceManager; |
| import android.os.ShellCallback; |
| import android.os.StrictMode; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.Trace; |
| import android.os.TransactionTooLargeException; |
| import android.os.UpdateLock; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.os.WorkSource; |
| import android.os.storage.IStorageManager; |
| import android.os.storage.StorageManager; |
| import android.os.storage.StorageManagerInternal; |
| import android.provider.Downloads; |
| import android.provider.Settings; |
| import android.service.voice.IVoiceInteractionSession; |
| import android.text.TextUtils; |
| import android.text.format.DateUtils; |
| import android.text.format.Time; |
| import android.text.style.SuggestionSpan; |
| import android.util.ArrayMap; |
| import android.util.ArraySet; |
| import android.util.AtomicFile; |
| import android.util.DebugUtils; |
| import android.util.EventLog; |
| import android.util.Log; |
| import android.util.LongSparseArray; |
| import android.util.Pair; |
| import android.util.PrintWriterPrinter; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.util.SparseIntArray; |
| import android.util.StatsLog; |
| import android.util.TimeUtils; |
| import android.util.TimingsTraceLog; |
| import android.util.Xml; |
| import android.util.proto.ProtoOutputStream; |
| import android.util.proto.ProtoUtils; |
| import android.view.Gravity; |
| import android.view.IRecentsAnimationRunner; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.WindowManager; |
| import android.view.autofill.AutofillManagerInternal; |
| |
| import com.android.internal.R; |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.app.DumpHeapActivity; |
| import com.android.internal.app.IAppOpsCallback; |
| import com.android.internal.app.IAppOpsService; |
| import com.android.internal.app.IVoiceInteractor; |
| import com.android.internal.app.ProcessMap; |
| import com.android.internal.app.SystemUserHomeActivity; |
| import com.android.internal.app.procstats.ProcessStats; |
| import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; |
| import com.android.internal.notification.SystemNotificationChannels; |
| import com.android.internal.os.BackgroundThread; |
| import com.android.internal.os.BatteryStatsImpl; |
| import com.android.internal.os.BinderInternal; |
| import com.android.internal.os.ByteTransferPipe; |
| import com.android.internal.os.IResultReceiver; |
| import com.android.internal.os.ProcessCpuTracker; |
| import com.android.internal.os.TransferPipe; |
| import com.android.internal.os.Zygote; |
| import com.android.internal.telephony.TelephonyIntents; |
| import com.android.internal.util.ArrayUtils; |
| import com.android.internal.util.DumpUtils; |
| 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.AlarmManagerInternal; |
| import com.android.server.AppOpsService; |
| import com.android.server.AttributeCache; |
| import com.android.server.BinderCallsStatsService; |
| import com.android.server.DeviceIdleController; |
| import com.android.server.IntentResolver; |
| import com.android.server.IoThread; |
| import com.android.server.LocalServices; |
| import com.android.server.LockGuard; |
| import com.android.server.NetworkManagementInternal; |
| import com.android.server.RescueParty; |
| import com.android.server.ServiceThread; |
| import com.android.server.SystemConfig; |
| import com.android.server.SystemService; |
| import com.android.server.SystemServiceManager; |
| import com.android.server.ThreadPriorityBooster; |
| import com.android.server.Watchdog; |
| import com.android.server.am.ActivityStack.ActivityState; |
| import com.android.server.am.MemoryStatUtil.MemoryStat; |
| import com.android.server.am.ActivityManagerServiceProto; |
| import com.android.server.am.ActivityManagerServiceDumpActivitiesProto; |
| import com.android.server.am.ActivityManagerServiceDumpBroadcastsProto; |
| import com.android.server.am.ActivityManagerServiceDumpProcessesProto; |
| import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; |
| import com.android.server.am.ActivityManagerServiceDumpServicesProto; |
| import com.android.server.am.GrantUriProto; |
| import com.android.server.am.ImportanceTokenProto; |
| import com.android.server.am.MemInfoDumpProto; |
| import com.android.server.am.NeededUriGrantsProto; |
| import com.android.server.am.ProcessOomProto; |
| import com.android.server.am.ProcessToGcProto; |
| import com.android.server.am.StickyBroadcastProto; |
| import com.android.server.firewall.IntentFirewall; |
| import com.android.server.job.JobSchedulerInternal; |
| import com.android.server.pm.Installer; |
| import com.android.server.pm.Installer.InstallerException; |
| import com.android.server.pm.dex.DexManager; |
| import com.android.server.utils.PriorityDump; |
| import com.android.server.vr.VrManagerInternal; |
| import com.android.server.wm.WindowManagerService; |
| |
| import dalvik.system.VMRuntime; |
| |
| import libcore.io.IoUtils; |
| import libcore.util.EmptyArray; |
| |
| import com.google.android.collect.Lists; |
| import com.google.android.collect.Maps; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| import org.xmlpull.v1.XmlSerializer; |
| |
| 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.io.UnsupportedEncodingException; |
| import java.lang.ref.WeakReference; |
| import java.nio.charset.StandardCharsets; |
| import java.text.DateFormat; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Date; |
| 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.Objects; |
| import java.util.Set; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| public class ActivityManagerService extends IActivityManager.Stub |
| implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { |
| |
| /** |
| * Priority we boost main thread and RT of top app to. |
| */ |
| public static final int TOP_APP_PRIORITY_BOOST = -10; |
| |
| private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM; |
| private static final String TAG_BACKUP = TAG + POSTFIX_BACKUP; |
| private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST; |
| private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP; |
| private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; |
| private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; |
| private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE; |
| private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK; |
| private static final String TAG_LRU = TAG + POSTFIX_LRU; |
| private static final String TAG_MU = TAG + POSTFIX_MU; |
| private static final String TAG_NETWORK = TAG + POSTFIX_NETWORK; |
| private static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ; |
| private static final String TAG_POWER = TAG + POSTFIX_POWER; |
| private static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS; |
| private static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES; |
| private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER; |
| private static final String TAG_PSS = TAG + POSTFIX_PSS; |
| private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; |
| private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE; |
| private static final String TAG_STACK = TAG + POSTFIX_STACK; |
| private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; |
| private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS; |
| private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION; |
| private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY; |
| |
| // Mock "pretend we're idle now" broadcast action to the job scheduler; declared |
| // here so that while the job scheduler can depend on AMS, the other way around |
| // need not be the case. |
| public static final String ACTION_TRIGGER_IDLE = "com.android.server.ACTION_TRIGGER_IDLE"; |
| |
| /** Control over CPU and battery monitoring */ |
| // write battery stats every 30 minutes. |
| static final long BATTERY_STATS_TIME = 30 * 60 * 1000; |
| static final boolean MONITOR_CPU_USAGE = true; |
| // don't sample cpu less than every 5 seconds. |
| static final long MONITOR_CPU_MIN_TIME = 5 * 1000; |
| // wait possibly forever for next cpu sample. |
| static final long MONITOR_CPU_MAX_TIME = 0x0fffffff; |
| 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; |
| |
| static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; |
| |
| // Maximum number of receivers an app can register. |
| private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000; |
| |
| // 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 an attached process to publish its content providers |
| // before we decide it must be hung. |
| static final int CONTENT_PROVIDER_PUBLISH_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 = 1200*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; |
| |
| // Disable hidden API checks for the newly started instrumentation. |
| // Must be kept in sync with Am. |
| private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0; |
| |
| // Maximum number of persisted Uri grants a package is allowed |
| static final int MAX_PERSISTED_URI_GRANTS = 128; |
| |
| static final int MY_PID = myPid(); |
| |
| static final String[] EMPTY_STRING_ARRAY = new String[0]; |
| |
| // How many bytes to write into the dropbox log before truncating |
| static final int DROPBOX_MAX_SIZE = 192 * 1024; |
| // Assumes logcat entries average around 100 bytes; that's not perfect stack traces count |
| // as one line, but close enough for now. |
| static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100; |
| |
| /** If a UID observer takes more than this long, send a WTF. */ |
| private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20; |
| |
| // Access modes for handleIncomingUser. |
| static final int ALLOW_NON_FULL = 0; |
| static final int ALLOW_NON_FULL_IN_PROFILE = 1; |
| static final int ALLOW_FULL_ONLY = 2; |
| |
| // Necessary ApplicationInfo flags to mark an app as persistent |
| private static final int PERSISTENT_MASK = |
| ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT; |
| |
| // Intent sent when remote bugreport collection has been completed |
| private static final String INTENT_REMOTE_BUGREPORT_FINISHED = |
| "com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED"; |
| |
| // Used to indicate that an app transition should be animated. |
| static final boolean ANIMATE = true; |
| |
| // Determines whether to take full screen screenshots |
| static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true; |
| |
| /** |
| * Default value for {@link Settings.Global#NETWORK_ACCESS_TIMEOUT_MS}. |
| */ |
| private static final long NETWORK_ACCESS_TIMEOUT_DEFAULT_MS = 200; // 0.2 sec |
| |
| /** |
| * State indicating that there is no need for any blocking for network. |
| */ |
| @VisibleForTesting |
| static final int NETWORK_STATE_NO_CHANGE = 0; |
| |
| /** |
| * State indicating that the main thread needs to be informed about the network wait. |
| */ |
| @VisibleForTesting |
| static final int NETWORK_STATE_BLOCK = 1; |
| |
| /** |
| * State indicating that any threads waiting for network state to get updated can be unblocked. |
| */ |
| @VisibleForTesting |
| static final int NETWORK_STATE_UNBLOCK = 2; |
| |
| // Max character limit for a notification title. If the notification title is larger than this |
| // the notification will not be legible to the user. |
| private static final int MAX_BUGREPORT_TITLE_SIZE = 50; |
| |
| private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds; |
| |
| /** All system services */ |
| SystemServiceManager mSystemServiceManager; |
| |
| private Installer mInstaller; |
| |
| /** Run all ActivityStacks through this */ |
| ActivityStackSupervisor mStackSupervisor; |
| |
| final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter(); |
| |
| final ArrayList<ActiveInstrumentation> mActiveInstrumentation = new ArrayList<>(); |
| |
| public final IntentFirewall mIntentFirewall; |
| |
| // Whether we should show our dialogs (ANR, crash, etc) or just perform their |
| // default action automatically. Important for devices without direct input |
| // devices. |
| private boolean mShowDialogs = true; |
| |
| // VR Vr2d Display Id. |
| int mVr2dDisplayId = INVALID_DISPLAY; |
| |
| // Whether we should use SCHED_FIFO for UI and RenderThreads. |
| private boolean mUseFifoUiScheduling = false; |
| |
| private static final String SYSUI_COMPONENT_NAME = "com.android.systemui/.SystemUIService"; |
| |
| 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]; |
| |
| BroadcastStats mLastBroadcastStats; |
| BroadcastStats mCurBroadcastStats; |
| |
| BroadcastQueue broadcastQueueForIntent(Intent intent) { |
| final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0; |
| if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST, |
| "Broadcast intent " + intent + " on " |
| + (isFg ? "foreground" : "background") + " queue"); |
| return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue; |
| } |
| |
| /** |
| * The last resumed activity. This is identical to the current resumed activity most |
| * of the time but could be different when we're pausing one activity before we resume |
| * another activity. |
| */ |
| ActivityRecord mLastResumedActivity; |
| |
| /** |
| * The activity that is currently being traced as the active resumed activity. |
| * |
| * @see #updateResumedAppTrace |
| */ |
| private @Nullable ActivityRecord mTracedResumedActivity; |
| |
| /** |
| * If non-null, we are tracking the time the user spends in the currently focused app. |
| */ |
| private AppTimeTracker mCurAppTimeTracker; |
| |
| /** |
| * The package name of the DeviceOwner. This package is not permitted to have its data cleared. |
| */ |
| String mDeviceOwnerName; |
| |
| final UserController mUserController; |
| |
| /** |
| * Packages that are being allowed to perform unrestricted app switches. Mapping is |
| * User -> Type -> uid. |
| */ |
| final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>(); |
| |
| final AppErrors mAppErrors; |
| |
| final AppWarnings mAppWarnings; |
| |
| /** |
| * Dump of the activity state at the time of the last ANR. Cleared after |
| * {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS} |
| */ |
| String mLastANRState; |
| |
| /** |
| * Indicates the maximum time spent waiting for the network rules to get updated. |
| */ |
| @VisibleForTesting |
| long mWaitForNetworkTimeoutMs; |
| |
| /** Total # of UID change events dispatched, shown in dumpsys. */ |
| int mUidChangeDispatchCount; |
| |
| /** |
| * Helper class which strips out priority and proto arguments then calls the dump function with |
| * the appropriate arguments. If priority arguments are omitted, function calls the legacy |
| * dump command. |
| * If priority arguments are omitted all sections are dumped, otherwise sections are dumped |
| * according to their priority. |
| */ |
| private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() { |
| @Override |
| public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args, |
| boolean asProto) { |
| if (asProto) return; |
| doDump(fd, pw, new String[]{"activities"}, asProto); |
| doDump(fd, pw, new String[]{"service", SYSUI_COMPONENT_NAME}, asProto); |
| } |
| |
| @Override |
| public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { |
| doDump(fd, pw, new String[]{"-a", "--normal-priority"}, asProto); |
| } |
| |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { |
| doDump(fd, pw, args, asProto); |
| } |
| }; |
| |
| public boolean canShowErrorDialogs() { |
| return mShowDialogs && !mSleeping && !mShuttingDown |
| && !mActivityTaskManager.mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY) |
| && !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, |
| mUserController.getCurrentUserId()) |
| && !(UserManager.isDeviceInDemoMode(mContext) |
| && mUserController.getCurrentUser().isDemo()); |
| } |
| |
| private static ThreadPriorityBooster sThreadPriorityBooster = new ThreadPriorityBooster( |
| THREAD_PRIORITY_FOREGROUND, LockGuard.INDEX_ACTIVITY); |
| |
| static void boostPriorityForLockedSection() { |
| sThreadPriorityBooster.boost(); |
| } |
| |
| static void resetPriorityAfterLockedSection() { |
| sThreadPriorityBooster.reset(); |
| } |
| |
| /** |
| * 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; |
| |
| /** |
| * Non-persistent appId whitelist for background restrictions |
| */ |
| int[] mBackgroundAppIdWhitelist = new int[] { |
| BLUETOOTH_UID |
| }; |
| |
| /** |
| * Broadcast actions that will always be deliverable to unlaunched/background apps |
| */ |
| ArraySet<String> mBackgroundLaunchBroadcasts; |
| |
| /** |
| * 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 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<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>(); |
| |
| /** |
| * 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>(); |
| |
| 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<UidRecord> mActiveUids = new SparseArray<>(); |
| |
| /** |
| * This is for verifying the UID report flow. |
| */ |
| static final boolean VALIDATE_UID_STATES = true; |
| final SparseArray<UidRecord> 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<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; |
| |
| /** |
| * 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<>(); |
| |
| /** |
| * 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; |
| |
| 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<ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>>> |
| 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<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_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<ArrayMap<GrantUri, UriPermission>> |
| mGrantedUriPermissions = new SparseArray<ArrayMap<GrantUri, UriPermission>>(); |
| |
| 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<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 |
| */ |
| 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<PendingTempWhitelist> mPendingTempWhitelist = new SparseArray<>(); |
| |
| /** |
| * Information about and control over application operations |
| */ |
| final AppOpsService mAppOpsService; |
| |
| /** |
| * 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<String, IBinder> mAppBindArgs; |
| ArrayMap<String, IBinder> 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; |
| |
| /** |
| * 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<ArrayList<ProcessRecord>> mForegroundPackages |
| = new ProcessMap<ArrayList<ProcessRecord>>(); |
| |
| /** |
| * 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<String, String> mAppAgentMap = null; |
| |
| int mProfileType = 0; |
| final ProcessMap<Pair<Long, String>> 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<BroadcastQueue> 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<ProcessRecord> 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<NI; i++) { |
| final long pToken = proto.start(UidObserverRegistrationProto.LAST_PROC_STATES); |
| proto.write(UidObserverRegistrationProto.ProcState.UID, lastProcStates.keyAt(i)); |
| proto.write(UidObserverRegistrationProto.ProcState.STATE, lastProcStates.valueAt(i)); |
| proto.end(pToken); |
| } |
| } |
| proto.end(token); |
| } |
| } |
| |
| final List<ScreenObserver> mScreenObservers = new ArrayList<>(); |
| |
| final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>(); |
| ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5]; |
| |
| final ArrayList<ProcessChangeItem> mPendingProcessChanges = new ArrayList<>(); |
| final ArrayList<ProcessChangeItem> mAvailProcessChanges = new ArrayList<>(); |
| |
| final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>(); |
| UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5]; |
| |
| final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>(); |
| final ArrayList<UidRecord.ChangeItem> 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; |
| ActivityTaskManagerService mActivityTaskManager; |
| 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 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 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; |
| |
| 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<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(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<IResultReceiver> callbacks |
| = (RemoteCallbackList<IResultReceiver>)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(); |
| // We have to clean up the RemoteCallbackList here, because otherwise it will |
| // needlessly hold the enclosed callbacks until the remote process dies. |
| callbacks.kill(); |
| } 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<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)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 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<Long, String> 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 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<ProcessCpuTracker.Stats> 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; |
| mActivityTaskManager.setWindowManager(wm); |
| mStackSupervisor.setWindowManager(wm); |
| } |
| } |
| |
| public void setActivityTaskManager(ActivityTaskManagerService atm) { |
| synchronized (this) { |
| mActivityTaskManager = atm; |
| mActivityTaskManager.setActivityManagerService(this); |
| mStackSupervisor = mActivityTaskManager.mStackSupervisor; |
| } |
| } |
| |
| 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<String> 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; |
| mAppErrors = null; |
| mAppWarnings = null; |
| mAppOpsService = mInjector.getAppOpsService(null, null); |
| mBatteryStatsService = null; |
| mCompatModePackages = null; |
| mConstants = null; |
| mGrantFile = null; |
| mHandler = null; |
| mHandlerThread = null; |
| mIntentFirewall = null; |
| mPermissionReviewRequired = false; |
| mProcessCpuThread = null; |
| mProcessStats = null; |
| mProviderMap = null; |
| mServices = null; |
| mSystemThread = null; |
| mUiHandler = injector.getUiHandler(null); |
| mUserController = 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); |
| |
| 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")); |
| mCompatModePackages = new CompatModePackages(this, systemDir, mHandler); |
| mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler); |
| |
| 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"); |
| } |
| |
| } |
| |
| 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 access 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) { |
| mActivityTaskManager.getRecentTasks().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<String> 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<IBinder> procs = new ArrayList<IBinder>(); |
| synchronized(this) { |
| final int NP = mProcessNames.getMap().size(); |
| for (int ip=0; ip<NP; ip++) { |
| SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip); |
| final int NA = apps.size(); |
| for (int ia=0; ia<NA; ia++) { |
| ProcessRecord app = apps.valueAt(ia); |
| if (app.thread != null) { |
| procs.add(app.thread.asBinder()); |
| } |
| } |
| } |
| } |
| |
| int N = procs.size(); |
| for (int i=0; i<N; i++) { |
| Parcel data2 = Parcel.obtain(); |
| try { |
| procs.get(i).transact(IBinder.SYSPROPS_TRANSACTION, data2, null, |
| Binder.FLAG_ONEWAY); |
| } catch (RemoteException e) { |
| } |
| data2.recycle(); |
| } |
| } |
| try { |
| return super.onTransact(code, data, reply, flags); |
| } catch (RuntimeException e) { |
| // The activity manager only throws certain exceptions intentionally, so let's |
| // log all others. |
| if (!(e instanceof SecurityException |
| || e instanceof IllegalArgumentException |
| || e instanceof IllegalStateException)) { |
| Slog.wtf(TAG, "Activity Manager Crash." |
| + " UID:" + Binder.getCallingUid() |
| + " PID:" + Binder.getCallingPid() |
| + " TRANS:" + code, e); |
| } |
| throw e; |
| } |
| } |
| |
| void updateCpuStats() { |
| final long now = SystemClock.uptimeMillis(); |
| if (mLastCpuTime.get() >= 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<N; i++) { |
| ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); |
| if (!st.working) { |
| continue; |
| } |
| ProcessRecord pr = mPidsSelfLocked.get(st.pid); |
| totalUTime += st.rel_utime; |
| totalSTime += st.rel_stime; |
| if (pr != null) { |
| BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats; |
| if (ps == null || !ps.isActive()) { |
| pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked( |
| pr.info.uid, pr.processName); |
| } |
| ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); |
| pr.curCpuTime += st.rel_utime + st.rel_stime; |
| if (pr.lastCpuTime == 0) { |
| pr.lastCpuTime = pr.curCpuTime; |
| } |
| } else { |
| BatteryStatsImpl.Uid.Proc ps = st.batteryStats; |
| if (ps == null || !ps.isActive()) { |
| st.batteryStats = ps = bstats.getProcessStatsLocked( |
| bstats.mapUid(st.uid), st.name); |
| } |
| ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); |
| } |
| } |
| final int userTime = mProcessCpuTracker.getLastUserTime(); |
| final int systemTime = mProcessCpuTracker.getLastSystemTime(); |
| final int iowaitTime = mProcessCpuTracker.getLastIoWaitTime(); |
| final int irqTime = mProcessCpuTracker.getLastIrqTime(); |
| final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime(); |
| final int idleTime = mProcessCpuTracker.getLastIdleTime(); |
| bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime, |
| systemTime, iowaitTime, irqTime, softIrqTime, idleTime); |
| } |
| } |
| } |
| |
| if (mLastWriteTime < (now-BATTERY_STATS_TIME)) { |
| mLastWriteTime = now; |
| mBatteryStatsService.scheduleWriteToDisk(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void batteryNeedsCpuUpdate() { |
| updateCpuStatsNow(); |
| } |
| |
| @Override |
| public void batteryPowerChanged(boolean onBattery) { |
| // When plugging in, update the CPU stats first before changing |
| // the plug state. |
| updateCpuStatsNow(); |
| synchronized (this) { |
| synchronized(mPidsSelfLocked) { |
| mOnBattery = DEBUG_POWER ? true : onBattery; |
| } |
| } |
| } |
| |
| @Override |
| public void batteryStatsReset() { |
| BinderCallsStatsService.reset(); |
| } |
| |
| @Override |
| public void batterySendBroadcast(Intent intent) { |
| synchronized (this) { |
| broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, |
| OP_NONE, null, false, false, |
| -1, SYSTEM_UID, UserHandle.USER_ALL); |
| } |
| } |
| |
| /** |
| * Initialize the application bind args. These are passed to each |
| * process when the bindApplication() IPC is sent to the process. They're |
| * lazily setup to make sure the services are running when they're asked for. |
| */ |
| private ArrayMap<String, IBinder> 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<String, IBinder> 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. |
| mActivityTaskManager.finishVoiceTask(session); |
| } |
| } |
| } |
| |
| if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) { |
| mUserController.sendForegroundProfileChanged(r.userId); |
| } |
| updateResumedAppTrace(r); |
| mLastResumedActivity = r; |
| |
| mWindowManager.setFocusedApp(r.appToken, true); |
| |
| applyUpdateLockStateLocked(r); |
| mActivityTaskManager.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) { |
| mActivityTaskManager.setFocusedStack(stackId); |
| } |
| |
| /** Sets the task stack listener that gets callbacks when a task stack changes. */ |
| @Override |
| public void registerTaskStackListener(ITaskStackListener listener) { |
| mActivityTaskManager.registerTaskStackListener(listener); |
| } |
| |
| /** |
| * Unregister a task stack listener so that it stops receiving callbacks. |
| */ |
| @Override |
| public void unregisterTaskStackListener(ITaskStackListener listener) { |
| mActivityTaskManager.unregisterTaskStackListener(listener); |
| } |
| |
| 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 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<ProcessRecord> 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 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; |
| mActivityTaskManager.getActivityStartController().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 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<N; j++) { |
| ProcessChangeItem item = mActiveProcessChanges[j]; |
| if ((item.changes&ProcessChangeItem.CHANGE_ACTIVITIES) != 0) { |
| if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, |
| "ACTIVITIES CHANGED pid=" + item.pid + " uid=" |
| + item.uid + ": " + item.foregroundActivities); |
| observer.onForegroundActivitiesChanged(item.pid, item.uid, |
| item.foregroundActivities); |
| } |
| } |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| mProcessObservers.finishBroadcast(); |
| |
| synchronized (this) { |
| for (int j=0; j<N; j++) { |
| mAvailProcessChanges.add(mActiveProcessChanges[j]); |
| } |
| } |
| } |
| |
| private void dispatchProcessDied(int pid, int uid) { |
| int i = mProcessObservers.beginBroadcast(); |
| while (i > 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<N; i++) { |
| final UidRecord.ChangeItem change = mPendingUidChanges.get(i); |
| mActiveUidChanges[i] = change; |
| if (change.uidRecord != null) { |
| change.uidRecord.pendingChange = null; |
| change.uidRecord = null; |
| } |
| } |
| mPendingUidChanges.clear(); |
| if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, |
| "*** Delivering " + N + " uid changes"); |
| } |
| |
| mUidChangeDispatchCount += N; |
| int i = mUidObservers.beginBroadcast(); |
| while (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 int startActivity(IApplicationThread caller, String callingPackage, |
| Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, |
| int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) { |
| return mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType, |
| resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions); |
| } |
| |
| @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 mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent, |
| resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, |
| userId); |
| } |
| |
| @Override |
| public final int startActivityFromRecents(int taskId, Bundle bOptions) { |
| return mActivityTaskManager.startActivityFromRecents(taskId, bOptions); |
| } |
| |
| @Override |
| public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver, |
| IRecentsAnimationRunner recentsAnimationRunner) { |
| mActivityTaskManager.startRecentsActivity( |
| intent, assistDataReceiver, recentsAnimationRunner); |
| } |
| |
| @Override |
| public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { |
| mActivityTaskManager.cancelRecentsAnimation(restoreHomeStackPosition); |
| } |
| |
| @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(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * This is the internal entry point for handling Activity.finish(). |
| * |
| * @param token The Binder token referencing the Activity we want to finish. |
| * @param resultCode Result code, if any, from this Activity. |
| * @param resultData Result data (Intent), if any, from this Activity. |
| * @param finishTask Whether to finish the task associated with this Activity. |
| * |
| * @return Returns true if the activity successfully finished, or false if it is still running. |
| */ |
| @Override |
| public final boolean finishActivity(IBinder token, int resultCode, Intent resultData, |
| int finishTask) { |
| return mActivityTaskManager.finishActivity(token, resultCode, resultData, finishTask); |
| } |
| |
| @Override |
| public void setRequestedOrientation(IBinder token, int requestedOrientation) { |
| mActivityTaskManager.setRequestedOrientation(token, requestedOrientation); |
| } |
| |
| @Override |
| public final void finishHeavyWeightApp() { |
| if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) |
| != PackageManager.PERMISSION_GRANTED) { |
| String msg = "Permission Denial: finishHeavyWeightApp() from pid=" |
| + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingUid() |
| + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| |
| synchronized(this) { |
| final ProcessRecord proc = mHeavyWeightProcess; |
| if (proc == null) { |
| return; |
| } |
| |
| ArrayList<ActivityRecord> 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); |
| } |
| } |
| |
| /** |
| * 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<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap(); |
| for (int i = pmap.size()-1; i >= 0; i--) { |
| final SparseArray<ProcessRecord> 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<ProcessMemInfo> memInfos |
| = doReport ? new ArrayList<ProcessMemInfo>(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<Integer> firstPids, |
| ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, |
| ArrayList<Integer> nativePids) { |
| ArrayList<Integer> 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); |
| } |
| } |
| } |
| |
| final File tracesDir = new File("/data/anr"); |
| // 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. |
| File tracesFile = createAnrDumpFile(tracesDir); |
| if (tracesFile == null) { |
| return null; |
| } |
| |
| dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids); |
| 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 File[] files = tracesDir.listFiles(); |
| if (files == null) return; |
| |
| final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64); |
| final long now = System.currentTimeMillis(); |
| Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed()); |
| for (int i = 0; i < files.length; ++i) { |
| if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) { |
| if (!files[i].delete()) { |
| Slog.w(TAG, "Unable to prune stale trace file: " + files[i]); |
| } |
| } |
| } |
| } |
| |
| /** |
| * 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<Integer> firstPids, |
| ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) { |
| |
| // 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. |
| |
| // We must complete all stack dumps within 20 seconds. |
| long remainingTime = 20 * 1000; |
| |
| // 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 = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, |
| 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 = dumpJavaTracesTombstoned(pid, tracesFile, 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"); |
| } |
| } |
| } |
| } |
| |
| final void logAppTooSlow(ProcessRecord app, long startTime, String msg) { |
| if (true || Build.IS_USER) { |
| return; |
| } |
| |
| StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); |
| StrictMode.allowThreadDiskWrites(); |
| try { |
| File tracesDir = new File("/data/anr"); |
| File tracesFile = null; |
| try { |
| tracesFile = File.createTempFile("app_slow", null, tracesDir); |
| |
| 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: " + tracesFile, e); |
| return; |
| } |
| |
| if (app != null && app.pid > 0) { |
| ArrayList<Integer> firstPids = new ArrayList<Integer>(); |
| firstPids.add(app.pid); |
| dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null); |
| } |
| |
| 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); |
| } 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"); |
| mActivityTaskManager.getRecentTasks().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<ProcessRecord> procs = new ArrayList<>(); |
| final int NP = mProcessNames.getMap().size(); |
| for (int ip = 0; ip < NP; ip++) { |
| final SparseArray<ProcessRecord> 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<ProcessRecord> procs = new ArrayList<>(); |
| final int NP = mProcessNames.getMap().size(); |
| for (int ip = 0; ip < NP; ip++) { |
| final SparseArray<ProcessRecord> 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<String>(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<ProcessRecord> 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<NP; ip++) { |
| SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip); |
| final int NA = apps.size(); |
| for (int ia=0; ia<NA; ia++) { |
| ProcessRecord app = apps.valueAt(ia); |
| if (app.persistent && !evenPersistent) { |
| // we don't kill persistent processes |
| continue; |
| } |
| if (app.removed) { |
| if (doit) { |
| procs.add(app); |
| } |
| continue; |
| } |
| |
| // Skip process if it doesn't meet our oom adj requirement. |
| if (app.setAdj < minOomAdj) { |
| continue; |
| } |
| |
| // If no package is specified, we call all processes under the |
| // give user id. |
| if (packageName == null) { |
| if (userId != UserHandle.USER_ALL && app.userId != userId) { |
| continue; |
| } |
| if (appId >= 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<N; i++) { |
| removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); |
| } |
| updateOomAdjLocked(); |
| return N > 0; |
| } |
| |
| private void cleanupDisabledPackageComponentsLocked( |
| String packageName, int userId, boolean killProcess, String[] changedClasses) { |
| |
| Set<String> 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 |
| mActivityTaskManager.getRecentTasks().cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId); |
| |
| // Clean-up disabled services. |
| mServices.bringDownDisabledPackageServicesLocked( |
| packageName, disabledClasses, userId, false, killProcess, true); |
| |
| // Clean-up disabled providers. |
| ArrayList<ContentProviderRecord> 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 |= mActivityTaskManager.getActivityStartController().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<ContentProviderRecord> 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<WeakReference<PendingIntentRecord>> it |
| = mIntentSenderRecords.values().iterator(); |
| while (it.hasNext()) { |
| WeakReference<PendingIntentRecord> 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<ProviderInfo> 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); |
| } |
| } |
| |
| 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); |
| } |
| |
| final void finishBooting() { |
| synchronized (this) { |
| if (!mBootAnimationComplete) { |
| mCallFinishBooting = true; |
| return; |
| } |
| mCallFinishBooting = false; |
| } |
| |
| ArraySet<String> completedIsas = new ArraySet<String>(); |
| 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) { |
| if (!VMRuntime.didPruneDalvikCache()) { |
| // This is technically not the right filter, as different zygotes may |
| // have made different pruning decisions. But the log is best effort, |
| // anyways. |
| 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<ProcessRecord> procs = |
| new ArrayList<ProcessRecord>(mProcessesOnHold); |
| for (int ip=0; ip<NP; ip++) { |
| if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: " |
| + procs.get(ip)); |
| startProcessLocked(procs.get(ip), "on-hold", null); |
| } |
| } |
| if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) { |
| return; |
| } |
| // Start looking for apps that are abusing wake locks. |
| Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG); |
| mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); |
| // Tell anyone interested that we are done booting! |
| SystemProperties.set("sys.boot_completed", "1"); |
| |
| // And trigger dev.bootcomplete if we are not showing encryption progress |
| if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt")) |
| || "".equals(SystemProperties.get("vold.encrypt_progress"))) { |
| SystemProperties.set("dev.bootcomplete", "1"); |
| } |
| mUserController.sendBootCompleted( |
| new IIntentReceiver.Stub() { |
| @Override |
| public void performReceive(Intent intent, int resultCode, |
| String data, Bundle extras, boolean ordered, |
| boolean sticky, int sendingUser) { |
| synchronized (ActivityManagerService.this) { |
| requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); |
| } |
| } |
| }); |
| mUserController.scheduleStartProfiles(); |
| } |
| } |
| |
| @Override |
| public void bootAnimationComplete() { |
| final boolean callFinishBooting; |
| synchronized (this) { |
| callFinishBooting = mCallFinishBooting; |
| mBootAnimationComplete = true; |
| } |
| if (callFinishBooting) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting"); |
| finishBooting(); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| } |
| } |
| |
| final void ensureBootCompleted() { |
| boolean booting; |
| boolean enableScreen; |
| synchronized (this) { |
| booting = mBooting; |
| mBooting = false; |
| enableScreen = !mBooted; |
| mBooted = true; |
| } |
| |
| if (booting) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "FinishBooting"); |
| finishBooting(); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| } |
| |
| if (enableScreen) { |
| enableScreenAfterBoot(); |
| } |
| } |
| |
| @Override |
| public IIntentSender getIntentSender(int type, |
| String packageName, IBinder token, String resultWho, |
| int requestCode, Intent[] intents, String[] resolvedTypes, |
| int flags, Bundle bOptions, int userId) { |
| enforceNotIsolatedCaller("getIntentSender"); |
| // Refuse possible leaked file descriptors |
| if (intents != null) { |
| if (intents.length < 1) { |
| throw new IllegalArgumentException("Intents array length must be >= 1"); |
| } |
| for (int i=0; i<intents.length; i++) { |
| Intent intent = intents[i]; |
| if (intent != null) { |
| if (intent.hasFileDescriptors()) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| if (type == ActivityManager.INTENT_SENDER_BROADCAST && |
| (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) { |
| throw new IllegalArgumentException( |
| "Can't use FLAG_RECEIVER_BOOT_UPGRADE here"); |
| } |
| intents[i] = new Intent(intent); |
| } |
| } |
| if (resolvedTypes != null && resolvedTypes.length != intents.length) { |
| throw new IllegalArgumentException( |
| "Intent array length does not match resolvedTypes length"); |
| } |
| } |
| if (bOptions != null) { |
| if (bOptions.hasFileDescriptors()) { |
| throw new IllegalArgumentException("File descriptors passed in options"); |
| } |
| } |
| |
| synchronized(this) { |
| int callingUid = Binder.getCallingUid(); |
| int origUserId = userId; |
| userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId, |
| type == ActivityManager.INTENT_SENDER_BROADCAST, |
| ALLOW_NON_FULL, "getIntentSender", null); |
| if (origUserId == UserHandle.USER_CURRENT) { |
| // We don't want to evaluate this until the pending intent is |
| // actually executed. However, we do want to always do the |
| // security checking for it above. |
| userId = UserHandle.USER_CURRENT; |
| } |
| try { |
| if (callingUid != 0 && callingUid != SYSTEM_UID) { |
| final int uid = AppGlobals.getPackageManager().getPackageUid(packageName, |
| MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid)); |
| if (!UserHandle.isSameApp(callingUid, uid)) { |
| String msg = "Permission Denial: getIntentSender() from pid=" |
| + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingUid() |
| + ", (need uid=" + uid + ")" |
| + " is not allowed to send as package " + packageName; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| } |
| |
| return getIntentSenderLocked(type, packageName, callingUid, userId, |
| token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions); |
| |
| } catch (RemoteException e) { |
| throw new SecurityException(e); |
| } |
| } |
| } |
| |
| IIntentSender getIntentSenderLocked(int type, String packageName, |
| int callingUid, int userId, IBinder token, String resultWho, |
| int requestCode, Intent[] intents, String[] resolvedTypes, int flags, |
| Bundle bOptions) { |
| if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid); |
| ActivityRecord activity = null; |
| if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) { |
| activity = ActivityRecord.isInStackLocked(token); |
| if (activity == null) { |
| Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack"); |
| return null; |
| } |
| if (activity.finishing) { |
| Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing"); |
| return null; |
| } |
| } |
| |
| // We're going to be splicing together extras before sending, so we're |
| // okay poking into any contained extras. |
| if (intents != null) { |
| for (int i = 0; i < intents.length; i++) { |
| intents[i].setDefusable(true); |
| } |
| } |
| Bundle.setDefusable(bOptions, true); |
| |
| final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0; |
| final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0; |
| final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0; |
| flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT |
| |PendingIntent.FLAG_UPDATE_CURRENT); |
| |
| PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, activity, |
| resultWho, requestCode, intents, resolvedTypes, flags, |
| SafeActivityOptions.fromBundle(bOptions), userId); |
| WeakReference<PendingIntentRecord> 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<WeakReference<PendingIntentRecord>>(); |
| } |
| 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<IResultReceiver> 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<res.key.allIntents.length; i++) { |
| Intent intent = res.key.allIntents[i]; |
| if (intent.getPackage() != null && intent.getComponent() != null) { |
| return false; |
| } |
| } |
| return true; |
| } catch (ClassCastException e) { |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isIntentSenderAnActivity(IIntentSender pendingResult) { |
| if (!(pendingResult instanceof PendingIntentRecord)) { |
| return false; |
| } |
| try { |
| PendingIntentRecord res = (PendingIntentRecord)pendingResult; |
| if (res.key.type == ActivityManager.INTENT_SENDER_ACTIVITY) { |
| return true; |
| } |
| return false; |
| } catch (ClassCastException e) { |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isIntentSenderAForegroundService(IIntentSender pendingResult) { |
| if (pendingResult instanceof PendingIntentRecord) { |
| final PendingIntentRecord res = (PendingIntentRecord) pendingResult; |
| return res.key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE; |
| } |
| return false; |
| } |
| |
| @Override |
| public Intent getIntentForIntentSender(IIntentSender pendingResult) { |
| enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT, |
| "getIntentForIntentSender()"); |
| if (!(pendingResult instanceof PendingIntentRecord)) { |
| return null; |
| } |
| try { |
| PendingIntentRecord res = (PendingIntentRecord)pendingResult; |
| return res.key.requestIntent != null ? new Intent(res.key.requestIntent) : null; |
| } catch (ClassCastException e) { |
| } |
| return null; |
| } |
| |
| @Override |
| public String getTagForIntentSender(IIntentSender pendingResult, String prefix) { |
| if (!(pendingResult instanceof PendingIntentRecord)) { |
| return null; |
| } |
| try { |
| PendingIntentRecord res = (PendingIntentRecord)pendingResult; |
| synchronized (this) { |
| return getTagForIntentSenderLocked(res, prefix); |
| } |
| } catch (ClassCastException e) { |
| } |
| return null; |
| } |
| |
| String getTagForIntentSenderLocked(PendingIntentRecord res, String prefix) { |
| final Intent intent = res.key.requestIntent; |
| if (intent != null) { |
| if (res.lastTag != null && res.lastTagPrefix == prefix && (res.lastTagPrefix == null |
| || res.lastTagPrefix.equals(prefix))) { |
| return res.lastTag; |
| } |
| res.lastTagPrefix = prefix; |
| final StringBuilder sb = new StringBuilder(128); |
| if (prefix != null) { |
| sb.append(prefix); |
| } |
| if (intent.getAction() != null) { |
| sb.append(intent.getAction()); |
| } else if (intent.getComponent() != null) { |
| intent.getComponent().appendShortString(sb); |
| } else { |
| sb.append("?"); |
| } |
| return res.lastTag = sb.toString(); |
| } |
| return null; |
| } |
| |
| @Override |
| public void setProcessLimit(int max) { |
| enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, |
| "setProcessLimit()"); |
| synchronized (this) { |
| mConstants.setOverrideMaxCachedProcesses(max); |
| } |
| trimApplications(); |
| } |
| |
| @Override |
| public int getProcessLimit() { |
| synchronized (this) { |
| return mConstants.getOverrideMaxCachedProcesses(); |
| } |
| } |
| |
| void importanceTokenDied(ImportanceToken token) { |
| synchronized (ActivityManagerService.this) { |
| synchronized (mPidsSelfLocked) { |
| ImportanceToken cur |
| = mImportantProcesses.get(token.pid); |
| if (cur != token) { |
| return; |
| } |
| mImportantProcesses.remove(token.pid); |
| ProcessRecord pr = mPidsSelfLocked.get(token.pid); |
| if (pr == null) { |
| return; |
| } |
| pr.forcingToImportant = null; |
| updateProcessForegroundLocked(pr, false, false); |
| } |
| updateOomAdjLocked(); |
| } |
| } |
| |
| @Override |
| public void setProcessImportant(IBinder token, int pid, boolean isForeground, String reason) { |
| enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, |
| "setProcessImportant()"); |
| synchronized(this) { |
| boolean changed = false; |
| |
| synchronized (mPidsSelfLocked) { |
| ProcessRecord pr = mPidsSelfLocked.get(pid); |
| if (pr == null && isForeground) { |
| Slog.w(TAG, "setProcessForeground called on unknown pid: " + pid); |
| return; |
| } |
| ImportanceToken oldToken = mImportantProcesses.get(pid); |
| if (oldToken != null) { |
| oldToken.token.unlinkToDeath(oldToken, 0); |
| mImportantProcesses.remove(pid); |
| if (pr != null) { |
| pr.forcingToImportant = null; |
| } |
| changed = true; |
| } |
| if (isForeground && token != null) { |
| ImportanceToken newToken = new ImportanceToken(pid, token, reason) { |
| @Override |
| public void binderDied() { |
| importanceTokenDied(this); |
| } |
| }; |
| try { |
| token.linkToDeath(newToken, 0); |
| mImportantProcesses.put(pid, newToken); |
| pr.forcingToImportant = newToken; |
| changed = true; |
| } catch (RemoteException e) { |
| // If the process died while doing this, we will later |
| // do the cleanup with the process death link. |
| } |
| } |
| } |
| |
| if (changed) { |
| updateOomAdjLocked(); |
| } |
| } |
| } |
| |
| @Override |
| public boolean isAppForeground(int uid) { |
| synchronized (this) { |
| UidRecord uidRec = mActiveUids.get(uid); |
| if (uidRec == null || uidRec.idle) { |
| return false; |
| } |
| return uidRec.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; |
| } |
| } |
| |
| // NOTE: this is an internal method used by the OnShellCommand implementation only and should |
| // be guarded by permission checking. |
| int getUidState(int uid) { |
| synchronized (this) { |
| return getUidStateLocked(uid); |
| } |
| } |
| |
| int getUidStateLocked(int uid) { |
| UidRecord uidRec = mActiveUids.get(uid); |
| return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState; |
| } |
| |
| // ========================================================= |
| // 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 (!mActivityTaskManager.getRecentTasks().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 <provider> 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 |
| // <path-permission> below removes this ability |
| boolean allowDefaultRead = pi.readPermission == null; |
| boolean allowDefaultWrite = pi.writePermission == null; |
| |
| // check if target holds any <path-permission> 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 <provider> read/write, if not blocked by |
| // <path-permission> 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<GrantUri, UriPermission> 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<GrantUri, UriPermission> 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<GrantUri, UriPermission> 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<N; i++) { |
| if (pi.pathPermissions[i] != null |
| && pi.pathPermissions[i].match(grantUri.uri.getPath())) { |
| if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) { |
| if (pi.pathPermissions[i].getReadPermission() != null) { |
| allowed = false; |
| } |
| } |
| if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) { |
| if (pi.pathPermissions[i].getWritePermission() != null) { |
| allowed = false; |
| } |
| } |
| break; |
| } |
| } |
| } |
| if (allowed) { |
| return -1; |
| } |
| } |
| |
| /* There is a special cross user grant if: |
| * - The target is on another user. |
| * - Apps on the current user can access the uri without any uid permissions. |
| * In this case, we grant a uri permission, even if the ContentProvider does not normally |
| * grant uri permissions. |
| */ |
| boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId |
| && checkHoldingPermissionsInternalLocked(pm, pi, grantUri, callingUid, |
| modeFlags, false /*without considering the uid permissions*/); |
| |
| // Second... is the provider allowing granting of URI permissions? |
| if (!specialCrossUserGrant) { |
| if (!pi.grantUriPermissions) { |
| throw new SecurityException("Provider " + pi.packageName |
| + "/" + pi.name |
| + " does not allow granting of Uri permissions (uri " |
| + grantUri + ")"); |
| } |
| if (pi.uriPermissionPatterns != null) { |
| final int N = pi.uriPermissionPatterns.length; |
| boolean allowed = false; |
| for (int i=0; i<N; i++) { |
| if (pi.uriPermissionPatterns[i] != null |
| && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) { |
| allowed = true; |
| break; |
| } |
| } |
| if (!allowed) { |
| throw new SecurityException("Provider " + pi.packageName |
| + "/" + pi.name |
| + " does not allow granting of permission to path of Uri " |
| + grantUri); |
| } |
| } |
| } |
| |
| // Third... does the caller itself have permission to access |
| // this uri? |
| if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) { |
| // Require they hold a strong enough Uri permission |
| if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) { |
| if (android.Manifest.permission.MANAGE_DOCUMENTS.equals(pi.readPermission)) { |
| throw new SecurityException( |
| "UID " + callingUid + " does not have permission to " + grantUri |
| + "; you could obtain access using ACTION_OPEN_DOCUMENT " |
| + "or related APIs"); |
| } else { |
| throw new SecurityException( |
| "UID " + callingUid + " does not have permission to " + grantUri); |
| } |
| } |
| } |
| return targetUid; |
| } |
| |
| /** |
| * @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 checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, |
| final int modeFlags, int userId) { |
| enforceNotIsolatedCaller("checkGrantUriPermission"); |
| synchronized(this) { |
| return checkGrantUriPermissionLocked(callingUid, targetPkg, |
| new GrantUri(userId, uri, false), modeFlags, -1); |
| } |
| } |
| |
| @GuardedBy("this") |
| void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, GrantUri grantUri, |
| final int modeFlags, UriPermissionOwner owner) { |
| if (!Intent.isAccessUriMode(modeFlags)) { |
| return; |
| } |
| |
| // So here we are: the caller has the assumed permission |
| // to the uri, and the target doesn't. Let's now give this to |
| // the target. |
| |
| if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, |
| "Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri); |
| |
| 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 grant: " + grantUri.toSafeString()); |
| return; |
| } |
| |
| if ((modeFlags & Intent.FLAG_GRANT_PREFIX_URI_PERMISSION) != 0) { |
| grantUri.prefix = true; |
| } |
| final UriPermission perm = findOrCreateUriPermissionLocked( |
| pi.packageName, targetPkg, targetUid, grantUri); |
| perm.grantModes(modeFlags, owner); |
| } |
| |
| @GuardedBy("this") |
| void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri, |
| final int modeFlags, UriPermissionOwner owner, int targetUserId) { |
| if (targetPkg == null) { |
| throw new NullPointerException("targetPkg"); |
| } |
| int targetUid; |
| final IPackageManager pm = AppGlobals.getPackageManager(); |
| try { |
| targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId); |
| } catch (RemoteException ex) { |
| return; |
| } |
| |
| targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, modeFlags, |
| targetUid); |
| if (targetUid < 0) { |
| return; |
| } |
| |
| grantUriPermissionUncheckedLocked(targetUid, targetPkg, grantUri, modeFlags, |
| owner); |
| } |
| |
| static class NeededUriGrants extends ArrayList<GrantUri> { |
| 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<N; i++) { |
| this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS); |
| } |
| proto.end(token); |
| } |
| } |
| |
| /** |
| * Like checkGrantUriPermissionLocked, but takes an Intent. |
| */ |
| @GuardedBy("this") |
| NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid, |
| String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) { |
| if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, |
| "Checking URI perm to data=" + (intent != null ? intent.getData() : null) |
| + " clip=" + (intent != null ? intent.getClipData() : null) |
| + " from " + intent + "; flags=0x" |
| + Integer.toHexString(intent != null ? intent.getFlags() : 0)); |
| |
| if (targetPkg == null) { |
| throw new NullPointerException("targetPkg"); |
| } |
| |
| if (intent == null) { |
| return null; |
| } |
| Uri data = intent.getData(); |
| ClipData clip = intent.getClipData(); |
| if (data == null && clip == null) { |
| return null; |
| } |
| // Default userId for uris in the intent (if they don't specify it themselves) |
| int contentUserHint = intent.getContentUserHint(); |
| if (contentUserHint == UserHandle.USER_CURRENT) { |
| contentUserHint = UserHandle.getUserId(callingUid); |
| } |
| final IPackageManager pm = AppGlobals.getPackageManager(); |
| int targetUid; |
| if (needed != null) { |
| targetUid = needed.targetUid; |
| } else { |
| try { |
| targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, |
| targetUserId); |
| } catch (RemoteException ex) { |
| return null; |
| } |
| if (targetUid < 0) { |
| if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, |
| "Can't grant URI permission no uid for: " + targetPkg |
| + " on user " + targetUserId); |
| return null; |
| } |
| } |
| if (data != null) { |
| GrantUri grantUri = GrantUri.resolve(contentUserHint, data); |
| targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode, |
| targetUid); |
| if (targetUid > 0) { |
| if (needed == null) { |
| needed = new NeededUriGrants(targetPkg, targetUid, mode); |
| } |
| needed.add(grantUri); |
| } |
| } |
| if (clip != null) { |
| for (int i=0; i<clip.getItemCount(); i++) { |
| Uri uri = clip.getItemAt(i).getUri(); |
| if (uri != null) { |
| GrantUri grantUri = GrantUri.resolve(contentUserHint, uri); |
| targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode, |
| targetUid); |
| if (targetUid > 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<needed.size(); i++) { |
| GrantUri grantUri = needed.get(i); |
| grantUriPermissionUncheckedLocked(needed.targetUid, needed.targetPkg, |
| grantUri, needed.flags, owner); |
| } |
| } |
| } |
| |
| @GuardedBy("this") |
| void grantUriPermissionFromIntentLocked(int callingUid, |
| String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) { |
| NeededUriGrants needed = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg, |
| intent, intent != null ? intent.getFlags() : 0, null, targetUserId); |
| if (needed == null) { |
| return; |
| } |
| |
| grantUriPermissionUncheckedFromIntentLocked(needed, owner); |
| } |
| |
| /** |
| * @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 grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri, |
| final int modeFlags, int userId) { |
| enforceNotIsolatedCaller("grantUriPermission"); |
| GrantUri grantUri = new GrantUri(userId, uri, false); |
| synchronized(this) { |
| final ProcessRecord r = getRecordForAppLocked(caller); |
| if (r == null) { |
| throw new SecurityException("Unable to find app for caller " |
| + caller |
| + " when granting permission to uri " + grantUri); |
| } |
| if (targetPkg == null) { |
| throw new IllegalArgumentException("null target"); |
| } |
| if (grantUri == null) { |
| throw new IllegalArgumentException("null uri"); |
| } |
| |
| Preconditions.checkFlagsArgument(modeFlags, Intent.FLAG_GRANT_READ_URI_PERMISSION |
| | Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
| | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
| | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); |
| |
| grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null, |
| UserHandle.getUserId(r.uid)); |
| } |
| } |
| |
| @GuardedBy("this") |
| void removeUriPermissionIfNeededLocked(UriPermission perm) { |
| if (perm.modeFlags == 0) { |
| final ArrayMap<GrantUri, UriPermission> 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<GrantUri, UriPermission> 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<GrantUri, UriPermission> 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 <em>from</em> or <em>to</em> 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<GrantUri, UriPermission> perms = mGrantedUriPermissions.valueAt(i); |
| |
| // Only inspect grants matching user |
| if (userHandle == UserHandle.USER_ALL |
| || userHandle == UserHandle.getUserId(targetUid)) { |
| for (Iterator<UriPermission> 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(); |
| } |
| } |
| |
| /** |
| * @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<UriPermission.Snapshot> persist = Lists.newArrayList(); |
| synchronized (this) { |
| final int size = mGrantedUriPermissions.size(); |
| for (int i = 0; i < size; i++) { |
| final ArrayMap<GrantUri, UriPermission> 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<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid); |
| if (perms == null) return false; |
| if (perms.size() < MAX_PERSISTED_URI_GRANTS) return false; |
| |
| final ArrayList<UriPermission> 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<android.content.UriPermission> 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<android.content.UriPermission> result = Lists.newArrayList(); |
| synchronized (this) { |
| if (incoming) { |
| final ArrayMap<GrantUri, UriPermission> 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<GrantUri, UriPermission> 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<android.content.UriPermission>(result); |
| } |
| |
| @Override |
| public ParceledListSlice<GrantedUriPermission> getGrantedUriPermissions( |
| @Nullable String packageName, int userId) { |
| enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS, |
| "getGrantedUriPermissions"); |
| |
| final List<GrantedUriPermission> result = new ArrayList<>(); |
| synchronized (this) { |
| final int size = mGrantedUriPermissions.size(); |
| for (int i = 0; i < size; i++) { |
| final ArrayMap<GrantUri, UriPermission> 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<RunningTaskInfo> getTasks(int maxNum) { |
| return mActivityTaskManager.getTasks(maxNum); |
| } |
| |
| @Override |
| public List<RunningTaskInfo> getFilteredTasks(int maxNum, @ActivityType int ignoreActivityType, |
| @WindowingMode int ignoreWindowingMode) { |
| return mActivityTaskManager.getFilteredTasks( |
| maxNum, ignoreActivityType, ignoreWindowingMode); |
| } |
| |
| @Override |
| public void cancelTaskWindowTransition(int taskId) { |
| mActivityTaskManager.cancelTaskWindowTransition(taskId); |
| } |
| |
| boolean isGetTasksAllowed(String caller, int callingPid, int callingUid) { |
| if (mActivityTaskManager.getRecentTasks().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 void setTaskResizeable(int taskId, int resizeableMode) { |
| mActivityTaskManager.setTaskResizeable(taskId, resizeableMode); |
| } |
| |
| @Override |
| public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) { |
| return mActivityTaskManager.getTaskSnapshot(taskId, reducedResolution); |
| } |
| |
| @Override |
| public void resizeTask(int taskId, Rect bounds, int resizeMode) { |
| mActivityTaskManager.resizeTask(taskId, bounds, resizeMode); |
| } |
| |
| @Override |
| public Rect getTaskBounds(int taskId) { |
| return mActivityTaskManager.getTaskBounds(taskId); |
| } |
| |
| @Override |
| public void removeStack(int stackId) { |
| mActivityTaskManager.removeStack(stackId); |
| } |
| |
| @Override |
| public boolean removeTask(int taskId) { |
| return mActivityTaskManager.removeTask(taskId); |
| } |
| |
| @Override |
| public void moveTaskToFront(int taskId, int flags, Bundle bOptions) { |
| mActivityTaskManager.moveTaskToFront(taskId, flags, bOptions); |
| } |
| |
| /** |
| * 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) { |
| return mActivityTaskManager.moveActivityTaskToBack(token, nonRoot); |
| } |
| |
| @Override |
| public void moveTaskToStack(int taskId, int stackId, boolean toTop) { |
| mActivityTaskManager.moveTaskToStack(taskId, stackId, toTop); |
| } |
| |
| @Override |
| public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode, |
| boolean preserveWindows, boolean animate, int animationDuration) { |
| mActivityTaskManager.resizeStack(stackId, destBounds, allowResizeInDockedMode, |
| preserveWindows, animate, animationDuration); |
| } |
| |
| @Override |
| public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, |
| int userId) { |
| return mActivityTaskManager.getRecentTasks(maxNum, flags, userId); |
| } |
| |
| /** |
| * 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) { |
| return mActivityTaskManager.moveTopActivityToPinnedStack(stackId, bounds); |
| } |
| |
| @Override |
| public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds, |
| Rect tempDockedTaskInsetBounds, |
| Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) { |
| mActivityTaskManager.resizeDockedStack(dockedBounds, tempDockedTaskBounds, |
| tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds); |
| } |
| |
| @Override |
| public void positionTaskInStack(int taskId, int stackId, int position) { |
| mActivityTaskManager.positionTaskInStack(taskId, stackId, position); |
| } |
| |
| @Override |
| public List<StackInfo> getAllStackInfos() { |
| return mActivityTaskManager.getAllStackInfos(); |
| } |
| |
| @Override |
| public int getTaskForActivity(IBinder token, boolean onlyRoot) { |
| return mActivityTaskManager.getTaskForActivity(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)); |
| mActivityTaskManager.getLockTaskController().updateLockTaskPackages(userId, packages); |
| } |
| } |
| |
| @Override |
| public boolean isInLockTaskMode() { |
| return mActivityTaskManager.isInLockTaskMode(); |
| } |
| |
| @Override |
| public int getLockTaskModeState() { |
| return mActivityTaskManager.getLockTaskModeState(); |
| } |
| |
| @Override |
| public void startSystemLockTaskMode(int taskId) throws RemoteException { |
| mActivityTaskManager.startSystemLockTaskMode(taskId); |
| } |
| |
| // ========================================================= |
| // CONTENT PROVIDERS |
| // ========================================================= |
| |
| private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) { |
| List<ProviderInfo> 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<N; i++) { |
| // TODO: keep logic in sync with installEncryptionUnawareProviders |
| ProviderInfo cpi = |
| (ProviderInfo)providers.get(i); |
| boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo, |
| cpi.name, cpi.flags); |
| if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) { |
| // This is a singleton provider, but a user besides the |
| // default user is asking to initialize a process it runs |
| // in... well, no, it doesn't actually run in this process, |
| // it runs in the process of the default user. Get rid of it. |
| providers.remove(i); |
| N--; |
| i--; |
| continue; |
| } |
| |
| ComponentName comp = new ComponentName(cpi.packageName, cpi.name); |
| ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId); |
| if (cpr == null) { |
| cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton); |
| mProviderMap.putProviderByClass(comp, cpr); |
| } |
| if (DEBUG_MU) Slog.v(TAG_MU, |
| "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid); |
| app.pubProviders.put(cpi.name, cpr); |
| if (!cpi.multiprocess || !"android".equals(cpi.packageName)) { |
| // Don't add this if it is a platform component that is marked |
| // to run in multiple processes, because this is actually |
| // part of the framework so doesn't make sense to track as a |
| // separate apk in the process. |
| app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode, |
| mProcessStats); |
| } |
| notifyPackageUse(cpi.applicationInfo.packageName, |
| PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER); |
| } |
| } |
| return providers; |
| } |
| |
| /** |
| * Check if the calling UID has a possible chance at accessing the provider |
| * at the given authority and user. |
| */ |
| public String checkContentProviderAccess(String authority, int userId) { |
| if (userId == UserHandle.USER_ALL) { |
| mContext.enforceCallingOrSelfPermission( |
| Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG); |
| userId = UserHandle.getCallingUserId(); |
| } |
| |
| ProviderInfo cpi = null; |
| try { |
| cpi = AppGlobals.getPackageManager().resolveContentProvider(authority, |
| STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS |
| | PackageManager.MATCH_DISABLED_COMPONENTS |
| | PackageManager.MATCH_DIRECT_BOOT_AWARE |
| | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, |
| userId); |
| } catch (RemoteException ignored) { |
| } |
| if (cpi == null) { |
| return "Failed to find provider " + authority + " for user " + userId |
| + "; expected to find a valid ContentProvider for this authority"; |
| } |
| |
| ProcessRecord r = null; |
| synchronized (mPidsSelfLocked) { |
| r = mPidsSelfLocked.get(Binder.getCallingPid()); |
| } |
| if (r == null) { |
| return "Failed to find PID " + Binder.getCallingPid(); |
| } |
| |
| synchronized (this) { |
| return checkContentProviderPermissionLocked(cpi, r, userId, true); |
| } |
| } |
| |
| /** |
| * Check if {@link ProcessRecord} has a possible chance at accessing the |
| * given {@link ProviderInfo}. Final permission checking is always done |
| * in {@link ContentProvider}. |
| */ |
| private final String checkContentProviderPermissionLocked( |
| ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser) { |
| final int callingPid = (r != null) ? r.pid : Binder.getCallingPid(); |
| final int callingUid = (r != null) ? r.uid : Binder.getCallingUid(); |
| boolean checkedGrants = false; |
| if (checkUser) { |
| // Looking for cross-user grants before enforcing the typical cross-users permissions |
| int tmpTargetUserId = mUserController.unsafeConvertIncomingUser(userId); |
| if (tmpTargetUserId != UserHandle.getUserId(callingUid)) { |
| if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) { |
| return null; |
| } |
| checkedGrants = true; |
| } |
| userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false, |
| ALLOW_NON_FULL, "checkContentProviderPermissionLocked " + cpi.authority, null); |
| if (userId != tmpTargetUserId) { |
| // When we actually went to determine the final targer user ID, this ended |
| // up different than our initial check for the authority. This is because |
| // they had asked for USER_CURRENT_OR_SELF and we ended up switching to |
| // SELF. So we need to re-check the grants again. |
| checkedGrants = false; |
| } |
| } |
| if (checkComponentPermission(cpi.readPermission, callingPid, callingUid, |
| cpi.applicationInfo.uid, cpi.exported) |
| == PackageManager.PERMISSION_GRANTED) { |
| return null; |
| } |
| if (checkComponentPermission(cpi.writePermission, callingPid, callingUid, |
| cpi.applicationInfo.uid, cpi.exported) |
| == PackageManager.PERMISSION_GRANTED) { |
| return null; |
| } |
| |
| PathPermission[] pps = cpi.pathPermissions; |
| if (pps != null) { |
| int i = pps.length; |
| while (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<GrantUri, UriPermission> 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<r.conProviders.size(); i++) { |
| ContentProviderConnection conn = r.conProviders.get(i); |
| if (conn.provider == cpr) { |
| if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER, |
| "Adding provider requested by " |
| + r.processName + " from process " |
| + cpr.info.processName + ": " + cpr.name.flattenToShortString() |
| + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount); |
| if (stable) { |
| conn.stableCount++; |
| conn.numStableIncs++; |
| } else { |
| conn.unstableCount++; |
| conn.numUnstableIncs++; |
| } |
| return conn; |
| } |
| } |
| ContentProviderConnection conn = new ContentProviderConnection(cpr, r); |
| if (stable) { |
| conn.stableCount = 1; |
| conn.numStableIncs = 1; |
| } else { |
| conn.unstableCount = 1; |
| conn.numUnstableIncs = 1; |
| } |
| cpr.connections.add(conn); |
| r.conProviders.add(conn); |
| startAssociationLocked(r.uid, r.processName, r.curProcState, |
| cpr.uid, cpr.name, cpr.info.processName); |
| return conn; |
| } |
| cpr.addExternalProcessHandleLocked(externalProcessToken); |
| return null; |
| } |
| |
| boolean decProviderCountLocked(ContentProviderConnection conn, |
| ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) { |
| if (conn != null) { |
| cpr = conn.provider; |
| if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER, |
| "Removing provider requested by " |
| + conn.client.processName + " from process " |
| + cpr.info.processName + ": " + cpr.name.flattenToShortString() |
| + " scnt=" + conn.stableCount + " uscnt=" + conn.unstableCount); |
| if (stable) { |
| conn.stableCount--; |
| } else { |
| conn.unstableCount--; |
| } |
| if (conn.stableCount == 0 && conn.unstableCount == 0) { |
| cpr.connections.remove(conn); |
| conn.client.conProviders.remove(conn); |
| if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) { |
| // The client is more important than last activity -- note the time this |
| // is happening, so we keep the old provider process around a bit as last |
| // activity to avoid thrashing it. |
| if (cpr.proc != null) { |
| cpr.proc.lastProviderTime = SystemClock.uptimeMillis(); |
| } |
| } |
| stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name); |
| return true; |
| } |
| return false; |
| } |
| cpr.removeExternalProcessHandleLocked(externalProcessToken); |
| return false; |
| } |
| |
| private void checkTime(long startTime, String where) { |
| long now = SystemClock.uptimeMillis(); |
| if ((now-startTime) > 50) { |
| // If we are taking more than 50ms, log about it. |
| Slog.w(TAG, "Slow operation: " + (now-startTime) + "ms so far, now at " + where); |
| } |
| } |
| |
| private static final int[] PROCESS_STATE_STATS_FORMAT = new int[] { |
| PROC_SPACE_TERM, |
| PROC_SPACE_TERM|PROC_PARENS, |
| PROC_SPACE_TERM|PROC_CHAR|PROC_OUT_LONG, // 3: process state |
| }; |
| |
| private final long[] mProcessStateStatsLongs = new long[1]; |
| |
| private boolean isProcessAliveLocked(ProcessRecord proc) { |
| if (proc.pid <= 0) { |
| if (DEBUG_OOM_ADJ) Slog.d(TAG, "Process hasn't started yet: " + proc); |
| return false; |
| } |
| if (proc.procStatFile == null) { |
| proc.procStatFile = "/proc/" + proc.pid + "/stat"; |
| } |
| mProcessStateStatsLongs[0] = 0; |
| if (!readProcFile(proc.procStatFile, PROCESS_STATE_STATS_FORMAT, null, |
| mProcessStateStatsLongs, null)) { |
| if (DEBUG_OOM_ADJ) Slog.d(TAG, "UNABLE TO RETRIEVE STATE FOR " + proc.procStatFile); |
| return false; |
| } |
| final long state = mProcessStateStatsLongs[0]; |
| if (DEBUG_OOM_ADJ) Slog.d(TAG, "RETRIEVED STATE FOR " + proc.procStatFile + ": " |
| + (char)state); |
| return state != 'Z' && state != 'X' && state != 'x' && state != 'K'; |
| } |
| |
| private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, |
| String name, IBinder token, boolean stable, int userId) { |
| ContentProviderRecord cpr; |
| ContentProviderConnection conn = null; |
| ProviderInfo cpi = null; |
| |
| synchronized(this) { |
| long startTime = SystemClock.uptimeMillis(); |
| |
| ProcessRecord r = null; |
| if (caller != null) { |
| r = getRecordForAppLocked(caller); |
| if (r == null) { |
| throw new SecurityException( |
| "Unable to find app for caller " + caller |
| + " (pid=" + Binder.getCallingPid() |
| + ") when getting content provider " + name); |
| } |
| } |
| |
| boolean checkCrossUser = true; |
| |
| checkTime(startTime, "getContentProviderImpl: getProviderByName"); |
| |
| // First check if this content provider has been published... |
| cpr = mProviderMap.getProviderByName(name, userId); |
| // If that didn't work, check if it exists for user 0 and then |
| // verify that it's a singleton provider before using it. |
| if (cpr == null && userId != UserHandle.USER_SYSTEM) { |
| cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM); |
| if (cpr != null) { |
| cpi = cpr.info; |
| if (isSingleton(cpi.processName, cpi.applicationInfo, |
| cpi.name, cpi.flags) |
| && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) { |
| userId = UserHandle.USER_SYSTEM; |
| checkCrossUser = false; |
| } else { |
| cpr = null; |
| cpi = null; |
| } |
| } |
| } |
| |
| boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed; |
| if (providerRunning) { |
| cpi = cpr.info; |
| String msg; |
| |
| if (r != null && cpr.canRunHere(r)) { |
| checkTime(startTime, |
| "getContentProviderImpl: before checkContentProviderPermission"); |
| if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) |
| != null) { |
| throw new SecurityException(msg); |
| } |
| checkTime(startTime, |
| "getContentProviderImpl: after checkContentProviderPermission"); |
| |
| // This provider has been published or is in the process |
| // of being published... but it is also allowed to run |
| // in the caller's process, so don't make a connection |
| // and just let the caller instantiate its own instance. |
| ContentProviderHolder holder = cpr.newHolder(null); |
| // don't give caller the provider object, it needs |
| // to make its own. |
| holder.provider = null; |
| return holder; |
| } |
| |
| // Don't expose providers between normal apps and instant apps |
| try { |
| if (AppGlobals.getPackageManager() |
| .resolveContentProvider(name, 0 /*flags*/, userId) == null) { |
| return null; |
| } |
| } catch (RemoteException e) { |
| } |
| |
| checkTime(startTime, |
| "getContentProviderImpl: before checkContentProviderPermission"); |
| if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) |
| != null) { |
| throw new SecurityException(msg); |
| } |
| checkTime(startTime, |
| "getContentProviderImpl: after checkContentProviderPermission"); |
| |
| final long origId = Binder.clearCallingIdentity(); |
| |
| checkTime(startTime, "getContentProviderImpl: incProviderCountLocked"); |
| |
| // In this case the provider instance already exists, so we can |
| // return it right away. |
| conn = incProviderCountLocked(r, cpr, token, stable); |
| if (conn != null && (conn.stableCount+conn.unstableCount) == 1) { |
| if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { |
| // If this is a perceptible app accessing the provider, |
| // make sure to count it as being accessed and thus |
| // back up on the LRU list. This is good because |
| // content providers are often expensive to start. |
| checkTime(startTime, "getContentProviderImpl: before updateLruProcess"); |
| updateLruProcessLocked(cpr.proc, false, null); |
| checkTime(startTime, "getContentProviderImpl: after updateLruProcess"); |
| } |
| } |
| |
| checkTime(startTime, "getContentProviderImpl: before updateOomAdj"); |
| final int verifiedAdj = cpr.proc.verifiedAdj; |
| boolean success = updateOomAdjLocked(cpr.proc, true); |
| // XXX things have changed so updateOomAdjLocked doesn't actually tell us |
| // if the process has been successfully adjusted. So to reduce races with |
| // it, we will check whether the process still exists. Note that this doesn't |
| // completely get rid of races with LMK killing the process, but should make |
| // them much smaller. |
| if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) { |
| success = false; |
| } |
| maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name); |
| checkTime(startTime, "getContentProviderImpl: after updateOomAdj"); |
| if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success); |
| // NOTE: there is still a race here where a signal could be |
| // pending on the process even though we managed to update its |
| // adj level. Not sure what to do about this, but at least |
| // the race is now smaller. |
| if (!success) { |
| // Uh oh... it looks like the provider's process |
| // has been killed on us. We need to wait for a new |
| // process to be started, and make sure its death |
| // doesn't kill our process. |
| Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString() |
| + " is crashing; detaching " + r); |
| boolean lastRef = decProviderCountLocked(conn, cpr, token, stable); |
| checkTime(startTime, "getContentProviderImpl: before appDied"); |
| appDiedLocked(cpr.proc); |
| checkTime(startTime, "getContentProviderImpl: after appDied"); |
| if (!lastRef) { |
| // This wasn't the last ref our process had on |
| // the provider... we have now been killed, bail. |
| return null; |
| } |
| providerRunning = false; |
| conn = null; |
| } else { |
| cpr.proc.verifiedAdj = cpr.proc.setAdj; |
| } |
| |
| Binder.restoreCallingIdentity(origId); |
| } |
| |
| if (!providerRunning) { |
| try { |
| checkTime(startTime, "getContentProviderImpl: before resolveContentProvider"); |
| cpi = AppGlobals.getPackageManager(). |
| resolveContentProvider(name, |
| STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); |
| checkTime(startTime, "getContentProviderImpl: after resolveContentProvider"); |
| } catch (RemoteException ex) { |
| } |
| if (cpi == null) { |
| return null; |
| } |
| // If the provider is a singleton AND |
| // (it's a call within the same user || the provider is a |
| // privileged app) |
| // Then allow connecting to the singleton provider |
| boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo, |
| cpi.name, cpi.flags) |
| && isValidSingletonCall(r.uid, cpi.applicationInfo.uid); |
| if (singleton) { |
| userId = UserHandle.USER_SYSTEM; |
| } |
| cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId); |
| checkTime(startTime, "getContentProviderImpl: got app info for user"); |
| |
| String msg; |
| checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); |
| if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton)) |
| != null) { |
| throw new SecurityException(msg); |
| } |
| checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission"); |
| |
| if (!mProcessesReady |
| && !cpi.processName.equals("system")) { |
| // If this content provider does not run in the system |
| // process, and the system is not yet ready to run other |
| // processes, then fail fast instead of hanging. |
| throw new IllegalArgumentException( |
| "Attempt to launch content provider before system ready"); |
| } |
| |
| // If system providers are not installed yet we aggressively crash to avoid |
| // creating multiple instance of these providers and then bad things happen! |
| if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp() |
| && "system".equals(cpi.processName)) { |
| throw new IllegalStateException("Cannot access system provider: '" |
| + cpi.authority + "' before system providers are installed!"); |
| } |
| |
| // Make sure that the user who owns this provider is running. If not, |
| // we don't want to allow it to run. |
| if (!mUserController.isUserRunning(userId, 0)) { |
| Slog.w(TAG, "Unable to launch app " |
| + cpi.applicationInfo.packageName + "/" |
| + cpi.applicationInfo.uid + " for provider " |
| + name + ": user " + userId + " is stopped"); |
| return null; |
| } |
| |
| ComponentName comp = new ComponentName(cpi.packageName, cpi.name); |
| checkTime(startTime, "getContentProviderImpl: before getProviderByClass"); |
| cpr = mProviderMap.getProviderByClass(comp, userId); |
| checkTime(startTime, "getContentProviderImpl: after getProviderByClass"); |
| final boolean firstClass = cpr == null; |
| if (firstClass) { |
| final long ident = Binder.clearCallingIdentity(); |
| |
| // If permissions need a review before any of the app components can run, |
| // we return no provider and launch a review activity if the calling app |
| // is in the foreground. |
| if (mPermissionReviewRequired) { |
| if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) { |
| return null; |
| } |
| } |
| |
| try { |
| checkTime(startTime, "getContentProviderImpl: before getApplicationInfo"); |
| ApplicationInfo ai = |
| AppGlobals.getPackageManager(). |
| getApplicationInfo( |
| cpi.applicationInfo.packageName, |
| STOCK_PM_FLAGS, userId); |
| checkTime(startTime, "getContentProviderImpl: after getApplicationInfo"); |
| if (ai == null) { |
| Slog.w(TAG, "No package info for content provider " |
| + cpi.name); |
| return null; |
| } |
| ai = getAppInfoForUser(ai, userId); |
| cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton); |
| } catch (RemoteException ex) { |
| // pm is in same process, this will never happen. |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord"); |
| |
| if (r != null && cpr.canRunHere(r)) { |
| // If this is a multiprocess provider, then just return its |
| // info and allow the caller to instantiate it. Only do |
| // this if the provider is the same user as the caller's |
| // process, or can run as root (so can be in any process). |
| return cpr.newHolder(null); |
| } |
| |
| if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid " |
| + (r != null ? r.uid : null) + " pruid " + cpr.appInfo.uid + "): " |
| + cpr.info.name + " callers=" + Debug.getCallers(6)); |
| |
| // This is single process, and our app is now connecting to it. |
| // See if we are already in the process of launching this |
| // provider. |
| final int N = mLaunchingProviders.size(); |
| int i; |
| for (i = 0; i < N; i++) { |
| if (mLaunchingProviders.get(i) == cpr) { |
| break; |
| } |
| } |
| |
| // If the provider is not already being launched, then get it |
| // started. |
| if (i >= N) { |
| final long origId = Binder.clearCallingIdentity(); |
| |
| try { |
| // Content provider is now in use, its package can't be stopped. |
| try { |
| checkTime(startTime, "getContentProviderImpl: before set stopped state"); |
| AppGlobals.getPackageManager().setPackageStoppedState( |
| cpr.appInfo.packageName, false, userId); |
| checkTime(startTime, "getContentProviderImpl: after set stopped state"); |
| } catch (RemoteException e) { |
| } catch (IllegalArgumentException e) { |
| Slog.w(TAG, "Failed trying to unstop package " |
| + cpr.appInfo.packageName + ": " + e); |
| } |
| |
| // Use existing process if already started |
| checkTime(startTime, "getContentProviderImpl: looking for process record"); |
| ProcessRecord proc = getProcessRecordLocked( |
| cpi.processName, cpr.appInfo.uid, false); |
| if (proc != null && proc.thread != null && !proc.killed) { |
| if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER, |
| "Installing in existing process " + proc); |
| if (!proc.pubProviders.containsKey(cpi.name)) { |
| checkTime(startTime, "getContentProviderImpl: scheduling install"); |
| proc.pubProviders.put(cpi.name, cpr); |
| try { |
| proc.thread.scheduleInstallProvider(cpi); |
| } catch (RemoteException e) { |
| } |
| } |
| } else { |
| checkTime(startTime, "getContentProviderImpl: before start process"); |
| proc = startProcessLocked(cpi.processName, |
| cpr.appInfo, false, 0, "content provider", |
| new ComponentName(cpi.applicationInfo.packageName, |
| cpi.name), false, false, false); |
| checkTime(startTime, "getContentProviderImpl: after start process"); |
| if (proc == null) { |
| Slog.w(TAG, "Unable to launch app " |
| + cpi.applicationInfo.packageName + "/" |
| + cpi.applicationInfo.uid + " for provider " |
| + name + ": process is bad"); |
| return null; |
| } |
| } |
| cpr.launchingApp = proc; |
| mLaunchingProviders.add(cpr); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| checkTime(startTime, "getContentProviderImpl: updating data structures"); |
| |
| // Make sure the provider is published (the same provider class |
| // may be published under multiple names). |
| if (firstClass) { |
| mProviderMap.putProviderByClass(comp, cpr); |
| } |
| |
| mProviderMap.putProviderByName(name, cpr); |
| conn = incProviderCountLocked(r, cpr, token, stable); |
| if (conn != null) { |
| conn.waiting = true; |
| } |
| } |
| checkTime(startTime, "getContentProviderImpl: done!"); |
| |
| grantEphemeralAccessLocked(userId, null /*intent*/, |
| cpi.applicationInfo.uid, UserHandle.getAppId(Binder.getCallingUid())); |
| } |
| |
| // Wait for the provider to be published... |
| synchronized (cpr) { |
| while (cpr.provider == null) { |
| if (cpr.launchingApp == null) { |
| Slog.w(TAG, "Unable to launch app " |
| + cpi.applicationInfo.packageName + "/" |
| + cpi.applicationInfo.uid + " for provider " |
| + name + ": launching app became null"); |
| EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS, |
| UserHandle.getUserId(cpi.applicationInfo.uid), |
| cpi.applicationInfo.packageName, |
| cpi.applicationInfo.uid, name); |
| return null; |
| } |
| try { |
| if (DEBUG_MU) Slog.v(TAG_MU, |
| "Waiting to start provider " + cpr |
| + " launchingApp=" + cpr.launchingApp); |
| if (conn != null) { |
| conn.waiting = true; |
| } |
| cpr.wait(); |
| } catch (InterruptedException ex) { |
| } finally { |
| if (conn != null) { |
| conn.waiting = false; |
| } |
| } |
| } |
| } |
| return cpr != null ? cpr.newHolder(conn) : null; |
| } |
| |
| private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi, |
| ProcessRecord r, final int userId) { |
| if (getPackageManagerInternalLocked().isPermissionsReviewRequired( |
| cpi.packageName, userId)) { |
| |
| final boolean callerForeground = r == null || r.setSchedGroup |
| != ProcessList.SCHED_GROUP_BACKGROUND; |
| |
| // Show a permission review UI only for starting from a foreground app |
| if (!callerForeground) { |
| Slog.w(TAG, "u" + userId + " Instantiating a provider in package" |
| + cpi.packageName + " requires a permissions review"); |
| return false; |
| } |
| |
| final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); |
| intent.addFlags(FLAG_ACTIVITY_NEW_TASK |
| | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); |
| intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName); |
| |
| if (DEBUG_PERMISSIONS_REVIEW) { |
| Slog.i(TAG, "u" + userId + " Launching permission review " |
| + "for package " + cpi.packageName); |
| } |
| |
| final UserHandle userHandle = new UserHandle(userId); |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mContext.startActivityAsUser(intent, userHandle); |
| } |
| }); |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Returns the PackageManager. Used by classes hosted by {@link ActivityManagerService}. The |
| * PackageManager could be unavailable at construction time and therefore needs to be accessed |
| * on demand. |
| */ |
| IPackageManager getPackageManager() { |
| return AppGlobals.getPackageManager(); |
| } |
| |
| PackageManagerInternal getPackageManagerInternalLocked() { |
| if (mPackageManagerInt == null) { |
| mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); |
| } |
| return mPackageManagerInt; |
| } |
| |
| @Override |
| public final ContentProviderHolder getContentProvider( |
| IApplicationThread caller, String name, int userId, boolean stable) { |
| enforceNotIsolatedCaller("getContentProvider"); |
| if (caller == null) { |
| String msg = "null IApplicationThread when getting content provider " |
| + name; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal |
| // with cross-user grant. |
| return getContentProviderImpl(caller, name, null, stable, userId); |
| } |
| |
| public ContentProviderHolder getContentProviderExternal( |
| String name, int userId, IBinder token) { |
| enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY, |
| "Do not have permission in call getContentProviderExternal()"); |
| userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), |
| userId, false, ALLOW_FULL_ONLY, "getContentProvider", null); |
| return getContentProviderExternalUnchecked(name, token, userId); |
| } |
| |
| private ContentProviderHolder getContentProviderExternalUnchecked(String name, |
| IBinder token, int userId) { |
| return getContentProviderImpl(null, name, token, true, userId); |
| } |
| |
| /** |
| * Drop a content provider from a ProcessRecord's bookkeeping |
| */ |
| public void removeContentProvider(IBinder connection, boolean stable) { |
| enforceNotIsolatedCaller("removeContentProvider"); |
| long ident = Binder.clearCallingIdentity(); |
| try { |
| synchronized (this) { |
| ContentProviderConnection conn; |
| try { |
| conn = (ContentProviderConnection)connection; |
| } catch (ClassCastException e) { |
| String msg ="removeContentProvider: " + connection |
| + " not a ContentProviderConnection"; |
| Slog.w(TAG, msg); |
| throw new IllegalArgumentException(msg); |
| } |
| if (conn == null) { |
| throw new NullPointerException("connection is null"); |
| } |
| if (decProviderCountLocked(conn, null, null, stable)) { |
| updateOomAdjLocked(); |
| } |
| } |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| public void removeContentProviderExternal(String name, IBinder token) { |
| enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY, |
| "Do not have permission in call removeContentProviderExternal()"); |
| int userId = UserHandle.getCallingUserId(); |
| long ident = Binder.clearCallingIdentity(); |
| try { |
| removeContentProviderExternalUnchecked(name, token, userId); |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| private void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) { |
| synchronized (this) { |
| ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId); |
| if(cpr == null) { |
| //remove from mProvidersByClass |
| if(DEBUG_ALL) Slog.v(TAG, name+" content provider not found in providers list"); |
| return; |
| } |
| |
| //update content provider record entry info |
| ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name); |
| ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId); |
| if (localCpr.hasExternalProcessHandles()) { |
| if (localCpr.removeExternalProcessHandleLocked(token)) { |
| updateOomAdjLocked(); |
| } else { |
| Slog.e(TAG, "Attmpt to remove content provider " + localCpr |
| + " with no external reference for token: " |
| + token + "."); |
| } |
| } else { |
| Slog.e(TAG, "Attmpt to remove content provider: " + localCpr |
| + " with no external references."); |
| } |
| } |
| } |
| |
| public final void publishContentProviders(IApplicationThread caller, |
| List<ContentProviderHolder> 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<ProviderInfo> 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<ApplicationInfo> 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<ProcessRecord> 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); |
| StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, info.uid, uid, |
| StatsLog.ISOLATED_UID_CHANGED__EVENT__CREATED); |
| } |
| 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() { |
| mActivityTaskManager.unhandledBack(); |
| } |
| |
| 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 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(); |
| } |
| } |
| |
| @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(); |
| mActivityTaskManager.notifyTaskPersisterLocked(null, true); |
| } |
| |
| return timedout; |
| } |
| |
| @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) { |
| mActivityTaskManager.setLockScreenShown( |
| keyguardShowing, aodShowing, secondaryDisplayShowing); |
| } |
| |
| @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 (mActivityTaskManager.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; |
| mActivityTaskManager.getActivityStartController().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<String, Integer> 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 (mActivityTaskManager.getRecentTasks().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; |
| } |
| |
| 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 void setPersistentVrThread(int tid) { |
| mActivityTaskManager.setPersistentVrThread(tid); |
| } |
| |
| /** |
| * 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. |
| */ |
| 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 boolean isVrModePackageEnabled(ComponentName packageName) { |
| enforceSystemHasVrFeature(); |
| |
| final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); |
| |
| return vrService.hasVrPackage(packageName, UserHandle.getCallingUserId()) == |
| VrManagerInternal.NO_ERROR; |
| } |
| |
| public boolean isTopActivityImmersive() { |
| return mActivityTaskManager.isTopActivityImmersive(); |
| } |
| |
| @Override |
| public boolean isTopOfTask(IBinder token) { |
| return mActivityTaskManager.isTopOfTask(token); |
| } |
| |
| @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); |
| if (workSource != null) { |
| StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, workSource, tag); |
| } else { |
| StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, sourceUid, null, 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<pids.length; i++) { |
| ProcessRecord proc = mPidsSelfLocked.get(pids[i]); |
| if (proc != null) { |
| int type = proc.setAdj; |
| if (type > 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<pids.length; i++) { |
| ProcessRecord proc = mPidsSelfLocked.get(pids[i]); |
| if (proc == null) { |
| continue; |
| } |
| int adj = proc.setAdj; |
| if (adj >= 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 = ActivityTaskManager.supportsMultiWindow(mContext); |
| final boolean supportsPictureInPicture = supportsMultiWindow && |
| mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); |
| final boolean supportsSplitScreenMultiWindow = |
| ActivityTaskManager.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); |
| mActivityTaskManager.onSystemReady(); |
| // Make sure we have the current profile info, since it is needed for security checks. |
| mUserController.onSystemReady(); |
| mAppOpsService.systemReady(); |
| mSystemReady = true; |
| } |
| |
| try { |
| sTheRealBuildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface( |
| ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE)) |
| .getSerial(); |
| } catch (RemoteException e) {} |
| |
| ArrayList<ProcessRecord> 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<ProcessRecord>(); |
| } |
| 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<String, Object> data = new HashMap<String, Object>(); |
| 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<NP; ip++) { |
| SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip); |
| final int NA = apps.size(); |
| for (int ia=0; ia<NA; ia++) { |
| ProcessRecord p = apps.valueAt(ia); |
| if (p.thread != null && p.thread.asBinder() == app) { |
| return p; |
| } |
| } |
| } |
| |
| Slog.w(TAG, "Can't find mystery application for " + reason |
| + " from pid=" + Binder.getCallingPid() |
| + " uid=" + Binder.getCallingUid() + ": " + app); |
| return null; |
| } |
| } |
| |
| /** |
| * Utility function for addErrorToDropBox and handleStrictModeViolation's logging |
| * to append various headers to the dropbox log text. |
| */ |
| private void appendDropBoxProcessHeaders(ProcessRecord process, String processName, |
| StringBuilder sb) { |
| // Watchdog thread ends up invoking this function (with |
| // a null ProcessRecord) to add the stack file to dropbox. |
| // Do not acquire a lock on this (am) in such cases, as it |
| // could cause a potential deadlock, if and when watchdog |
| // is invoked due to unavailability of lock on am and it |
| // would prevent watchdog from killing system_server. |
| if (process == null) { |
| sb.append("Process: ").append(processName).append("\n"); |
| return; |
| } |
| // Note: ProcessRecord 'process' is guarded by the service |
| // instance. (notably process.pkgList, which could otherwise change |
| // concurrently during execution of this method) |
| synchronized (this) { |
| sb.append("Process: ").append(processName).append("\n"); |
| sb.append("PID: ").append(process.pid).append("\n"); |
| int flags = process.info.flags; |
| IPackageManager pm = AppGlobals.getPackageManager(); |
| sb.append("Flags: 0x").append(Integer.toHexString(flags)).append("\n"); |
| for (int ip=0; ip<process.pkgList.size(); ip++) { |
| String pkg = process.pkgList.keyAt(ip); |
| sb.append("Package: ").append(pkg); |
| try { |
| PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId()); |
| if (pi != null) { |
| sb.append(" v").append(pi.getLongVersionCode()); |
| if (pi.versionName != null) { |
| sb.append(" (").append(pi.versionName).append(")"); |
| } |
| } |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Error getting package info: " + pkg, e); |
| } |
| sb.append("\n"); |
| } |
| if (process.info.isInstantApp()) { |
| sb.append("Instant-App: true\n"); |
| } |
| } |
| } |
| |
| private static String processClass(ProcessRecord process) { |
| if (process == null || process.pid == MY_PID) { |
| return "system_server"; |
| } else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { |
| return "system_app"; |
| } else { |
| return "data_app"; |
| } |
| } |
| |
| private volatile long mWtfClusterStart; |
| private volatile int mWtfClusterCount; |
| |
| /** |
| * Write a description of an error (crash, WTF, ANR) to the drop box. |
| * @param eventType to include in the drop box tag ("crash", "wtf", etc.) |
| * @param process which caused the error, null means the system server |
| * @param activity which triggered the error, null if unknown |
| * @param parent activity related to the error, null if unknown |
| * @param subject line related to the error, null if absent |
| * @param report in long form describing the error, null if absent |
| * @param dataFile text file to include in the report, null if none |
| * @param crashInfo giving an application stack trace, null if absent |
| */ |
| public void addErrorToDropBox(String eventType, |
| ProcessRecord process, String processName, ActivityRecord activity, |
| ActivityRecord parent, String subject, |
| final String report, final File dataFile, |
| final ApplicationErrorReport.CrashInfo crashInfo) { |
| // NOTE -- this must never acquire the ActivityManagerService lock, |
| // otherwise the watchdog may be prevented from resetting the system. |
| |
| // Bail early if not published yet |
| if (ServiceManager.getService(Context.DROPBOX_SERVICE) == null) return; |
| final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class); |
| |
| // Exit early if the dropbox isn't configured to accept this report type. |
| final String dropboxTag = processClass(process) + "_" + eventType; |
| if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; |
| |
| // Rate-limit how often we're willing to do the heavy lifting below to |
| // collect and record logs; currently 5 logs per 10 second period. |
| final long now = SystemClock.elapsedRealtime(); |
| if (now - mWtfClusterStart > 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<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() { |
| enforceNotIsolatedCaller("getProcessesInErrorState"); |
| // assume our apps are happy - lazy create the list |
| List<ActivityManager.ProcessErrorStateInfo> 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<ActivityManager.ProcessErrorStateInfo>(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<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() { |
| enforceNotIsolatedCaller("getRunningAppProcesses"); |
| |
| final int callingUid = Binder.getCallingUid(); |
| final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid); |
| |
| // Lazy instantiation of list |
| List<ActivityManager.RunningAppProcessInfo> 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<ApplicationInfo> getRunningExternalApplications() { |
| enforceNotIsolatedCaller("getRunningExternalApplications"); |
| List<ActivityManager.RunningAppProcessInfo> runningApps = getRunningAppProcesses(); |
| List<ApplicationInfo> retList = new ArrayList<ApplicationInfo>(); |
| if (runningApps != null && runningApps.size() > 0) { |
| Set<String> extList = new HashSet<String>(); |
| 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 (mActivityTaskManager.getRecentTasks() != null) { |
| mActivityTaskManager.getRecentTasks().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 (mActivityTaskManager.getRecentTasks() != null) { |
| mActivityTaskManager.getRecentTasks().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 (mActivityTaskManager.getRecentTasks() != null) { |
| mActivityTaskManager.getRecentTasks().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(" <no ANR has occurred since boot>"); |
| } 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)"); |
| mActivityTaskManager.getActivityStartController().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<N1; i1++) { |
| ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> targetComponents |
| = mAssociations.valueAt(i1); |
| for (int i2=0, N2=targetComponents.size(); i2<N2; i2++) { |
| SparseArray<ArrayMap<String, Association>> sourceUids |
| = targetComponents.valueAt(i2); |
| for (int i3=0, N3=sourceUids.size(); i3<N3; i3++) { |
| ArrayMap<String, Association> sourceProcesses = sourceUids.valueAt(i3); |
| for (int i4=0, N4=sourceProcesses.size(); i4<N4; i4++) { |
| Association ass = sourceProcesses.valueAt(i4); |
| if (dumpPackage != null) { |
| if (!ass.mTargetComponent.getPackageName().equals(dumpPackage) |
| && UserHandle.getAppId(ass.mSourceUid) != dumpUid) { |
| continue; |
| } |
| } |
| printedAnything = true; |
| pw.print(" "); |
| pw.print(ass.mTargetProcess); |
| pw.print("/"); |
| UserHandle.formatUid(pw, ass.mTargetUid); |
| pw.print(" <- "); |
| pw.print(ass.mSourceProcess); |
| pw.print("/"); |
| UserHandle.formatUid(pw, ass.mSourceUid); |
| pw.println(); |
| pw.print(" via "); |
| pw.print(ass.mTargetComponent.flattenToShortString()); |
| pw.println(); |
| pw.print(" "); |
| long dur = ass.mTime; |
| if (ass.mNesting > 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<ass.mStateTimes.length; i++) { |
| long amt = ass.mStateTimes[i]; |
| if ((ass.mLastState-ActivityManager.MIN_PROCESS_STATE) == i) { |
| amt += now - ass.mLastStateUptime; |
| } |
| if (amt != 0) { |
| pw.print(" "); |
| pw.print(ProcessList.makeProcStateString( |
| i + ActivityManager.MIN_PROCESS_STATE)); |
| pw.print("="); |
| TimeUtils.formatDuration(amt, pw); |
| if ((ass.mLastState-ActivityManager.MIN_PROCESS_STATE) == i) { |
| pw.print("*"); |
| } |
| } |
| } |
| pw.println(); |
| if (ass.mNesting > 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<UidRecord> uids, |
| String header, boolean needSep) { |
| boolean printed = false; |
| for (int i=0; i<uids.size(); i++) { |
| UidRecord uidRec = uids.valueAt(i); |
| if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) { |
| continue; |
| } |
| if (!printed) { |
| printed = true; |
| if (needSep) { |
| pw.println(); |
| } |
| pw.print(" "); |
| pw.println(header); |
| needSep = true; |
| } |
| pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid); |
| pw.print(": "); pw.println(uidRec); |
| } |
| return printed; |
| } |
| |
| boolean dumpBinderProxiesCounts(PrintWriter pw, SparseIntArray counts, String header) { |
| if(counts != null) { |
| pw.println(header); |
| for (int i = 0; i < counts.size(); i++) { |
| final int uid = counts.keyAt(i); |
| final int binderCount = counts.valueAt(i); |
| pw.print(" UID "); |
| pw.print(uid); |
| pw.print(", binder count = "); |
| pw.print(binderCount); |
| pw.print(", package(s)= "); |
| final String[] pkgNames = mContext.getPackageManager().getPackagesForUid(uid); |
| if (pkgNames != null) { |
| for (int j = 0; j < pkgNames.length; j++) { |
| pw.print(pkgNames[j]); |
| pw.print("; "); |
| } |
| } else { |
| pw.print("NO PACKAGE NAME FOUND"); |
| } |
| pw.println(); |
| } |
| pw.println(); |
| return true; |
| } |
| return false; |
| } |
| |
| @GuardedBy("this") |
| void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, |
| int opti, boolean dumpAll, String dumpPackage, int dumpAppId) { |
| boolean needSep = false; |
| int numPers = 0; |
| |
| pw.println("ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)"); |
| |
| if (dumpAll) { |
| final int NP = mProcessNames.getMap().size(); |
| for (int ip=0; ip<NP; ip++) { |
| SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip); |
| final int NA = procs.size(); |
| for (int ia=0; ia<NA; ia++) { |
| ProcessRecord r = procs.valueAt(ia); |
| if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { |
| continue; |
| } |
| if (!needSep) { |
| pw.println(" All known processes:"); |
| needSep = true; |
| } |
| pw.print(r.persistent ? " *PERS*" : " *APP*"); |
| pw.print(" UID "); pw.print(procs.keyAt(ia)); |
| pw.print(" "); pw.println(r); |
| r.dump(pw, " "); |
| if (r.persistent) { |
| numPers++; |
| } |
| } |
| } |
| } |
| |
| if (mIsolatedProcesses.size() > 0) { |
| boolean printed = false; |
| for (int i=0; i<mIsolatedProcesses.size(); i++) { |
| ProcessRecord r = mIsolatedProcesses.valueAt(i); |
| if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { |
| continue; |
| } |
| if (!printed) { |
| if (needSep) { |
| pw.println(); |
| } |
| pw.println(" Isolated process list (sorted by uid):"); |
| printed = true; |
| needSep = true; |
| } |
| pw.print(" Isolated #"); pw.print(i); pw.print(": "); |
| pw.println(r); |
| } |
| } |
| |
| if (mActiveInstrumentation.size() > 0) { |
| boolean printed = false; |
| for (int i=0; i<mActiveInstrumentation.size(); i++) { |
| ActiveInstrumentation ai = mActiveInstrumentation.get(i); |
| if (dumpPackage != null && !ai.mClass.getPackageName().equals(dumpPackage) |
| && !ai.mTargetInfo.packageName.equals(dumpPackage)) { |
| continue; |
| } |
| if (!printed) { |
| if (needSep) { |
| pw.println(); |
| } |
| pw.println(" Active instrumentation:"); |
| printed = true; |
| needSep = true; |
| } |
| pw.print(" Instrumentation #"); pw.print(i); pw.print(": "); |
| pw.println(ai); |
| ai.dump(pw, " "); |
| } |
| } |
| |
| if (mActiveUids.size() > 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<mPidsSelfLocked.size(); i++) { |
| ProcessRecord r = mPidsSelfLocked.valueAt(i); |
| if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { |
| continue; |
| } |
| if (!printed) { |
| if (needSep) pw.println(); |
| needSep = true; |
| pw.println(" PID mappings:"); |
| printed = true; |
| } |
| pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i)); |
| pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i)); |
| } |
| } |
| } |
| |
| if (mImportantProcesses.size() > 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<String, Integer> 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<NI; i++) { |
| final UidObserverRegistration reg = (UidObserverRegistration) |
| mUidObservers.getRegisteredCallbackCookie(i); |
| if (dumpPackage == null || dumpPackage.equals(reg.pkg)) { |
| if (!printed) { |
| pw.println(" mUidObservers:"); |
| printed = true; |
| } |
| pw.print(" "); UserHandle.formatUid(pw, reg.uid); |
| pw.print(" "); pw.print(reg.pkg); pw.print(":"); |
| if ((reg.which&ActivityManager.UID_OBSERVER_IDLE) != 0) { |
| pw.print(" IDLE"); |
| } |
| if ((reg.which&ActivityManager.UID_OBSERVER_ACTIVE) != 0) { |
| pw.print(" ACT" ); |
| } |
| if ((reg.which&ActivityManager.UID_OBSERVER_GONE) != 0) { |
| pw.print(" GONE"); |
| } |
| if ((reg.which&ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { |
| pw.print(" STATE"); |
| pw.print(" (cut="); pw.print(reg.cutpoint); |
| pw.print(")"); |
| } |
| pw.println(); |
| if (reg.lastProcStates != null) { |
| final int NJ = reg.lastProcStates.size(); |
| for (int j=0; j<NJ; j++) { |
| pw.print(" Last "); |
| UserHandle.formatUid(pw, reg.lastProcStates.keyAt(j)); |
| pw.print(": "); pw.println(reg.lastProcStates.valueAt(j)); |
| } |
| } |
| } |
| } |
| pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist)); |
| pw.println(" mDeviceIdleExceptIdleWhitelist=" |
| + Arrays.toString(mDeviceIdleExceptIdleWhitelist)); |
| pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist)); |
| if (mPendingTempWhitelist.size() > 0) { |
| pw.println(" mPendingTempWhitelist:"); |
| for (int i = 0; i < mPendingTempWhitelist.size(); i++) { |
| PendingTempWhitelist ptw = mPendingTempWhitelist.valueAt(i); |
| pw.print(" "); |
| UserHandle.formatUid(pw, ptw.targetUid); |
| pw.print(": "); |
| TimeUtils.formatDuration(ptw.duration, pw); |
| pw.print(" "); |
| pw.println(ptw.tag); |
| } |
| } |
| } |
| if (dumpPackage == null) { |
| pw.println(" mWakefulness=" |
| + PowerManagerInternal.wakefulnessToString(mWakefulness)); |
| pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens); |
| pw.println(" mSleeping=" + mSleeping); |
| pw.println(" mShuttingDown=" + mShuttingDown + " mTestPssMode=" + mTestPssMode); |
| if (mRunningVoice != null) { |
| pw.println(" mRunningVoice=" + mRunningVoice); |
| pw.println(" mVoiceWakeLock" + mVoiceWakeLock); |
| } |
| mActivityTaskManager.dumpVrControllerLocked(pw); |
| } |
| if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient |
| || mOrigWaitForDebugger) { |
| if (dumpPackage == null || dumpPackage.equals(mDebugApp) |
| || dumpPackage.equals(mOrigDebugApp)) { |
| if (needSep) { |
| pw.println(); |
| needSep = false; |
| } |
| pw.println(" mDebugApp=" + mDebugApp + "/orig=" + mOrigDebugApp |
| + " mDebugTransient=" + mDebugTransient |
| + " mOrigWaitForDebugger=" + mOrigWaitForDebugger); |
| } |
| } |
| if (mCurAppTimeTracker != null) { |
| mCurAppTimeTracker.dumpWithHeader(pw, " ", true); |
| } |
| if (mMemWatchProcesses.getMap().size() > 0) { |
| pw.println(" Mem watch processes:"); |
| final ArrayMap<String, SparseArray<Pair<Long, String>>> procs |
| = mMemWatchProcesses.getMap(); |
| for (int i=0; i<procs.size(); i++) { |
| final String proc = procs.keyAt(i); |
| final SparseArray<Pair<Long, String>> uids = procs.valueAt(i); |
| for (int j=0; j<uids.size(); j++) { |
| if (needSep) { |
| pw.println(); |
| needSep = false; |
| } |
| StringBuilder sb = new StringBuilder(); |
| sb.append(" ").append(proc).append('/'); |
| UserHandle.formatUid(sb, uids.keyAt(j)); |
| Pair<Long, String> 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<String, Integer> 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<NP; ip++) { |
| SparseArray<ProcessRecord> procs = mProcessNames.getMap().valueAt(ip); |
| final int NA = procs.size(); |
| for (int ia = 0; ia<NA; ia++) { |
| ProcessRecord r = procs.valueAt(ia); |
| if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { |
| continue; |
| } |
| r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PROCS); |
| if (r.persistent) { |
| numPers++; |
| } |
| } |
| } |
| |
| for (int i=0; i<mIsolatedProcesses.size(); i++) { |
| ProcessRecord r = mIsolatedProcesses.valueAt(i); |
| if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { |
| continue; |
| } |
| r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ISOLATED_PROCS); |
| } |
| |
| for (int i=0; i<mActiveInstrumentation.size(); i++) { |
| ActiveInstrumentation ai = mActiveInstrumentation.get(i); |
| if (dumpPackage != null && !ai.mClass.getPackageName().equals(dumpPackage) |
| && !ai.mTargetInfo.packageName.equals(dumpPackage)) { |
| continue; |
| } |
| ai.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS); |
| } |
| |
| int whichAppId = getAppId(dumpPackage); |
| for (int i=0; i<mActiveUids.size(); i++) { |
| UidRecord uidRec = mActiveUids.valueAt(i); |
| if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) { |
| continue; |
| } |
| uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS); |
| } |
| |
| for (int i=0; i<mValidateUids.size(); i++) { |
| UidRecord uidRec = mValidateUids.valueAt(i); |
| if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) { |
| continue; |
| } |
| uidRec.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS); |
| } |
| |
| if (mLruProcesses.size() > 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<mPidsSelfLocked.size(); i++) { |
| ProcessRecord r = mPidsSelfLocked.valueAt(i); |
| if (!r.pkgList.containsKey(dumpPackage)) { |
| continue; |
| } |
| r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PIDS_SELF_LOCKED); |
| } |
| } |
| } |
| |
| if (mImportantProcesses.size() > 0) { |
| synchronized (mPidsSelfLocked) { |
| for (int i=0; i<mImportantProcesses.size(); i++) { |
| ImportanceToken it = mImportantProcesses.valueAt(i); |
| ProcessRecord r = mPidsSelfLocked.get(it.pid); |
| if (dumpPackage != null && (r == null |
| || !r.pkgList.containsKey(dumpPackage))) { |
| continue; |
| } |
| it.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.IMPORTANT_PROCS); |
| } |
| } |
| } |
| |
| for (int i=0; i<mPersistentStartingProcesses.size(); i++) { |
| ProcessRecord r = mPersistentStartingProcesses.get(i); |
| if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { |
| continue; |
| } |
| r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PERSISTENT_STARTING_PROCS); |
| } |
| |
| for (int i=0; i<mRemovedProcesses.size(); i++) { |
| ProcessRecord r = mRemovedProcesses.get(i); |
| if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { |
| continue; |
| } |
| r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.REMOVED_PROCS); |
| } |
| |
| for (int i=0; i<mProcessesOnHold.size(); i++) { |
| ProcessRecord r = mProcessesOnHold.get(i); |
| if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { |
| continue; |
| } |
| r.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.ON_HOLD_PROCS); |
| } |
| |
| writeProcessesToGcToProto(proto, ActivityManagerServiceDumpProcessesProto.GC_PROCS, dumpPackage); |
| mAppErrors.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.APP_ERRORS, dumpPackage); |
| |
| if (dumpPackage == null) { |
| mUserController.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.USER_CONTROLLER); |
| getGlobalConfiguration().writeToProto(proto, ActivityManagerServiceDumpProcessesProto.GLOBAL_CONFIGURATION); |
| proto.write(ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE, getFocusedStack().mConfigWillChange); |
| } |
| |
| if (mHomeProcess != null && (dumpPackage == null |
| || mHomeProcess.pkgList.containsKey(dumpPackage))) { |
| mHomeProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HOME_PROC); |
| } |
| |
| if (mPreviousProcess != null && (dumpPackage == null |
| || mPreviousProcess.pkgList.containsKey(dumpPackage))) { |
| mPreviousProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC); |
| proto.write(ActivityManagerServiceDumpProcessesProto.PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime); |
| } |
| |
| if (mHeavyWeightProcess != null && (dumpPackage == null |
| || mHeavyWeightProcess.pkgList.containsKey(dumpPackage))) { |
| mHeavyWeightProcess.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.HEAVY_WEIGHT_PROC); |
| } |
| |
| for (Map.Entry<String, Integer> 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<NI; i++) { |
| final UidObserverRegistration reg = (UidObserverRegistration) |
| mUidObservers.getRegisteredCallbackCookie(i); |
| if (dumpPackage == null || dumpPackage.equals(reg.pkg)) { |
| reg.writeToProto(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS); |
| } |
| } |
| |
| for (int v : mDeviceIdleWhitelist) { |
| proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_WHITELIST, v); |
| } |
| |
| for (int v : mDeviceIdleTempWhitelist) { |
| proto.write(ActivityManagerServiceDumpProcessesProto.DEVICE_IDLE_TEMP_WHITELIST, v); |
| } |
| |
| if (mPendingTempWhitelist.size() > 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); |
| } |
| |
| mActivityTaskManager.writeVrControllerToProto( |
| 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<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap(); |
| for (int i=0; i<procs.size(); i++) { |
| final String proc = procs.keyAt(i); |
| final SparseArray<Pair<Long, String>> uids = procs.valueAt(i); |
| final long ptoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS); |
| proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME, proc); |
| for (int j=0; j<uids.size(); j++) { |
| final long utoken = proto.start(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.MEM_STATS); |
| Pair<Long, String> 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<mProcessesToGc.size(); i++) { |
| ProcessRecord r = mProcessesToGc.get(i); |
| if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { |
| continue; |
| } |
| final long token = proto.start(fieldId); |
| r.writeToProto(proto, ProcessToGcProto.PROC); |
| proto.write(ProcessToGcProto.REPORT_LOW_MEMORY, r.reportLowMemory); |
| proto.write(ProcessToGcProto.NOW_UPTIME_MS, now); |
| proto.write(ProcessToGcProto.LAST_GCED_MS, r.lastRequestedGc); |
| proto.write(ProcessToGcProto.LAST_LOW_MEMORY_MS, r.lastLowMemory); |
| proto.end(token); |
| } |
| } |
| } |
| |
| boolean dumpProcessesToGc(PrintWriter pw, boolean needSep, String dumpPackage) { |
| if (mProcessesToGc.size() > 0) { |
| boolean printed = false; |
| long now = SystemClock.uptimeMillis(); |
| for (int i=0; i<mProcessesToGc.size(); i++) { |
| ProcessRecord proc = mProcessesToGc.get(i); |
| if (dumpPackage != null && !dumpPackage.equals(proc.info.packageName)) { |
| continue; |
| } |
| if (!printed) { |
| if (needSep) pw.println(); |
| needSep = true; |
| pw.println(" Processes that are waiting to GC:"); |
| printed = true; |
| } |
| pw.print(" Process "); pw.println(proc); |
| pw.print(" lowMem="); pw.print(proc.reportLowMemory); |
| pw.print(", last gced="); |
| pw.print(now-proc.lastRequestedGc); |
| pw.print(" ms ago, last lowMem="); |
| pw.print(now-proc.lastLowMemory); |
| pw.println(" ms ago"); |
| |
| } |
| } |
| return needSep; |
| } |
| |
| void printOomLevel(PrintWriter pw, String name, int adj) { |
| pw.print(" "); |
| if (adj >= 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<ComponentName> components; |
| ArrayList<String> strings; |
| ArrayList<Integer> objects; |
| boolean all; |
| |
| ItemMatcher() { |
| all = true; |
| } |
| |
| void build(String name) { |
| ComponentName componentName = ComponentName.unflattenFromString(name); |
| if (componentName != null) { |
| if (components == null) { |
| components = new ArrayList<ComponentName>(); |
| } |
| 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<Integer>(); |
| } |
| objects.add(objectId); |
| all = false; |
| } catch (RuntimeException e) { |
| // Not an integer; just do string match. |
| if (strings == null) { |
| strings = new ArrayList<String>(); |
| } |
| strings.add(name); |
| all = false; |
| } |
| } |
| } |
| |
| int build(String[] args, int opti) { |
| for (; opti<args.length; opti++) { |
| String name = args[opti]; |
| if ("--".equals(name)) { |
| return opti+1; |
| } |
| build(name); |
| } |
| return opti; |
| } |
| |
| boolean match(Object object, ComponentName comp) { |
| if (all) { |
| return true; |
| } |
| if (components != null) { |
| for (int i=0; i<components.size(); i++) { |
| if (components.get(i).equals(comp)) { |
| return true; |
| } |
| } |
| } |
| if (objects != null) { |
| for (int i=0; i<objects.size(); i++) { |
| if (System.identityHashCode(object) == objects.get(i)) { |
| return true; |
| } |
| } |
| } |
| if (strings != null) { |
| String flat = comp.flattenToString(); |
| for (int i=0; i<strings.size(); i++) { |
| if (flat.contains(strings.get(i))) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * There are three things that cmd can be: |
| * - a flattened component name that matches an existing activity |
| * - the cmd arg isn't the flattened component name of an existing activity: |
| * dump all activity whose component contains the cmd as a substring |
| * - A hex number of the ActivityRecord object instance. |
| * |
| * @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack |
| * @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack |
| */ |
| protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args, |
| int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) { |
| ArrayList<ActivityRecord> 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<mStickyBroadcasts.size(); user++) { |
| long token = proto.start(ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS); |
| proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user)); |
| for (Map.Entry<String, ArrayList<Intent>> 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<mStickyBroadcasts.size(); user++) { |
| if (needSep) { |
| pw.println(); |
| } |
| needSep = true; |
| printedAnything = true; |
| pw.print(" Sticky broadcasts for user "); |
| pw.print(mStickyBroadcasts.keyAt(user)); pw.println(":"); |
| StringBuilder sb = new StringBuilder(128); |
| for (Map.Entry<String, ArrayList<Intent>> ent |
| : mStickyBroadcasts.valueAt(user).entrySet()) { |
| pw.print(" * Sticky action "); pw.print(ent.getKey()); |
| if (dumpAll) { |
| pw.println(":"); |
| ArrayList<Intent> intents = ent.getValue(); |
| final int N = intents.size(); |
| for (int i=0; i<N; i++) { |
| sb.setLength(0); |
| sb.append(" Intent: "); |
| intents.get(i).toShortString(sb, false, true, false, false); |
| pw.println(sb.toString()); |
| Bundle bundle = intents.get(i).getExtras(); |
| if (bundle != null) { |
| pw.print(" "); |
| pw.println(bundle.toString()); |
| } |
| } |
| } else { |
| pw.println(""); |
| } |
| } |
| } |
| } |
| |
| if (!onlyHistory && dumpAll) { |
| pw.println(); |
| for (BroadcastQueue queue : mBroadcastQueues) { |
| pw.println(" mBroadcastsScheduled [" + queue.mQueueName + "]=" |
| + queue.mBroadcastsScheduled); |
| } |
| pw.println(" mHandler:"); |
| mHandler.dump(new PrintWriterPrinter(pw), " "); |
| needSep = true; |
| printedAnything = true; |
| } |
| |
| if (!printedAnything) { |
| pw.println(" (nothing)"); |
| } |
| } |
| |
| void dumpBroadcastStatsLocked(FileDescriptor fd, PrintWriter pw, String[] args, |
| int opti, boolean dumpAll, String dumpPackage) { |
| if (mCurBroadcastStats == null) { |
| return; |
| } |
| |
| pw.println("ACTIVITY MANAGER BROADCAST STATS STATE (dumpsys activity broadcast-stats)"); |
| final long now = SystemClock.elapsedRealtime(); |
| if (mLastBroadcastStats != null) { |
| pw.print(" Last stats (from "); |
| TimeUtils.formatDuration(mLastBroadcastStats.mStartRealtime, now, pw); |
| pw.print(" to "); |
| TimeUtils.formatDuration(mLastBroadcastStats.mEndRealtime, now, pw); |
| pw.print(", "); |
| TimeUtils.formatDuration(mLastBroadcastStats.mEndUptime |
| - mLastBroadcastStats.mStartUptime, pw); |
| pw.println(" uptime):"); |
| if (!mLastBroadcastStats.dumpStats(pw, " ", dumpPackage)) { |
| pw.println(" (nothing)"); |
| } |
| pw.println(); |
| } |
| pw.print(" Current stats (from "); |
| TimeUtils.formatDuration(mCurBroadcastStats.mStartRealtime, now, pw); |
| pw.print(" to now, "); |
| TimeUtils.formatDuration(SystemClock.uptimeMillis() |
| - mCurBroadcastStats.mStartUptime, pw); |
| pw.println(" uptime):"); |
| if (!mCurBroadcastStats.dumpStats(pw, " ", dumpPackage)) { |
| pw.println(" (nothing)"); |
| } |
| } |
| |
| void dumpBroadcastStatsCheckinLocked(FileDescriptor fd, PrintWriter pw, String[] args, |
| int opti, boolean fullCheckin, String dumpPackage) { |
| if (mCurBroadcastStats == null) { |
| return; |
| } |
| |
| if (mLastBroadcastStats != null) { |
| mLastBroadcastStats.dumpCheckinStats(pw, dumpPackage); |
| if (fullCheckin) { |
| mLastBroadcastStats = null; |
| return; |
| } |
| } |
| mCurBroadcastStats.dumpCheckinStats(pw, dumpPackage); |
| if (fullCheckin) { |
| mCurBroadcastStats = null; |
| } |
| } |
| |
| void dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args, |
| int opti, boolean dumpAll, String dumpPackage) { |
| boolean needSep; |
| boolean printedAnything = false; |
| |
| ItemMatcher matcher = new ItemMatcher(); |
| matcher.build(args, opti); |
| |
| pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)"); |
| |
| needSep = mProviderMap.dumpProvidersLocked(pw, dumpAll, dumpPackage); |
| printedAnything |= needSep; |
| |
| if (mLaunchingProviders.size() > 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<mGrantedUriPermissions.size(); i++) { |
| int uid = mGrantedUriPermissions.keyAt(i); |
| if (dumpUid >= -1 && UserHandle.getAppId(uid) != dumpUid) { |
| continue; |
| } |
| final ArrayMap<GrantUri, UriPermission> 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<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>(); |
| final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>(); |
| final Iterator<WeakReference<PendingIntentRecord>> it |
| = mIntentSenderRecords.values().iterator(); |
| while (it.hasNext()) { |
| WeakReference<PendingIntentRecord> 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<PendingIntentRecord> 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<PendingIntentRecord> 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<Pair<ProcessRecord, Integer>> |
| sortProcessOomList(List<ProcessRecord> origList, String dumpPackage) { |
| ArrayList<Pair<ProcessRecord, Integer>> list |
| = new ArrayList<Pair<ProcessRecord, Integer>>(origList.size()); |
| for (int i=0; i<origList.size(); i++) { |
| ProcessRecord r = origList.get(i); |
| if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) { |
| continue; |
| } |
| list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i)); |
| } |
| |
| Comparator<Pair<ProcessRecord, Integer>> comparator |
| = new Comparator<Pair<ProcessRecord, Integer>>() { |
| @Override |
| public int compare(Pair<ProcessRecord, Integer> object1, |
| Pair<ProcessRecord, Integer> 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<ProcessRecord> origList, |
| boolean inclDetails, String dumpPackage) { |
| ArrayList<Pair<ProcessRecord, Integer>> 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<ProcessRecord> origList, |
| String prefix, String normalLabel, String persistentLabel, |
| boolean inclDetails, String dumpPackage) { |
| |
| ArrayList<Pair<ProcessRecord, Integer>> 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<ProcessRecord> collectProcesses(PrintWriter pw, int start, boolean allPkgs, |
| String[] args) { |
| ArrayList<ProcessRecord> procs; |
| synchronized (this) { |
| if (args != null && args.length > start |
| && args[start].charAt(0) != '-') { |
| procs = new ArrayList<ProcessRecord>(); |
| 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<ProcessRecord>(mLruProcesses); |
| } |
| } |
| return procs; |
| } |
| |
| final void dumpGraphicsHardwareUsage(FileDescriptor fd, |
| PrintWriter pw, String[] args) { |
| ArrayList<ProcessRecord> 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<ProcessRecord> 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<MemItem> 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<MemItem> items) { |
| Collections.sort(items, new Comparator<MemItem>() { |
| @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<MemItem> items, boolean sort, boolean isCompact, boolean dumpSwapPss) { |
| if (sort && !isCompact) { |
| sortMemItems(items); |
| } |
| |
| for (int i=0; i<items.size(); i++) { |
| MemItem mi = items.get(i); |
| if (!isCompact) { |
| if (dumpSwapPss) { |
| pw.printf("%s%s: %-60s (%s in swap)\n", prefix, stringifyKBSize(mi.pss), |
| mi.label, stringifyKBSize(mi.swapPss)); |
| } else { |
| pw.printf("%s%s: %s\n", prefix, stringifyKBSize(mi.pss), mi.label); |
| } |
| } else if (mi.isProc) { |
| pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel); |
| pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss); pw.print(","); |
| pw.print(dumpSwapPss ? mi.swapPss : "N/A"); |
| pw.println(mi.hasActivities ? ",a" : ",e"); |
| } else { |
| pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(","); |
| pw.print(mi.pss); pw.print(","); pw.println(dumpSwapPss ? mi.swapPss : "N/A"); |
| } |
| if (mi.subitems != null) { |
| dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems, |
| true, isCompact, dumpSwapPss); |
| } |
| } |
| } |
| |
| static final void dumpMemItems(ProtoOutputStream proto, long fieldId, String tag, |
| ArrayList<MemItem> items, boolean sort, boolean dumpSwapPss) { |
| if (sort) { |
| sortMemItems(items); |
| } |
| |
| for (int i=0; i<items.size(); i++) { |
| MemItem mi = items.get(i); |
| final long token = proto.start(fieldId); |
| |
| proto.write(MemInfoDumpProto.MemItem.TAG, tag); |
| proto.write(MemInfoDumpProto.MemItem.LABEL, mi.shortLabel); |
| proto.write(MemInfoDumpProto.MemItem.IS_PROC, mi.isProc); |
| proto.write(MemInfoDumpProto.MemItem.ID, mi.id); |
| proto.write(MemInfoDumpProto.MemItem.HAS_ACTIVITIES, mi.hasActivities); |
| proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss); |
| if (dumpSwapPss) { |
| proto.write(MemInfoDumpProto.MemItem.SWAP_PSS_KB, mi.swapPss); |
| } |
| if (mi.subitems != null) { |
| dumpMemItems(proto, MemInfoDumpProto.MemItem.SUB_ITEMS, mi.shortLabel, mi.subitems, |
| true, dumpSwapPss); |
| } |
| proto.end(token); |
| } |
| } |
| |
| // These are in KB. |
| static final long[] DUMP_MEM_BUCKETS = new long[] { |
| 5*1024, 7*1024, 10*1024, 15*1024, 20*1024, 30*1024, 40*1024, 80*1024, |
| 120*1024, 160*1024, 200*1024, |
| 250*1024, 300*1024, 350*1024, 400*1024, 500*1024, 600*1024, 800*1024, |
| 1*1024*1024, 2*1024*1024, 5*1024*1024, 10*1024*1024, 20*1024*1024 |
| }; |
| |
| static final void appendMemBucket(StringBuilder out, long memKB, String label, |
| boolean stackLike) { |
| int start = label.lastIndexOf('.'); |
| if (start >= 0) start++; |
| else start = 0; |
| int end = label.length(); |
| for (int i=0; i<DUMP_MEM_BUCKETS.length; i++) { |
| if (DUMP_MEM_BUCKETS[i] >= memKB) { |
| long bucket = DUMP_MEM_BUCKETS[i]/1024; |
| out.append(bucket); |
| out.append(stackLike ? "MB." : "MB "); |
| out.append(label, start, end); |
| return; |
| } |
| } |
| out.append(memKB/1024); |
| out.append(stackLike ? "MB." : "MB "); |
| out.append(label, start, end); |
| } |
| |
| static final int[] DUMP_MEM_OOM_ADJ = new int[] { |
| ProcessList.NATIVE_ADJ, |
| ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, |
| ProcessList.PERSISTENT_SERVICE_ADJ, ProcessList.FOREGROUND_APP_ADJ, |
| ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, |
| ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, |
| ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, |
| ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MIN_ADJ |
| }; |
| static final String[] DUMP_MEM_OOM_LABEL = new String[] { |
| "Native", |
| "System", "Persistent", "Persistent Service", "Foreground", |
| "Visible", "Perceptible", |
| "Heavy Weight", "Backup", |
| "A Services", "Home", |
| "Previous", "B Services", "Cached" |
| }; |
| static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] { |
| "native", |
| "sys", "pers", "persvc", "fore", |
| "vis", "percept", |
| "heavy", "backup", |
| "servicea", "home", |
| "prev", "serviceb", "cached" |
| }; |
| |
| private final void dumpApplicationMemoryUsageHeader(PrintWriter pw, long uptime, |
| long realtime, boolean isCheckinRequest, boolean isCompact) { |
| if (isCompact) { |
| pw.print("version,"); pw.println(MEMINFO_COMPACT_VERSION); |
| } |
| if (isCheckinRequest || isCompact) { |
| // short checkin version |
| pw.print("time,"); pw.print(uptime); pw.print(","); pw.println(realtime); |
| } else { |
| pw.println("Applications Memory Usage (in Kilobytes):"); |
| pw.println("Uptime: " + uptime + " Realtime: " + realtime); |
| } |
| } |
| |
| private static final int KSM_SHARED = 0; |
| private static final int KSM_SHARING = 1; |
| private static final int KSM_UNSHARED = 2; |
| private static final int KSM_VOLATILE = 3; |
| |
| private final long[] getKsmInfo() { |
| long[] longOut = new long[4]; |
| final int[] SINGLE_LONG_FORMAT = new int[] { |
| PROC_SPACE_TERM| PROC_OUT_LONG |
| }; |
| long[] longTmp = new long[1]; |
| readProcFile("/sys/kernel/mm/ksm/pages_shared", |
| SINGLE_LONG_FORMAT, null, longTmp, null); |
| longOut[KSM_SHARED] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; |
| longTmp[0] = 0; |
| readProcFile("/sys/kernel/mm/ksm/pages_sharing", |
| SINGLE_LONG_FORMAT, null, longTmp, null); |
| longOut[KSM_SHARING] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; |
| longTmp[0] = 0; |
| readProcFile("/sys/kernel/mm/ksm/pages_unshared", |
| SINGLE_LONG_FORMAT, null, longTmp, null); |
| longOut[KSM_UNSHARED] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; |
| longTmp[0] = 0; |
| readProcFile("/sys/kernel/mm/ksm/pages_volatile", |
| SINGLE_LONG_FORMAT, null, longTmp, null); |
| longOut[KSM_VOLATILE] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; |
| return longOut; |
| } |
| |
| private static String stringifySize(long size, int order) { |
| Locale locale = Locale.US; |
| switch (order) { |
| case 1: |
| return String.format(locale, "%,13d", size); |
| case 1024: |
| return String.format(locale, "%,9dK", size / 1024); |
| case 1024 * 1024: |
| return String.format(locale, "%,5dM", size / 1024 / 1024); |
| case 1024 * 1024 * 1024: |
| return String.format(locale, "%,1dG", size / 1024 / 1024 / 1024); |
| default: |
| throw new IllegalArgumentException("Invalid size order"); |
| } |
| } |
| |
| private static String stringifyKBSize(long size) { |
| return stringifySize(size * 1024, 1024); |
| } |
| |
| // Update this version number if you change the 'compact' format. |
| private static final int MEMINFO_COMPACT_VERSION = 1; |
| |
| private static class MemoryUsageDumpOptions { |
| boolean dumpDetails; |
| boolean dumpFullDetails; |
| boolean dumpDalvik; |
| boolean dumpSummaryOnly; |
| boolean dumpUnreachable; |
| boolean oomOnly; |
| boolean isCompact; |
| boolean localOnly; |
| boolean packages; |
| boolean isCheckinRequest; |
| boolean dumpSwapPss; |
| boolean dumpProto; |
| } |
| |
| final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, |
| String[] args, boolean brief, PrintWriter categoryPw, boolean asProto) { |
| MemoryUsageDumpOptions opts = new MemoryUsageDumpOptions(); |
| opts.dumpDetails = false; |
| opts.dumpFullDetails = false; |
| opts.dumpDalvik = false; |
| opts.dumpSummaryOnly = false; |
| opts.dumpUnreachable = false; |
| opts.oomOnly = false; |
| opts.isCompact = false; |
| opts.localOnly = false; |
| opts.packages = false; |
| opts.isCheckinRequest = false; |
| opts.dumpSwapPss = false; |
| opts.dumpProto = asProto; |
| |
| 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)) { |
| opts.dumpDetails = true; |
| opts.dumpFullDetails = true; |
| opts.dumpDalvik = true; |
| opts.dumpSwapPss = true; |
| } else if ("-d".equals(opt)) { |
| opts.dumpDalvik = true; |
| } else if ("-c".equals(opt)) { |
| opts.isCompact = true; |
| } else if ("-s".equals(opt)) { |
| opts.dumpDetails = true; |
| opts.dumpSummaryOnly = true; |
| } else if ("-S".equals(opt)) { |
| opts.dumpSwapPss = true; |
| } else if ("--unreachable".equals(opt)) { |
| opts.dumpUnreachable = true; |
| } else if ("--oom".equals(opt)) { |
| opts.oomOnly = true; |
| } else if ("--local".equals(opt)) { |
| opts.localOnly = true; |
| } else if ("--package".equals(opt)) { |
| opts.packages = true; |
| } else if ("--checkin".equals(opt)) { |
| opts.isCheckinRequest = true; |
| } else if ("--proto".equals(opt)) { |
| opts.dumpProto = true; |
| |
| } else if ("-h".equals(opt)) { |
| pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]"); |
| pw.println(" -a: include all available information for each process."); |
| pw.println(" -d: include dalvik details."); |
| pw.println(" -c: dump in a compact machine-parseable representation."); |
| pw.println(" -s: dump only summary of application memory usage."); |
| pw.println(" -S: dump also SwapPss."); |
| pw.println(" --oom: only show processes organized by oom adj."); |
| pw.println(" --local: only collect details locally, don't call process."); |
| pw.println(" --package: interpret process arg as package, dumping all"); |
| pw.println(" processes that have loaded that package."); |
| pw.println(" --checkin: dump data for a checkin"); |
| pw.println(" --proto: dump data to proto"); |
| pw.println("If [process] is specified it can be the name or "); |
| pw.println("pid of a specific process to dump."); |
| return; |
| } else { |
| pw.println("Unknown argument: " + opt + "; use -h for help"); |
| } |
| } |
| |
| String[] innerArgs = new String[args.length-opti]; |
| System.arraycopy(args, opti, innerArgs, 0, args.length-opti); |
| |
| ArrayList<ProcessRecord> procs = collectProcesses(pw, opti, opts.packages, args); |
| if (opts.dumpProto) { |
| dumpApplicationMemoryUsage(fd, opts, innerArgs, brief, procs); |
| } else { |
| dumpApplicationMemoryUsage(fd, pw, prefix, opts, innerArgs, brief, procs, categoryPw); |
| } |
| } |
| |
| private final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, |
| MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief, |
| ArrayList<ProcessRecord> procs, PrintWriter categoryPw) { |
| long uptime = SystemClock.uptimeMillis(); |
| long realtime = SystemClock.elapsedRealtime(); |
| final long[] tmpLong = new long[1]; |
| |
| if (procs == null) { |
| // No Java processes. Maybe they want to print a native process. |
| String proc = "N/A"; |
| if (innerArgs.length > 0) { |
| proc = innerArgs[0]; |
| if (proc.charAt(0) != '-') { |
| ArrayList<ProcessCpuTracker.Stats> nativeProcs |
| = new ArrayList<ProcessCpuTracker.Stats>(); |
| updateCpuStatsNow(); |
| int findPid = -1; |
| try { |
| findPid = Integer.parseInt(innerArgs[0]); |
| } catch (NumberFormatException e) { |
| } |
| synchronized (mProcessCpuTracker) { |
| final int N = mProcessCpuTracker.countStats(); |
| for (int i=0; i<N; i++) { |
| ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); |
| if (st.pid == findPid || (st.baseName != null |
| && st.baseName.equals(innerArgs[0]))) { |
| nativeProcs.add(st); |
| } |
| } |
| } |
| if (nativeProcs.size() > 0) { |
| dumpApplicationMemoryUsageHeader(pw, uptime, realtime, |
| opts.isCheckinRequest, opts.isCompact); |
| Debug.MemoryInfo mi = null; |
| for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) { |
| final ProcessCpuTracker.Stats r = nativeProcs.get(i); |
| final int pid = r.pid; |
| if (!opts.isCheckinRequest && opts.dumpDetails) { |
| pw.println("\n** MEMINFO in pid " + pid + " [" + r.baseName + "] **"); |
| } |
| if (mi == null) { |
| mi = new Debug.MemoryInfo(); |
| } |
| if (opts.dumpDetails || (!brief && !opts.oomOnly)) { |
| Debug.getMemoryInfo(pid, mi); |
| } else { |
| mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null); |
| mi.dalvikPrivateDirty = (int)tmpLong[0]; |
| } |
| ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, |
| opts.dumpFullDetails, opts.dumpDalvik, opts.dumpSummaryOnly, |
| pid, r.baseName, 0, 0, 0, 0, 0, 0); |
| if (opts.isCheckinRequest) { |
| pw.println(); |
| } |
| } |
| return; |
| } |
| } |
| } |
| pw.println("No process found for: " + proc); |
| return; |
| } |
| |
| if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) { |
| opts.dumpDetails = true; |
| } |
| |
| dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, opts.isCompact); |
| |
| ArrayList<MemItem> procMems = new ArrayList<MemItem>(); |
| final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>(); |
| long nativePss = 0; |
| long nativeSwapPss = 0; |
| long dalvikPss = 0; |
| long dalvikSwapPss = 0; |
| long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : |
| EmptyArray.LONG; |
| long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : |
| EmptyArray.LONG; |
| long otherPss = 0; |
| long otherSwapPss = 0; |
| long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; |
| long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; |
| |
| long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length]; |
| long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length]; |
| ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[]) |
| new ArrayList[DUMP_MEM_OOM_LABEL.length]; |
| |
| long totalPss = 0; |
| long totalSwapPss = 0; |
| long cachedPss = 0; |
| long cachedSwapPss = 0; |
| boolean hasSwapPss = false; |
| |
| Debug.MemoryInfo mi = null; |
| for (int i = procs.size() - 1 ; i >= 0 ; i--) { |
| final ProcessRecord r = procs.get(i); |
| final IApplicationThread thread; |
| final int pid; |
| final int oomAdj; |
| final boolean hasActivities; |
| synchronized (this) { |
| thread = r.thread; |
| pid = r.pid; |
| oomAdj = r.getSetAdjWithServices(); |
| hasActivities = r.activities.size() > 0; |
| } |
| if (thread != null) { |
| if (!opts.isCheckinRequest && opts.dumpDetails) { |
| pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **"); |
| } |
| if (mi == null) { |
| mi = new Debug.MemoryInfo(); |
| } |
| final int reportType; |
| final long startTime; |
| final long endTime; |
| if (opts.dumpDetails || (!brief && !opts.oomOnly)) { |
| reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW; |
| startTime = SystemClock.currentThreadTimeMillis(); |
| Debug.getMemoryInfo(pid, mi); |
| endTime = SystemClock.currentThreadTimeMillis(); |
| hasSwapPss = mi.hasSwappedOutPss; |
| } else { |
| reportType = ProcessStats.ADD_PSS_EXTERNAL; |
| startTime = SystemClock.currentThreadTimeMillis(); |
| mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null); |
| endTime = SystemClock.currentThreadTimeMillis(); |
| mi.dalvikPrivateDirty = (int)tmpLong[0]; |
| } |
| if (opts.dumpDetails) { |
| if (opts.localOnly) { |
| ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails, |
| opts.dumpDalvik, opts.dumpSummaryOnly, pid, r.processName, 0, 0, 0, 0, 0, 0); |
| if (opts.isCheckinRequest) { |
| pw.println(); |
| } |
| } else { |
| pw.flush(); |
| try { |
| TransferPipe tp = new TransferPipe(); |
| try { |
| thread.dumpMemInfo(tp.getWriteFd(), |
| mi, opts.isCheckinRequest, opts.dumpFullDetails, |
| opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable, innerArgs); |
| tp.go(fd, opts.dumpUnreachable ? 30000 : 5000); |
| } finally { |
| tp.kill(); |
| } |
| } catch (IOException e) { |
| if (!opts.isCheckinRequest) { |
| pw.println("Got IoException! " + e); |
| pw.flush(); |
| } |
| } catch (RemoteException e) { |
| if (!opts.isCheckinRequest) { |
| pw.println("Got RemoteException! " + e); |
| pw.flush(); |
| } |
| } |
| } |
| } |
| |
| final long myTotalPss = mi.getTotalPss(); |
| final long myTotalUss = mi.getTotalUss(); |
| final long myTotalRss = mi.getTotalRss(); |
| final long myTotalSwapPss = mi.getTotalSwappedOutPss(); |
| |
| synchronized (this) { |
| if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { |
| // Record this for posterity if the process has been stable. |
| r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true, |
| reportType, endTime-startTime, r.pkgList); |
| } |
| } |
| |
| if (!opts.isCheckinRequest && mi != null) { |
| totalPss += myTotalPss; |
| totalSwapPss += myTotalSwapPss; |
| MemItem pssItem = new MemItem(r.processName + " (pid " + pid + |
| (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss, |
| myTotalSwapPss, pid, hasActivities); |
| procMems.add(pssItem); |
| procMemsMap.put(pid, pssItem); |
| |
| nativePss += mi.nativePss; |
| nativeSwapPss += mi.nativeSwappedOutPss; |
| dalvikPss += mi.dalvikPss; |
| dalvikSwapPss += mi.dalvikSwappedOutPss; |
| for (int j=0; j<dalvikSubitemPss.length; j++) { |
| dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| dalvikSubitemSwapPss[j] += |
| mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| } |
| otherPss += mi.otherPss; |
| otherSwapPss += mi.otherSwappedOutPss; |
| for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { |
| long mem = mi.getOtherPss(j); |
| miscPss[j] += mem; |
| otherPss -= mem; |
| mem = mi.getOtherSwappedOutPss(j); |
| miscSwapPss[j] += mem; |
| otherSwapPss -= mem; |
| } |
| |
| if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) { |
| cachedPss += myTotalPss; |
| cachedSwapPss += myTotalSwapPss; |
| } |
| |
| for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) { |
| if (oomIndex == (oomPss.length - 1) |
| || (oomAdj >= DUMP_MEM_OOM_ADJ[oomIndex] |
| && oomAdj < DUMP_MEM_OOM_ADJ[oomIndex + 1])) { |
| oomPss[oomIndex] += myTotalPss; |
| oomSwapPss[oomIndex] += myTotalSwapPss; |
| if (oomProcs[oomIndex] == null) { |
| oomProcs[oomIndex] = new ArrayList<MemItem>(); |
| } |
| oomProcs[oomIndex].add(pssItem); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| long nativeProcTotalPss = 0; |
| |
| if (!opts.isCheckinRequest && procs.size() > 1 && !opts.packages) { |
| // If we are showing aggregations, also look for native processes to |
| // include so that our aggregations are more accurate. |
| updateCpuStatsNow(); |
| mi = null; |
| synchronized (mProcessCpuTracker) { |
| final int N = mProcessCpuTracker.countStats(); |
| for (int i=0; i<N; i++) { |
| ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); |
| if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) { |
| if (mi == null) { |
| mi = new Debug.MemoryInfo(); |
| } |
| if (!brief && !opts.oomOnly) { |
| Debug.getMemoryInfo(st.pid, mi); |
| } else { |
| mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null); |
| mi.nativePrivateDirty = (int)tmpLong[0]; |
| } |
| |
| final long myTotalPss = mi.getTotalPss(); |
| final long myTotalSwapPss = mi.getTotalSwappedOutPss(); |
| totalPss += myTotalPss; |
| totalSwapPss += myTotalSwapPss; |
| nativeProcTotalPss += myTotalPss; |
| |
| MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")", |
| st.name, myTotalPss, mi.getSummaryTotalSwapPss(), st.pid, false); |
| procMems.add(pssItem); |
| |
| nativePss += mi.nativePss; |
| nativeSwapPss += mi.nativeSwappedOutPss; |
| dalvikPss += mi.dalvikPss; |
| dalvikSwapPss += mi.dalvikSwappedOutPss; |
| for (int j=0; j<dalvikSubitemPss.length; j++) { |
| dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| dalvikSubitemSwapPss[j] += |
| mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| } |
| otherPss += mi.otherPss; |
| otherSwapPss += mi.otherSwappedOutPss; |
| for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { |
| long mem = mi.getOtherPss(j); |
| miscPss[j] += mem; |
| otherPss -= mem; |
| mem = mi.getOtherSwappedOutPss(j); |
| miscSwapPss[j] += mem; |
| otherSwapPss -= mem; |
| } |
| oomPss[0] += myTotalPss; |
| oomSwapPss[0] += myTotalSwapPss; |
| if (oomProcs[0] == null) { |
| oomProcs[0] = new ArrayList<MemItem>(); |
| } |
| oomProcs[0].add(pssItem); |
| } |
| } |
| } |
| |
| ArrayList<MemItem> catMems = new ArrayList<MemItem>(); |
| |
| catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1)); |
| final int dalvikId = -2; |
| catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikId)); |
| catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3)); |
| for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { |
| String label = Debug.MemoryInfo.getOtherLabel(j); |
| catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j)); |
| } |
| if (dalvikSubitemPss.length > 0) { |
| // Add dalvik subitems. |
| for (MemItem memItem : catMems) { |
| int memItemStart = 0, memItemEnd = 0; |
| if (memItem.id == dalvikId) { |
| memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_START; |
| memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_END; |
| } else if (memItem.id == Debug.MemoryInfo.OTHER_DALVIK_OTHER) { |
| memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_START; |
| memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_END; |
| } else if (memItem.id == Debug.MemoryInfo.OTHER_DEX) { |
| memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_START; |
| memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_END; |
| } else if (memItem.id == Debug.MemoryInfo.OTHER_ART) { |
| memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_ART_START; |
| memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_ART_END; |
| } else { |
| continue; // No subitems, continue. |
| } |
| memItem.subitems = new ArrayList<MemItem>(); |
| for (int j=memItemStart; j<=memItemEnd; j++) { |
| final String name = Debug.MemoryInfo.getOtherLabel( |
| Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], |
| dalvikSubitemSwapPss[j], j)); |
| } |
| } |
| } |
| |
| ArrayList<MemItem> oomMems = new ArrayList<MemItem>(); |
| for (int j=0; j<oomPss.length; j++) { |
| if (oomPss[j] != 0) { |
| String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j] |
| : DUMP_MEM_OOM_LABEL[j]; |
| MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], |
| DUMP_MEM_OOM_ADJ[j]); |
| item.subitems = oomProcs[j]; |
| oomMems.add(item); |
| } |
| } |
| |
| opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0; |
| if (!brief && !opts.oomOnly && !opts.isCompact) { |
| pw.println(); |
| pw.println("Total PSS by process:"); |
| dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, opts.dumpSwapPss); |
| pw.println(); |
| } |
| if (!opts.isCompact) { |
| pw.println("Total PSS by OOM adjustment:"); |
| } |
| dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, opts.dumpSwapPss); |
| if (!brief && !opts.oomOnly) { |
| PrintWriter out = categoryPw != null ? categoryPw : pw; |
| if (!opts.isCompact) { |
| out.println(); |
| out.println("Total PSS by category:"); |
| } |
| dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, opts.dumpSwapPss); |
| } |
| if (!opts.isCompact) { |
| pw.println(); |
| } |
| MemInfoReader memInfo = new MemInfoReader(); |
| memInfo.readMemInfo(); |
| if (nativeProcTotalPss > 0) { |
| synchronized (this) { |
| 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, nativeProcTotalPss*1024); |
| mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb, |
| nativeProcTotalPss); |
| } |
| } |
| if (!brief) { |
| if (!opts.isCompact) { |
| pw.print("Total RAM: "); pw.print(stringifyKBSize(memInfo.getTotalSizeKb())); |
| pw.print(" (status "); |
| switch (mLastMemoryLevel) { |
| case ProcessStats.ADJ_MEM_FACTOR_NORMAL: |
| pw.println("normal)"); |
| break; |
| case ProcessStats.ADJ_MEM_FACTOR_MODERATE: |
| pw.println("moderate)"); |
| break; |
| case ProcessStats.ADJ_MEM_FACTOR_LOW: |
| pw.println("low)"); |
| break; |
| case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: |
| pw.println("critical)"); |
| break; |
| default: |
| pw.print(mLastMemoryLevel); |
| pw.println(")"); |
| break; |
| } |
| pw.print(" Free RAM: "); |
| pw.print(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb() |
| + memInfo.getFreeSizeKb())); |
| pw.print(" ("); |
| pw.print(stringifyKBSize(cachedPss)); |
| pw.print(" cached pss + "); |
| pw.print(stringifyKBSize(memInfo.getCachedSizeKb())); |
| pw.print(" cached kernel + "); |
| pw.print(stringifyKBSize(memInfo.getFreeSizeKb())); |
| pw.println(" free)"); |
| } else { |
| pw.print("ram,"); pw.print(memInfo.getTotalSizeKb()); pw.print(","); |
| pw.print(cachedPss + memInfo.getCachedSizeKb() |
| + memInfo.getFreeSizeKb()); pw.print(","); |
| pw.println(totalPss - cachedPss); |
| } |
| } |
| long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss) |
| - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() |
| - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb(); |
| if (!opts.isCompact) { |
| pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss |
| + memInfo.getKernelUsedSizeKb())); pw.print(" ("); |
| pw.print(stringifyKBSize(totalPss - cachedPss)); pw.print(" used pss + "); |
| pw.print(stringifyKBSize(memInfo.getKernelUsedSizeKb())); pw.print(" kernel)\n"); |
| pw.print(" Lost RAM: "); pw.println(stringifyKBSize(lostRAM)); |
| } else { |
| pw.print("lostram,"); pw.println(lostRAM); |
| } |
| if (!brief) { |
| if (memInfo.getZramTotalSizeKb() != 0) { |
| if (!opts.isCompact) { |
| pw.print(" ZRAM: "); |
| pw.print(stringifyKBSize(memInfo.getZramTotalSizeKb())); |
| pw.print(" physical used for "); |
| pw.print(stringifyKBSize(memInfo.getSwapTotalSizeKb() |
| - memInfo.getSwapFreeSizeKb())); |
| pw.print(" in swap ("); |
| pw.print(stringifyKBSize(memInfo.getSwapTotalSizeKb())); |
| pw.println(" total swap)"); |
| } else { |
| pw.print("zram,"); pw.print(memInfo.getZramTotalSizeKb()); pw.print(","); |
| pw.print(memInfo.getSwapTotalSizeKb()); pw.print(","); |
| pw.println(memInfo.getSwapFreeSizeKb()); |
| } |
| } |
| final long[] ksm = getKsmInfo(); |
| if (!opts.isCompact) { |
| if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0 |
| || ksm[KSM_VOLATILE] != 0) { |
| pw.print(" KSM: "); pw.print(stringifyKBSize(ksm[KSM_SHARING])); |
| pw.print(" saved from shared "); |
| pw.print(stringifyKBSize(ksm[KSM_SHARED])); |
| pw.print(" "); pw.print(stringifyKBSize(ksm[KSM_UNSHARED])); |
| pw.print(" unshared; "); |
| pw.print(stringifyKBSize( |
| ksm[KSM_VOLATILE])); pw.println(" volatile"); |
| } |
| pw.print(" Tuning: "); |
| pw.print(ActivityManager.staticGetMemoryClass()); |
| pw.print(" (large "); |
| pw.print(ActivityManager.staticGetLargeMemoryClass()); |
| pw.print("), oom "); |
| pw.print(stringifySize( |
| mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ), 1024)); |
| pw.print(", restore limit "); |
| pw.print(stringifyKBSize(mProcessList.getCachedRestoreThresholdKb())); |
| if (ActivityManager.isLowRamDeviceStatic()) { |
| pw.print(" (low-ram)"); |
| } |
| if (ActivityManager.isHighEndGfx()) { |
| pw.print(" (high-end-gfx)"); |
| } |
| pw.println(); |
| } else { |
| pw.print("ksm,"); pw.print(ksm[KSM_SHARING]); pw.print(","); |
| pw.print(ksm[KSM_SHARED]); pw.print(","); pw.print(ksm[KSM_UNSHARED]); |
| pw.print(","); pw.println(ksm[KSM_VOLATILE]); |
| pw.print("tuning,"); |
| pw.print(ActivityManager.staticGetMemoryClass()); |
| pw.print(','); |
| pw.print(ActivityManager.staticGetLargeMemoryClass()); |
| pw.print(','); |
| pw.print(mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024); |
| if (ActivityManager.isLowRamDeviceStatic()) { |
| pw.print(",low-ram"); |
| } |
| if (ActivityManager.isHighEndGfx()) { |
| pw.print(",high-end-gfx"); |
| } |
| pw.println(); |
| } |
| } |
| } |
| } |
| |
| private final void dumpApplicationMemoryUsage(FileDescriptor fd, |
| MemoryUsageDumpOptions opts, String[] innerArgs, boolean brief, |
| ArrayList<ProcessRecord> procs) { |
| final long uptimeMs = SystemClock.uptimeMillis(); |
| final long realtimeMs = SystemClock.elapsedRealtime(); |
| final long[] tmpLong = new long[1]; |
| |
| if (procs == null) { |
| // No Java processes. Maybe they want to print a native process. |
| String proc = "N/A"; |
| if (innerArgs.length > 0) { |
| proc = innerArgs[0]; |
| if (proc.charAt(0) != '-') { |
| ArrayList<ProcessCpuTracker.Stats> nativeProcs |
| = new ArrayList<ProcessCpuTracker.Stats>(); |
| updateCpuStatsNow(); |
| int findPid = -1; |
| try { |
| findPid = Integer.parseInt(innerArgs[0]); |
| } catch (NumberFormatException e) { |
| } |
| synchronized (mProcessCpuTracker) { |
| final int N = mProcessCpuTracker.countStats(); |
| for (int i=0; i<N; i++) { |
| ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); |
| if (st.pid == findPid || (st.baseName != null |
| && st.baseName.equals(innerArgs[0]))) { |
| nativeProcs.add(st); |
| } |
| } |
| } |
| if (nativeProcs.size() > 0) { |
| ProtoOutputStream proto = new ProtoOutputStream(fd); |
| |
| proto.write(MemInfoDumpProto.UPTIME_DURATION_MS, uptimeMs); |
| proto.write(MemInfoDumpProto.ELAPSED_REALTIME_MS, realtimeMs); |
| Debug.MemoryInfo mi = null; |
| for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) { |
| final ProcessCpuTracker.Stats r = nativeProcs.get(i); |
| final int pid = r.pid; |
| final long nToken = proto.start(MemInfoDumpProto.NATIVE_PROCESSES); |
| |
| proto.write(MemInfoDumpProto.ProcessMemory.PID, pid); |
| proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.baseName); |
| |
| if (mi == null) { |
| mi = new Debug.MemoryInfo(); |
| } |
| if (opts.dumpDetails || (!brief && !opts.oomOnly)) { |
| Debug.getMemoryInfo(pid, mi); |
| } else { |
| mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null); |
| mi.dalvikPrivateDirty = (int)tmpLong[0]; |
| } |
| ActivityThread.dumpMemInfoTable(proto, mi, opts.dumpDalvik, |
| opts.dumpSummaryOnly, 0, 0, 0, 0, 0, 0); |
| |
| proto.end(nToken); |
| } |
| |
| proto.flush(); |
| return; |
| } |
| } |
| } |
| Log.d(TAG, "No process found for: " + innerArgs[0]); |
| return; |
| } |
| |
| if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) { |
| opts.dumpDetails = true; |
| } |
| |
| ProtoOutputStream proto = new ProtoOutputStream(fd); |
| |
| proto.write(MemInfoDumpProto.UPTIME_DURATION_MS, uptimeMs); |
| proto.write(MemInfoDumpProto.ELAPSED_REALTIME_MS, realtimeMs); |
| |
| ArrayList<MemItem> procMems = new ArrayList<MemItem>(); |
| final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>(); |
| long nativePss = 0; |
| long nativeSwapPss = 0; |
| long dalvikPss = 0; |
| long dalvikSwapPss = 0; |
| long[] dalvikSubitemPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : |
| EmptyArray.LONG; |
| long[] dalvikSubitemSwapPss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : |
| EmptyArray.LONG; |
| long otherPss = 0; |
| long otherSwapPss = 0; |
| long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; |
| long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; |
| |
| long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length]; |
| long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length]; |
| ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[]) |
| new ArrayList[DUMP_MEM_OOM_LABEL.length]; |
| |
| long totalPss = 0; |
| long totalSwapPss = 0; |
| long cachedPss = 0; |
| long cachedSwapPss = 0; |
| boolean hasSwapPss = false; |
| |
| Debug.MemoryInfo mi = null; |
| for (int i = procs.size() - 1 ; i >= 0 ; i--) { |
| final ProcessRecord r = procs.get(i); |
| final IApplicationThread thread; |
| final int pid; |
| final int oomAdj; |
| final boolean hasActivities; |
| synchronized (this) { |
| thread = r.thread; |
| pid = r.pid; |
| oomAdj = r.getSetAdjWithServices(); |
| hasActivities = r.activities.size() > 0; |
| } |
| if (thread == null) { |
| continue; |
| } |
| if (mi == null) { |
| mi = new Debug.MemoryInfo(); |
| } |
| final int reportType; |
| final long startTime; |
| final long endTime; |
| if (opts.dumpDetails || (!brief && !opts.oomOnly)) { |
| reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW; |
| startTime = SystemClock.currentThreadTimeMillis(); |
| Debug.getMemoryInfo(pid, mi); |
| endTime = SystemClock.currentThreadTimeMillis(); |
| hasSwapPss = mi.hasSwappedOutPss; |
| } else { |
| reportType = ProcessStats.ADD_PSS_EXTERNAL; |
| startTime = SystemClock.currentThreadTimeMillis(); |
| mi.dalvikPss = (int) Debug.getPss(pid, tmpLong, null); |
| endTime = SystemClock.currentThreadTimeMillis(); |
| mi.dalvikPrivateDirty = (int) tmpLong[0]; |
| } |
| if (opts.dumpDetails) { |
| if (opts.localOnly) { |
| final long aToken = proto.start(MemInfoDumpProto.APP_PROCESSES); |
| final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY); |
| proto.write(MemInfoDumpProto.ProcessMemory.PID, pid); |
| proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.processName); |
| ActivityThread.dumpMemInfoTable(proto, mi, opts.dumpDalvik, |
| opts.dumpSummaryOnly, 0, 0, 0, 0, 0, 0); |
| proto.end(mToken); |
| proto.end(aToken); |
| } else { |
| try { |
| ByteTransferPipe tp = new ByteTransferPipe(); |
| try { |
| thread.dumpMemInfoProto(tp.getWriteFd(), |
| mi, opts.dumpFullDetails, opts.dumpDalvik, opts.dumpSummaryOnly, |
| opts.dumpUnreachable, innerArgs); |
| proto.write(MemInfoDumpProto.APP_PROCESSES, tp.get()); |
| } finally { |
| tp.kill(); |
| } |
| } catch (IOException e) { |
| Log.e(TAG, "Got IOException!", e); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Got RemoteException!", e); |
| } |
| } |
| } |
| |
| final long myTotalPss = mi.getTotalPss(); |
| final long myTotalUss = mi.getTotalUss(); |
| final long myTotalRss = mi.getTotalRss(); |
| final long myTotalSwapPss = mi.getTotalSwappedOutPss(); |
| |
| synchronized (this) { |
| if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { |
| // Record this for posterity if the process has been stable. |
| r.baseProcessTracker.addPss(myTotalPss, myTotalUss, myTotalRss, true, |
| reportType, endTime-startTime, r.pkgList); |
| } |
| } |
| |
| if (!opts.isCheckinRequest && mi != null) { |
| totalPss += myTotalPss; |
| totalSwapPss += myTotalSwapPss; |
| MemItem pssItem = new MemItem(r.processName + " (pid " + pid + |
| (hasActivities ? " / activities)" : ")"), r.processName, myTotalPss, |
| myTotalSwapPss, pid, hasActivities); |
| procMems.add(pssItem); |
| procMemsMap.put(pid, pssItem); |
| |
| nativePss += mi.nativePss; |
| nativeSwapPss += mi.nativeSwappedOutPss; |
| dalvikPss += mi.dalvikPss; |
| dalvikSwapPss += mi.dalvikSwappedOutPss; |
| for (int j=0; j<dalvikSubitemPss.length; j++) { |
| dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| dalvikSubitemSwapPss[j] += |
| mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| } |
| otherPss += mi.otherPss; |
| otherSwapPss += mi.otherSwappedOutPss; |
| for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { |
| long mem = mi.getOtherPss(j); |
| miscPss[j] += mem; |
| otherPss -= mem; |
| mem = mi.getOtherSwappedOutPss(j); |
| miscSwapPss[j] += mem; |
| otherSwapPss -= mem; |
| } |
| |
| if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) { |
| cachedPss += myTotalPss; |
| cachedSwapPss += myTotalSwapPss; |
| } |
| |
| for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) { |
| if (oomIndex == (oomPss.length - 1) |
| || (oomAdj >= DUMP_MEM_OOM_ADJ[oomIndex] |
| && oomAdj < DUMP_MEM_OOM_ADJ[oomIndex + 1])) { |
| oomPss[oomIndex] += myTotalPss; |
| oomSwapPss[oomIndex] += myTotalSwapPss; |
| if (oomProcs[oomIndex] == null) { |
| oomProcs[oomIndex] = new ArrayList<MemItem>(); |
| } |
| oomProcs[oomIndex].add(pssItem); |
| break; |
| } |
| } |
| } |
| } |
| |
| long nativeProcTotalPss = 0; |
| |
| if (procs.size() > 1 && !opts.packages) { |
| // If we are showing aggregations, also look for native processes to |
| // include so that our aggregations are more accurate. |
| updateCpuStatsNow(); |
| mi = null; |
| synchronized (mProcessCpuTracker) { |
| final int N = mProcessCpuTracker.countStats(); |
| for (int i=0; i<N; i++) { |
| ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); |
| if (st.vsize > 0 && procMemsMap.indexOfKey(st.pid) < 0) { |
| if (mi == null) { |
| mi = new Debug.MemoryInfo(); |
| } |
| if (!brief && !opts.oomOnly) { |
| Debug.getMemoryInfo(st.pid, mi); |
| } else { |
| mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null); |
| mi.nativePrivateDirty = (int)tmpLong[0]; |
| } |
| |
| final long myTotalPss = mi.getTotalPss(); |
| final long myTotalSwapPss = mi.getTotalSwappedOutPss(); |
| totalPss += myTotalPss; |
| nativeProcTotalPss += myTotalPss; |
| |
| MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")", |
| st.name, myTotalPss, mi.getSummaryTotalSwapPss(), st.pid, false); |
| procMems.add(pssItem); |
| |
| nativePss += mi.nativePss; |
| nativeSwapPss += mi.nativeSwappedOutPss; |
| dalvikPss += mi.dalvikPss; |
| dalvikSwapPss += mi.dalvikSwappedOutPss; |
| for (int j=0; j<dalvikSubitemPss.length; j++) { |
| dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| dalvikSubitemSwapPss[j] += |
| mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| } |
| otherPss += mi.otherPss; |
| otherSwapPss += mi.otherSwappedOutPss; |
| for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { |
| long mem = mi.getOtherPss(j); |
| miscPss[j] += mem; |
| otherPss -= mem; |
| mem = mi.getOtherSwappedOutPss(j); |
| miscSwapPss[j] += mem; |
| otherSwapPss -= mem; |
| } |
| oomPss[0] += myTotalPss; |
| oomSwapPss[0] += myTotalSwapPss; |
| if (oomProcs[0] == null) { |
| oomProcs[0] = new ArrayList<MemItem>(); |
| } |
| oomProcs[0].add(pssItem); |
| } |
| } |
| } |
| |
| ArrayList<MemItem> catMems = new ArrayList<MemItem>(); |
| |
| catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1)); |
| final int dalvikId = -2; |
| catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikId)); |
| catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3)); |
| for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { |
| String label = Debug.MemoryInfo.getOtherLabel(j); |
| catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j)); |
| } |
| if (dalvikSubitemPss.length > 0) { |
| // Add dalvik subitems. |
| for (MemItem memItem : catMems) { |
| int memItemStart = 0, memItemEnd = 0; |
| if (memItem.id == dalvikId) { |
| memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_START; |
| memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_END; |
| } else if (memItem.id == Debug.MemoryInfo.OTHER_DALVIK_OTHER) { |
| memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_START; |
| memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_END; |
| } else if (memItem.id == Debug.MemoryInfo.OTHER_DEX) { |
| memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_START; |
| memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_END; |
| } else if (memItem.id == Debug.MemoryInfo.OTHER_ART) { |
| memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_ART_START; |
| memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_ART_END; |
| } else { |
| continue; // No subitems, continue. |
| } |
| memItem.subitems = new ArrayList<MemItem>(); |
| for (int j=memItemStart; j<=memItemEnd; j++) { |
| final String name = Debug.MemoryInfo.getOtherLabel( |
| Debug.MemoryInfo.NUM_OTHER_STATS + j); |
| memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], |
| dalvikSubitemSwapPss[j], j)); |
| } |
| } |
| } |
| |
| ArrayList<MemItem> oomMems = new ArrayList<MemItem>(); |
| for (int j=0; j<oomPss.length; j++) { |
| if (oomPss[j] != 0) { |
| String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j] |
| : DUMP_MEM_OOM_LABEL[j]; |
| MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], |
| DUMP_MEM_OOM_ADJ[j]); |
| item.subitems = oomProcs[j]; |
| oomMems.add(item); |
| } |
| } |
| |
| opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && totalSwapPss != 0; |
| if (!opts.oomOnly) { |
| dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_PROCESS, "proc", |
| procMems, true, opts.dumpSwapPss); |
| } |
| dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_OOM_ADJUSTMENT, "oom", |
| oomMems, false, opts.dumpSwapPss); |
| if (!brief && !opts.oomOnly) { |
| dumpMemItems(proto, MemInfoDumpProto.TOTAL_PSS_BY_CATEGORY, "cat", |
| catMems, true, opts.dumpSwapPss); |
| } |
| MemInfoReader memInfo = new MemInfoReader(); |
| memInfo.readMemInfo(); |
| if (nativeProcTotalPss > 0) { |
| synchronized (this) { |
| 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, nativeProcTotalPss*1024); |
| mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb, |
| nativeProcTotalPss); |
| } |
| } |
| if (!brief) { |
| proto.write(MemInfoDumpProto.TOTAL_RAM_KB, memInfo.getTotalSizeKb()); |
| proto.write(MemInfoDumpProto.STATUS, mLastMemoryLevel); |
| proto.write(MemInfoDumpProto.CACHED_PSS_KB, cachedPss); |
| proto.write(MemInfoDumpProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb()); |
| proto.write(MemInfoDumpProto.FREE_KB, memInfo.getFreeSizeKb()); |
| } |
| long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss) |
| - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() |
| - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb(); |
| proto.write(MemInfoDumpProto.USED_PSS_KB, totalPss - cachedPss); |
| proto.write(MemInfoDumpProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb()); |
| proto.write(MemInfoDumpProto.LOST_RAM_KB, lostRAM); |
| if (!brief) { |
| if (memInfo.getZramTotalSizeKb() != 0) { |
| proto.write(MemInfoDumpProto.TOTAL_ZRAM_KB, memInfo.getZramTotalSizeKb()); |
| proto.write(MemInfoDumpProto.ZRAM_PHYSICAL_USED_IN_SWAP_KB, |
| memInfo.getSwapTotalSizeKb() - memInfo.getSwapFreeSizeKb()); |
| proto.write(MemInfoDumpProto.TOTAL_ZRAM_SWAP_KB, memInfo.getSwapTotalSizeKb()); |
| } |
| final long[] ksm = getKsmInfo(); |
| proto.write(MemInfoDumpProto.KSM_SHARING_KB, ksm[KSM_SHARING]); |
| proto.write(MemInfoDumpProto.KSM_SHARED_KB, ksm[KSM_SHARED]); |
| proto.write(MemInfoDumpProto.KSM_UNSHARED_KB, ksm[KSM_UNSHARED]); |
| proto.write(MemInfoDumpProto.KSM_VOLATILE_KB, ksm[KSM_VOLATILE]); |
| |
| proto.write(MemInfoDumpProto.TUNING_MB, ActivityManager.staticGetMemoryClass()); |
| proto.write(MemInfoDumpProto.TUNING_LARGE_MB, ActivityManager.staticGetLargeMemoryClass()); |
| proto.write(MemInfoDumpProto.OOM_KB, |
| mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ) / 1024); |
| proto.write(MemInfoDumpProto.RESTORE_LIMIT_KB, |
| mProcessList.getCachedRestoreThresholdKb()); |
| |
| proto.write(MemInfoDumpProto.IS_LOW_RAM_DEVICE, ActivityManager.isLowRamDeviceStatic()); |
| proto.write(MemInfoDumpProto.IS_HIGH_END_GFX, ActivityManager.isHighEndGfx()); |
| } |
| } |
| |
| proto.flush(); |
| } |
| |
| private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss, |
| long memtrack, String name) { |
| sb.append(" "); |
| sb.append(ProcessList.makeOomAdjString(oomAdj)); |
| sb.append(' '); |
| sb.append(ProcessList.makeProcStateString(procState)); |
| sb.append(' '); |
| ProcessList.appendRamKb(sb, pss); |
| sb.append(": "); |
| sb.append(name); |
| if (memtrack > 0) { |
| sb.append(" ("); |
| sb.append(stringifyKBSize(memtrack)); |
| sb.append(" memtrack)"); |
| } |
| } |
| |
| private void appendMemInfo(StringBuilder sb, ProcessMemInfo mi) { |
| appendBasicMemEntry(sb, mi.oomAdj, mi.procState, mi.pss, mi.memtrack, mi.name); |
| sb.append(" (pid "); |
| sb.append(mi.pid); |
| sb.append(") "); |
| sb.append(mi.adjType); |
| sb.append('\n'); |
| if (mi.adjReason != null) { |
| sb.append(" "); |
| sb.append(mi.adjReason); |
| sb.append('\n'); |
| } |
| } |
| |
| void reportMemUsage(ArrayList<ProcessMemInfo> memInfos) { |
| final SparseArray<ProcessMemInfo> infoMap = new SparseArray<>(memInfos.size()); |
| for (int i=0, N=memInfos.size(); i<N; i++) { |
| ProcessMemInfo mi = memInfos.get(i); |
| infoMap.put(mi.pid, mi); |
| } |
| updateCpuStatsNow(); |
| long[] memtrackTmp = new long[1]; |
| final List<ProcessCpuTracker.Stats> stats; |
| // Get a list of Stats that have vsize > 0 |
| synchronized (mProcessCpuTracker) { |
| stats = mProcessCpuTracker.getStats((st) -> { |
| return st.vsize > 0; |
| }); |
| } |
| final int statsCount = stats.size(); |
| for (int i = 0; i < statsCount; i++) { |
| ProcessCpuTracker.Stats st = stats.get(i); |
| long pss = Debug.getPss(st.pid, null, memtrackTmp); |
| if (pss > 0) { |
| if (infoMap.indexOfKey(st.pid) < 0) { |
| ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid, |
| ProcessList.NATIVE_ADJ, -1, "native", null); |
| mi.pss = pss; |
| mi.memtrack = memtrackTmp[0]; |
| memInfos.add(mi); |
| } |
| } |
| } |
| |
| long totalPss = 0; |
| long totalMemtrack = 0; |
| for (int i=0, N=memInfos.size(); i<N; i++) { |
| ProcessMemInfo mi = memInfos.get(i); |
| if (mi.pss == 0) { |
| mi.pss = Debug.getPss(mi.pid, null, memtrackTmp); |
| mi.memtrack = memtrackTmp[0]; |
| } |
| totalPss += mi.pss; |
| totalMemtrack += mi.memtrack; |
| } |
| Collections.sort(memInfos, new Comparator<ProcessMemInfo>() { |
| @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) { |
| if (lhs.oomAdj != rhs.oomAdj) { |
| return lhs.oomAdj < rhs.oomAdj ? -1 : 1; |
| } |
| if (lhs.pss != rhs.pss) { |
| return lhs.pss < rhs.pss ? 1 : -1; |
| } |
| return 0; |
| } |
| }); |
| |
| StringBuilder tag = new StringBuilder(128); |
| StringBuilder stack = new StringBuilder(128); |
| tag.append("Low on memory -- "); |
| appendMemBucket(tag, totalPss, "total", false); |
| appendMemBucket(stack, totalPss, "total", true); |
| |
| StringBuilder fullNativeBuilder = new StringBuilder(1024); |
| StringBuilder shortNativeBuilder = new StringBuilder(1024); |
| StringBuilder fullJavaBuilder = new StringBuilder(1024); |
| |
| boolean firstLine = true; |
| int lastOomAdj = Integer.MIN_VALUE; |
| long extraNativeRam = 0; |
| long extraNativeMemtrack = 0; |
| long cachedPss = 0; |
| for (int i=0, N=memInfos.size(); i<N; i++) { |
| ProcessMemInfo mi = memInfos.get(i); |
| |
| if (mi.oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) { |
| cachedPss += mi.pss; |
| } |
| |
| if (mi.oomAdj != ProcessList.NATIVE_ADJ |
| && (mi.oomAdj < ProcessList.SERVICE_ADJ |
| || mi.oomAdj == ProcessList.HOME_APP_ADJ |
| || mi.oomAdj == ProcessList.PREVIOUS_APP_ADJ)) { |
| if (lastOomAdj != mi.oomAdj) { |
| lastOomAdj = mi.oomAdj; |
| if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { |
| tag.append(" / "); |
| } |
| if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ) { |
| if (firstLine) { |
| stack.append(":"); |
| firstLine = false; |
| } |
| stack.append("\n\t at "); |
| } else { |
| stack.append("$"); |
| } |
| } else { |
| tag.append(" "); |
| stack.append("$"); |
| } |
| if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { |
| appendMemBucket(tag, mi.pss, mi.name, false); |
| } |
| appendMemBucket(stack, mi.pss, mi.name, true); |
| if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ |
| && ((i+1) >= N || memInfos.get(i+1).oomAdj != lastOomAdj)) { |
| stack.append("("); |
| for (int k=0; k<DUMP_MEM_OOM_ADJ.length; k++) { |
| if (DUMP_MEM_OOM_ADJ[k] == mi.oomAdj) { |
| stack.append(DUMP_MEM_OOM_LABEL[k]); |
| stack.append(":"); |
| stack.append(DUMP_MEM_OOM_ADJ[k]); |
| } |
| } |
| stack.append(")"); |
| } |
| } |
| |
| appendMemInfo(fullNativeBuilder, mi); |
| if (mi.oomAdj == ProcessList.NATIVE_ADJ) { |
| // The short form only has native processes that are >= 512K. |
| if (mi.pss >= 512) { |
| appendMemInfo(shortNativeBuilder, mi); |
| } else { |
| extraNativeRam += mi.pss; |
| extraNativeMemtrack += mi.memtrack; |
| } |
| } else { |
| // Short form has all other details, but if we have collected RAM |
| // from smaller native processes let's dump a summary of that. |
| if (extraNativeRam > 0) { |
| appendBasicMemEntry(shortNativeBuilder, ProcessList.NATIVE_ADJ, |
| -1, extraNativeRam, extraNativeMemtrack, "(Other native)"); |
| shortNativeBuilder.append('\n'); |
| extraNativeRam = 0; |
| } |
| appendMemInfo(fullJavaBuilder, mi); |
| } |
| } |
| |
| fullJavaBuilder.append(" "); |
| ProcessList.appendRamKb(fullJavaBuilder, totalPss); |
| fullJavaBuilder.append(": TOTAL"); |
| if (totalMemtrack > 0) { |
| fullJavaBuilder.append(" ("); |
| fullJavaBuilder.append(stringifyKBSize(totalMemtrack)); |
| fullJavaBuilder.append(" memtrack)"); |
| } else { |
| } |
| fullJavaBuilder.append("\n"); |
| |
| MemInfoReader memInfo = new MemInfoReader(); |
| memInfo.readMemInfo(); |
| final long[] infos = memInfo.getRawInfo(); |
| |
| StringBuilder memInfoBuilder = new StringBuilder(1024); |
| Debug.getMemInfo(infos); |
| memInfoBuilder.append(" MemInfo: "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SLAB])).append(" slab, "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SHMEM])).append(" shmem, "); |
| memInfoBuilder.append(stringifyKBSize( |
| infos[Debug.MEMINFO_VM_ALLOC_USED])).append(" vm alloc, "); |
| memInfoBuilder.append(stringifyKBSize( |
| infos[Debug.MEMINFO_PAGE_TABLES])).append(" page tables "); |
| memInfoBuilder.append(stringifyKBSize( |
| infos[Debug.MEMINFO_KERNEL_STACK])).append(" kernel stack\n"); |
| memInfoBuilder.append(" "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_BUFFERS])).append(" buffers, "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_CACHED])).append(" cached, "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_MAPPED])).append(" mapped, "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_FREE])).append(" free\n"); |
| if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) { |
| memInfoBuilder.append(" ZRAM: "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_ZRAM_TOTAL])); |
| memInfoBuilder.append(" RAM, "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_TOTAL])); |
| memInfoBuilder.append(" swap total, "); |
| memInfoBuilder.append(stringifyKBSize(infos[Debug.MEMINFO_SWAP_FREE])); |
| memInfoBuilder.append(" swap free\n"); |
| } |
| final long[] ksm = getKsmInfo(); |
| if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0 |
| || ksm[KSM_VOLATILE] != 0) { |
| memInfoBuilder.append(" KSM: "); |
| memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARING])); |
| memInfoBuilder.append(" saved from shared "); |
| memInfoBuilder.append(stringifyKBSize(ksm[KSM_SHARED])); |
| memInfoBuilder.append("\n "); |
| memInfoBuilder.append(stringifyKBSize(ksm[KSM_UNSHARED])); |
| memInfoBuilder.append(" unshared; "); |
| memInfoBuilder.append(stringifyKBSize(ksm[KSM_VOLATILE])); |
| memInfoBuilder.append(" volatile\n"); |
| } |
| memInfoBuilder.append(" Free RAM: "); |
| memInfoBuilder.append(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb() |
| + memInfo.getFreeSizeKb())); |
| memInfoBuilder.append("\n"); |
| memInfoBuilder.append(" Used RAM: "); |
| memInfoBuilder.append(stringifyKBSize( |
| totalPss - cachedPss + memInfo.getKernelUsedSizeKb())); |
| memInfoBuilder.append("\n"); |
| memInfoBuilder.append(" Lost RAM: "); |
| memInfoBuilder.append(stringifyKBSize(memInfo.getTotalSizeKb() |
| - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() |
| - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb())); |
| memInfoBuilder.append("\n"); |
| Slog.i(TAG, "Low on memory:"); |
| Slog.i(TAG, shortNativeBuilder.toString()); |
| Slog.i(TAG, fullJavaBuilder.toString()); |
| Slog.i(TAG, memInfoBuilder.toString()); |
| |
| StringBuilder dropBuilder = new StringBuilder(1024); |
| /* |
| StringWriter oomSw = new StringWriter(); |
| PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256); |
| StringWriter catSw = new StringWriter(); |
| PrintWriter catPw = new FastPrintWriter(catSw, false, 256); |
| String[] emptyArgs = new String[] { }; |
| dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw); |
| oomPw.flush(); |
| String oomString = oomSw.toString(); |
| */ |
| dropBuilder.append("Low on memory:"); |
| dropBuilder.append(stack); |
| dropBuilder.append('\n'); |
| dropBuilder.append(fullNativeBuilder); |
| dropBuilder.append(fullJavaBuilder); |
| dropBuilder.append('\n'); |
| dropBuilder.append(memInfoBuilder); |
| dropBuilder.append('\n'); |
| /* |
| dropBuilder.append(oomString); |
| dropBuilder.append('\n'); |
| */ |
| StringWriter catSw = new StringWriter(); |
| synchronized (ActivityManagerService.this) { |
| PrintWriter catPw = new FastPrintWriter(catSw, false, 256); |
| String[] emptyArgs = new String[] { }; |
| catPw.println(); |
| dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null, -1); |
| catPw.println(); |
| mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0, |
| false, null).dumpLocked(); |
| catPw.println(); |
| dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); |
| catPw.flush(); |
| } |
| dropBuilder.append(catSw.toString()); |
| StatsLog.write(StatsLog.LOW_MEM_REPORTED); |
| addErrorToDropBox("lowmem", null, "system_server", null, |
| null, tag.toString(), dropBuilder.toString(), null, null); |
| //Slog.i(TAG, "Sent to dropbox:"); |
| //Slog.i(TAG, dropBuilder.toString()); |
| synchronized (ActivityManagerService.this) { |
| long now = SystemClock.uptimeMillis(); |
| if (mLastMemUsageReportTime < now) { |
| mLastMemUsageReportTime = now; |
| } |
| } |
| } |
| |
| /** |
| * Searches array of arguments for the specified string |
| * @param args array of argument strings |
| * @param value value to search for |
| * @return true if the value is contained in the array |
| */ |
| private static boolean scanArgs(String[] args, String value) { |
| if (args != null) { |
| for (String arg : args) { |
| if (value.equals(arg)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private final boolean removeDyingProviderLocked(ProcessRecord proc, |
| ContentProviderRecord cpr, boolean always) { |
| final boolean inLaunching = mLaunchingProviders.contains(cpr); |
| |
| if (!inLaunching || always) { |
| synchronized (cpr) { |
| cpr.launchingApp = null; |
| cpr.notifyAll(); |
| } |
| mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid)); |
| String names[] = cpr.info.authority.split(";"); |
| for (int j = 0; j < names.length; j++) { |
| mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid)); |
| } |
| } |
| |
| for (int i = cpr.connections.size() - 1; i >= 0; i--) { |
| ContentProviderConnection conn = cpr.connections.get(i); |
| if (conn.waiting) { |
| // If this connection is waiting for the provider, then we don't |
| // need to mess with its process unless we are always removing |
| // or for some reason the provider is not currently launching. |
| if (inLaunching && !always) { |
| continue; |
| } |
| } |
| ProcessRecord capp = conn.client; |
| conn.dead = true; |
| if (conn.stableCount > 0) { |
| if (!capp.persistent && capp.thread != null |
| && capp.pid != 0 |
| && capp.pid != MY_PID) { |
| capp.kill("depends on provider " |
| + cpr.name.flattenToShortString() |
| + " in dying proc " + (proc != null ? proc.processName : "??") |
| + " (adj " + (proc != null ? proc.setAdj : "??") + ")", true); |
| } |
| } else if (capp.thread != null && conn.provider.provider != null) { |
| try { |
| capp.thread.unstableProviderDied(conn.provider.provider.asBinder()); |
| } catch (RemoteException e) { |
| } |
| // In the protocol here, we don't expect the client to correctly |
| // clean up this connection, we'll just remove it. |
| cpr.connections.remove(i); |
| if (conn.client.conProviders.remove(conn)) { |
| stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name); |
| } |
| } |
| } |
| |
| if (inLaunching && always) { |
| mLaunchingProviders.remove(cpr); |
| } |
| return inLaunching; |
| } |
| |
| /** |
| * Main code for cleaning up a process when it has gone away. This is |
| * called both as a result of the process dying, or directly when stopping |
| * a process when running in single process mode. |
| * |
| * @return Returns true if the given process has been restarted, so the |
| * app that was passed in must remain on the process lists. |
| */ |
| @GuardedBy("this") |
| private final boolean cleanUpApplicationRecordLocked(ProcessRecord app, |
| boolean restarting, boolean allowRestart, int index, boolean replacingPid) { |
| if (index >= 0) { |
| removeLruProcessLocked(app); |
| ProcessList.remove(app.pid); |
| } |
| |
| mProcessesToGc.remove(app); |
| mPendingPssProcesses.remove(app); |
| ProcessList.abortNextPssTime(app.procStateMemTracker); |
| |
| // Dismiss any open dialogs. |
| if (app.crashDialog != null && !app.forceCrashReport) { |
| app.crashDialog.dismiss(); |
| app.crashDialog = null; |
| } |
| if (app.anrDialog != null) { |
| app.anrDialog.dismiss(); |
| app.anrDialog = null; |
| } |
| if (app.waitDialog != null) { |
| app.waitDialog.dismiss(); |
| app.waitDialog = null; |
| } |
| |
| app.crashing = false; |
| app.notResponding = false; |
| |
| app.resetPackageList(mProcessStats); |
| app.unlinkDeathRecipient(); |
| app.makeInactive(mProcessStats); |
| app.waitingToKill = null; |
| app.forcingToImportant = null; |
| updateProcessForegroundLocked(app, false, false); |
| app.foregroundActivities = false; |
| app.hasShownUi = false; |
| app.treatLikeActivity = false; |
| app.hasAboveClient = false; |
| app.hasClientActivities = false; |
| |
| mServices.killServicesLocked(app, allowRestart); |
| |
| boolean restart = false; |
| |
| // Remove published content providers. |
| for (int i = app.pubProviders.size() - 1; i >= 0; i--) { |
| ContentProviderRecord cpr = app.pubProviders.valueAt(i); |
| final boolean always = app.bad || !allowRestart; |
| boolean inLaunching = removeDyingProviderLocked(app, cpr, always); |
| if ((inLaunching || always) && cpr.hasConnectionOrHandle()) { |
| // We left the provider in the launching list, need to |
| // restart it. |
| restart = true; |
| } |
| |
| cpr.provider = null; |
| cpr.proc = null; |
| } |
| app.pubProviders.clear(); |
| |
| // Take care of any launching providers waiting for this process. |
| if (cleanupAppInLaunchingProvidersLocked(app, false)) { |
| restart = true; |
| } |
| |
| // Unregister from connected content providers. |
| if (!app.conProviders.isEmpty()) { |
| for (int i = app.conProviders.size() - 1; i >= 0; i--) { |
| ContentProviderConnection conn = app.conProviders.get(i); |
| conn.provider.connections.remove(conn); |
| stopAssociationLocked(app.uid, app.processName, conn.provider.uid, |
| conn.provider.name); |
| } |
| app.conProviders.clear(); |
| } |
| |
| // At this point there may be remaining entries in mLaunchingProviders |
| // where we were the only one waiting, so they are no longer of use. |
| // Look for these and clean up if found. |
| // XXX Commented out for now. Trying to figure out a way to reproduce |
| // the actual situation to identify what is actually going on. |
| if (false) { |
| for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) { |
| ContentProviderRecord cpr = mLaunchingProviders.get(i); |
| if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) { |
| synchronized (cpr) { |
| cpr.launchingApp = null; |
| cpr.notifyAll(); |
| } |
| } |
| } |
| } |
| |
| skipCurrentReceiverLocked(app); |
| |
| // Unregister any receivers. |
| for (int i = app.receivers.size() - 1; i >= 0; i--) { |
| removeReceiverLocked(app.receivers.valueAt(i)); |
| } |
| app.receivers.clear(); |
| |
| // If the app is undergoing backup, tell the backup manager about it |
| if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) { |
| if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App " |
| + mBackupTarget.appInfo + " died during backup"); |
| 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; backup manager is local |
| } |
| } |
| }); |
| } |
| |
| for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) { |
| ProcessChangeItem item = mPendingProcessChanges.get(i); |
| if (app.pid > 0 && item.pid == app.pid) { |
| mPendingProcessChanges.remove(i); |
| mAvailProcessChanges.add(item); |
| } |
| } |
| mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid, |
| null).sendToTarget(); |
| |
| // If the caller is restarting this app, then leave it in its |
| // current lists and let the caller take care of it. |
| if (restarting) { |
| return false; |
| } |
| |
| if (!app.persistent || app.isolated) { |
| if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, |
| "Removing non-persistent process during cleanup: " + app); |
| if (!replacingPid) { |
| removeProcessNameLocked(app.processName, app.uid, app); |
| } |
| if (mHeavyWeightProcess == app) { |
| mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG, |
| mHeavyWeightProcess.userId, 0)); |
| mHeavyWeightProcess = null; |
| } |
| } else if (!app.removed) { |
| // This app is persistent, so we need to keep its record around. |
| // If it is not already on the pending app list, add it there |
| // and start a new process for it. |
| if (mPersistentStartingProcesses.indexOf(app) < 0) { |
| mPersistentStartingProcesses.add(app); |
| restart = true; |
| } |
| } |
| if ((DEBUG_PROCESSES || DEBUG_CLEANUP) && mProcessesOnHold.contains(app)) Slog.v( |
| TAG_CLEANUP, "Clean-up removing on hold: " + app); |
| mProcessesOnHold.remove(app); |
| |
| if (app == mHomeProcess) { |
| mHomeProcess = null; |
| } |
| if (app == mPreviousProcess) { |
| mPreviousProcess = null; |
| } |
| |
| if (restart && !app.isolated) { |
| // We have components that still need to be running in the |
| // process, so re-launch it. |
| if (index < 0) { |
| ProcessList.remove(app.pid); |
| } |
| addProcessNameLocked(app); |
| app.pendingStart = false; |
| startProcessLocked(app, "restart", app.processName); |
| return true; |
| } else if (app.pid > 0 && app.pid != MY_PID) { |
| // Goodbye! |
| boolean removed; |
| synchronized (mPidsSelfLocked) { |
| mPidsSelfLocked.remove(app.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); |
| } |
| app.setPid(0); |
| } |
| return false; |
| } |
| |
| boolean checkAppInLaunchingProvidersLocked(ProcessRecord app) { |
| for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) { |
| ContentProviderRecord cpr = mLaunchingProviders.get(i); |
| if (cpr.launchingApp == app) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) { |
| // Look through the content providers we are waiting to have launched, |
| // and if any run in this process then either schedule a restart of |
| // the process or kill the client waiting for it if this process has |
| // gone bad. |
| boolean restart = false; |
| for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) { |
| ContentProviderRecord cpr = mLaunchingProviders.get(i); |
| if (cpr.launchingApp == app) { |
| if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) { |
| restart = true; |
| } else { |
| removeDyingProviderLocked(app, cpr, true); |
| } |
| } |
| } |
| return restart; |
| } |
| |
| // ========================================================= |
| // SERVICES |
| // ========================================================= |
| |
| @Override |
| public List<ActivityManager.RunningServiceInfo> getServices(int maxNum, int flags) { |
| enforceNotIsolatedCaller("getServices"); |
| |
| final int callingUid = Binder.getCallingUid(); |
| final boolean canInteractAcrossUsers = (ActivityManager.checkUidPermission( |
| INTERACT_ACROSS_USERS_FULL, callingUid) == PERMISSION_GRANTED); |
| final boolean allowed = isGetTasksAllowed("getServices", Binder.getCallingPid(), |
| callingUid); |
| synchronized (this) { |
| return mServices.getRunningServiceInfoLocked(maxNum, flags, callingUid, |
| allowed, canInteractAcrossUsers); |
| } |
| } |
| |
| @Override |
| public PendingIntent getRunningServiceControlPanel(ComponentName name) { |
| enforceNotIsolatedCaller("getRunningServiceControlPanel"); |
| synchronized (this) { |
| return mServices.getRunningServiceControlPanelLocked(name); |
| } |
| } |
| |
| @Override |
| public ComponentName startService(IApplicationThread caller, Intent service, |
| String resolvedType, boolean requireForeground, String callingPackage, int userId) |
| throws TransactionTooLargeException { |
| enforceNotIsolatedCaller("startService"); |
| // Refuse possible leaked file descriptors |
| if (service != null && service.hasFileDescriptors() == true) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| if (callingPackage == null) { |
| throw new IllegalArgumentException("callingPackage cannot be null"); |
| } |
| |
| if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, |
| "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground); |
| synchronized(this) { |
| final int callingPid = Binder.getCallingPid(); |
| final int callingUid = Binder.getCallingUid(); |
| final long origId = Binder.clearCallingIdentity(); |
| ComponentName res; |
| try { |
| res = mServices.startServiceLocked(caller, service, |
| resolvedType, callingPid, callingUid, |
| requireForeground, callingPackage, userId); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| return res; |
| } |
| } |
| |
| ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, |
| boolean fgRequired, String callingPackage, int userId) |
| throws TransactionTooLargeException { |
| synchronized(this) { |
| if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, |
| "startServiceInPackage: " + service + " type=" + resolvedType); |
| final long origId = Binder.clearCallingIdentity(); |
| ComponentName res; |
| try { |
| res = mServices.startServiceLocked(null, service, |
| resolvedType, -1, uid, fgRequired, callingPackage, userId); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| return res; |
| } |
| } |
| |
| @Override |
| public int stopService(IApplicationThread caller, Intent service, |
| String resolvedType, int userId) { |
| enforceNotIsolatedCaller("stopService"); |
| // Refuse possible leaked file descriptors |
| if (service != null && service.hasFileDescriptors() == true) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| synchronized(this) { |
| return mServices.stopServiceLocked(caller, service, resolvedType, userId); |
| } |
| } |
| |
| @Override |
| public IBinder peekService(Intent service, String resolvedType, String callingPackage) { |
| enforceNotIsolatedCaller("peekService"); |
| // Refuse possible leaked file descriptors |
| if (service != null && service.hasFileDescriptors() == true) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| if (callingPackage == null) { |
| throw new IllegalArgumentException("callingPackage cannot be null"); |
| } |
| |
| synchronized(this) { |
| return mServices.peekServiceLocked(service, resolvedType, callingPackage); |
| } |
| } |
| |
| @Override |
| public boolean stopServiceToken(ComponentName className, IBinder token, |
| int startId) { |
| synchronized(this) { |
| return mServices.stopServiceTokenLocked(className, token, startId); |
| } |
| } |
| |
| @Override |
| public void setServiceForeground(ComponentName className, IBinder token, |
| int id, Notification notification, int flags) { |
| synchronized(this) { |
| mServices.setServiceForegroundLocked(className, token, id, notification, flags); |
| } |
| } |
| |
| @Override |
| public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, |
| boolean requireFull, String name, String callerPackage) { |
| return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll, |
| requireFull ? ALLOW_FULL_ONLY : ALLOW_NON_FULL, name, callerPackage); |
| } |
| |
| boolean isSingleton(String componentProcessName, ApplicationInfo aInfo, |
| String className, int flags) { |
| boolean result = false; |
| // For apps that don't have pre-defined UIDs, check for permission |
| if (UserHandle.getAppId(aInfo.uid) >= FIRST_APPLICATION_UID) { |
| if ((flags & ServiceInfo.FLAG_SINGLE_USER) != 0) { |
| if (ActivityManager.checkUidPermission( |
| INTERACT_ACROSS_USERS, |
| aInfo.uid) != PackageManager.PERMISSION_GRANTED) { |
| ComponentName comp = new ComponentName(aInfo.packageName, className); |
| String msg = "Permission Denial: Component " + comp.flattenToShortString() |
| + " requests FLAG_SINGLE_USER, but app does not hold " |
| + INTERACT_ACROSS_USERS; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| // Permission passed |
| result = true; |
| } |
| } else if ("system".equals(componentProcessName)) { |
| result = true; |
| } else if ((flags & ServiceInfo.FLAG_SINGLE_USER) != 0) { |
| // Phone app and persistent apps are allowed to export singleuser providers. |
| result = UserHandle.isSameApp(aInfo.uid, PHONE_UID) |
| || (aInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0; |
| } |
| if (DEBUG_MU) Slog.v(TAG_MU, |
| "isSingleton(" + componentProcessName + ", " + aInfo + ", " + className + ", 0x" |
| + Integer.toHexString(flags) + ") = " + result); |
| return result; |
| } |
| |
| /** |
| * Checks to see if the caller is in the same app as the singleton |
| * component, or the component is in a special app. It allows special apps |
| * to export singleton components but prevents exporting singleton |
| * components for regular apps. |
| */ |
| boolean isValidSingletonCall(int callingUid, int componentUid) { |
| int componentAppId = UserHandle.getAppId(componentUid); |
| return UserHandle.isSameApp(callingUid, componentUid) |
| || componentAppId == SYSTEM_UID |
| || componentAppId == PHONE_UID |
| || ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, componentUid) |
| == PackageManager.PERMISSION_GRANTED; |
| } |
| |
| public int bindService(IApplicationThread caller, IBinder token, Intent service, |
| String resolvedType, IServiceConnection connection, int flags, String callingPackage, |
| int userId) throws TransactionTooLargeException { |
| enforceNotIsolatedCaller("bindService"); |
| |
| // Refuse possible leaked file descriptors |
| if (service != null && service.hasFileDescriptors() == true) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| if (callingPackage == null) { |
| throw new IllegalArgumentException("callingPackage cannot be null"); |
| } |
| |
| synchronized(this) { |
| return mServices.bindServiceLocked(caller, token, service, |
| resolvedType, connection, flags, callingPackage, userId); |
| } |
| } |
| |
| public boolean unbindService(IServiceConnection connection) { |
| synchronized (this) { |
| return mServices.unbindServiceLocked(connection); |
| } |
| } |
| |
| public void publishService(IBinder token, Intent intent, IBinder service) { |
| // Refuse possible leaked file descriptors |
| if (intent != null && intent.hasFileDescriptors() == true) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| synchronized(this) { |
| if (!(token instanceof ServiceRecord)) { |
| throw new IllegalArgumentException("Invalid service token"); |
| } |
| mServices.publishServiceLocked((ServiceRecord)token, intent, service); |
| } |
| } |
| |
| public void unbindFinished(IBinder token, Intent intent, boolean doRebind) { |
| // Refuse possible leaked file descriptors |
| if (intent != null && intent.hasFileDescriptors() == true) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| synchronized(this) { |
| mServices.unbindFinishedLocked((ServiceRecord)token, intent, doRebind); |
| } |
| } |
| |
| public void serviceDoneExecuting(IBinder token, int type, int startId, int res) { |
| synchronized(this) { |
| if (!(token instanceof ServiceRecord)) { |
| Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token); |
| throw new IllegalArgumentException("Invalid service token"); |
| } |
| mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res); |
| } |
| } |
| |
| // ========================================================= |
| // BACKUP AND RESTORE |
| // ========================================================= |
| |
| // Cause the target app to be launched if necessary and its backup agent |
| // instantiated. The backup agent will invoke backupAgentCreated() on the |
| // activity manager to announce its creation. |
| public boolean bindBackupAgent(String packageName, int backupMode, int userId) { |
| if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode); |
| enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "bindBackupAgent"); |
| |
| IPackageManager pm = AppGlobals.getPackageManager(); |
| ApplicationInfo app = null; |
| try { |
| app = pm.getApplicationInfo(packageName, STOCK_PM_FLAGS, userId); |
| } catch (RemoteException e) { |
| // can't happen; package manager is process-local |
| } |
| if (app == null) { |
| Slog.w(TAG, "Unable to bind backup agent for " + packageName); |
| return false; |
| } |
| |
| int oldBackupUid; |
| int newBackupUid; |
| |
| synchronized(this) { |
| // !!! TODO: currently no check here that we're already bound |
| BatteryStatsImpl.Uid.Pkg.Serv ss = null; |
| BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); |
| synchronized (stats) { |
| ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name); |
| } |
| |
| // Backup agent is now in use, its package can't be stopped. |
| try { |
| AppGlobals.getPackageManager().setPackageStoppedState( |
| app.packageName, false, UserHandle.getUserId(app.uid)); |
| } catch (RemoteException e) { |
| } catch (IllegalArgumentException e) { |
| Slog.w(TAG, "Failed trying to unstop package " |
| + app.packageName + ": " + e); |
| } |
| |
| BackupRecord r = new BackupRecord(ss, app, backupMode); |
| ComponentName hostingName = |
| (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL) |
| ? new ComponentName(app.packageName, app.backupAgentName) |
| : new ComponentName("android", "FullBackupAgent"); |
| // startProcessLocked() returns existing proc's record if it's already running |
| ProcessRecord proc = startProcessLocked(app.processName, app, |
| false, 0, "backup", hostingName, false, false, false); |
| if (proc == null) { |
| Slog.e(TAG, "Unable to start backup agent process " + r); |
| return false; |
| } |
| |
| // If the app is a regular app (uid >= 10000) and not the system server or phone |
| // process, etc, then mark it as being in full backup so that certain calls to the |
| // process can be blocked. This is not reset to false anywhere because we kill the |
| // process after the full backup is done and the ProcessRecord will vaporize anyway. |
| if (UserHandle.isApp(app.uid) && |
| backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL) { |
| proc.inFullBackup = true; |
| } |
| r.app = proc; |
| oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1; |
| newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1; |
| mBackupTarget = r; |
| mBackupAppName = app.packageName; |
| |
| // Try not to kill the process during backup |
| updateOomAdjLocked(proc, true); |
| |
| // If the process is already attached, schedule the creation of the backup agent now. |
| // If it is not yet live, this will be done when it attaches to the framework. |
| if (proc.thread != null) { |
| if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc); |
| try { |
| proc.thread.scheduleCreateBackupAgent(app, |
| compatibilityInfoForPackageLocked(app), backupMode); |
| } catch (RemoteException e) { |
| // Will time out on the backup manager side |
| } |
| } else { |
| if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc not running, waiting for attach"); |
| } |
| // Invariants: at this point, the target app process exists and the application |
| // is either already running or in the process of coming up. mBackupTarget and |
| // mBackupAppName describe the app, so that when it binds back to the AM we |
| // know that it's scheduled for a backup-agent operation. |
| } |
| |
| JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); |
| if (oldBackupUid != -1) { |
| js.removeBackingUpUid(oldBackupUid); |
| } |
| if (newBackupUid != -1) { |
| js.addBackingUpUid(newBackupUid); |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public void clearPendingBackup() { |
| if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "clearPendingBackup"); |
| enforceCallingPermission("android.permission.BACKUP", "clearPendingBackup"); |
| |
| synchronized (this) { |
| mBackupTarget = null; |
| mBackupAppName = null; |
| } |
| |
| JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); |
| js.clearAllBackingUpUids(); |
| } |
| |
| // A backup agent has just come up |
| public void backupAgentCreated(String agentPackageName, IBinder agent) { |
| if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "backupAgentCreated: " + agentPackageName |
| + " = " + agent); |
| |
| synchronized(this) { |
| if (!agentPackageName.equals(mBackupAppName)) { |
| Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!"); |
| return; |
| } |
| } |
| |
| long oldIdent = Binder.clearCallingIdentity(); |
| try { |
| IBackupManager bm = IBackupManager.Stub.asInterface( |
| ServiceManager.getService(Context.BACKUP_SERVICE)); |
| bm.agentConnected(agentPackageName, agent); |
| } catch (RemoteException e) { |
| // can't happen; the backup manager service is local |
| } catch (Exception e) { |
| Slog.w(TAG, "Exception trying to deliver BackupAgent binding: "); |
| e.printStackTrace(); |
| } finally { |
| Binder.restoreCallingIdentity(oldIdent); |
| } |
| } |
| |
| // done with this agent |
| public void unbindBackupAgent(ApplicationInfo appInfo) { |
| if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "unbindBackupAgent: " + appInfo); |
| enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "unbindBackupAgent"); |
| if (appInfo == null) { |
| Slog.w(TAG, "unbind backup agent for null app"); |
| return; |
| } |
| |
| int oldBackupUid; |
| |
| synchronized(this) { |
| try { |
| if (mBackupAppName == null) { |
| Slog.w(TAG, "Unbinding backup agent with no active backup"); |
| return; |
| } |
| |
| if (!mBackupAppName.equals(appInfo.packageName)) { |
| Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); |
| return; |
| } |
| |
| // Not backing this app up any more; reset its OOM adjustment |
| final ProcessRecord proc = mBackupTarget.app; |
| updateOomAdjLocked(proc, true); |
| proc.inFullBackup = false; |
| |
| oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1; |
| |
| // If the app crashed during backup, 'thread' will be null here |
| if (proc.thread != null) { |
| try { |
| proc.thread.scheduleDestroyBackupAgent(appInfo, |
| compatibilityInfoForPackageLocked(appInfo)); |
| } catch (Exception e) { |
| Slog.e(TAG, "Exception when unbinding backup agent:"); |
| e.printStackTrace(); |
| } |
| } |
| } finally { |
| mBackupTarget = null; |
| mBackupAppName = null; |
| } |
| } |
| |
| if (oldBackupUid != -1) { |
| JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); |
| js.removeBackingUpUid(oldBackupUid); |
| } |
| } |
| |
| // ========================================================= |
| // BROADCASTS |
| // ========================================================= |
| |
| private boolean isInstantApp(ProcessRecord record, @Nullable String callerPackage, int uid) { |
| if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) { |
| return false; |
| } |
| // Easy case -- we have the app's ProcessRecord. |
| if (record != null) { |
| return record.info.isInstantApp(); |
| } |
| // Otherwise check with PackageManager. |
| IPackageManager pm = AppGlobals.getPackageManager(); |
| try { |
| if (callerPackage == null) { |
| final String[] packageNames = pm.getPackagesForUid(uid); |
| if (packageNames == null || packageNames.length == 0) { |
| throw new IllegalArgumentException("Unable to determine caller package name"); |
| } |
| // Instant Apps can't use shared uids, so its safe to only check the first package. |
| callerPackage = packageNames[0]; |
| } |
| mAppOpsService.checkPackage(uid, callerPackage); |
| return pm.isInstantApp(callerPackage, UserHandle.getUserId(uid)); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "Error looking up if " + callerPackage + " is an instant app.", e); |
| return true; |
| } |
| } |
| |
| boolean isPendingBroadcastProcessLocked(int pid) { |
| return mFgBroadcastQueue.isPendingBroadcastProcessLocked(pid) |
| || mBgBroadcastQueue.isPendingBroadcastProcessLocked(pid); |
| } |
| |
| void skipPendingBroadcastLocked(int pid) { |
| Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping"); |
| for (BroadcastQueue queue : mBroadcastQueues) { |
| queue.skipPendingBroadcastLocked(pid); |
| } |
| } |
| |
| // The app just attached; send any pending broadcasts that it should receive |
| boolean sendPendingBroadcastsLocked(ProcessRecord app) { |
| boolean didSomething = false; |
| for (BroadcastQueue queue : mBroadcastQueues) { |
| didSomething |= queue.sendPendingBroadcastsLocked(app); |
| } |
| return didSomething; |
| } |
| |
| public Intent registerReceiver(IApplicationThread caller, String callerPackage, |
| IIntentReceiver receiver, IntentFilter filter, String permission, int userId, |
| int flags) { |
| enforceNotIsolatedCaller("registerReceiver"); |
| ArrayList<Intent> stickyIntents = null; |
| ProcessRecord callerApp = null; |
| final boolean visibleToInstantApps |
| = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0; |
| int callingUid; |
| int callingPid; |
| boolean instantApp; |
| synchronized(this) { |
| if (caller != null) { |
| callerApp = getRecordForAppLocked(caller); |
| if (callerApp == null) { |
| throw new SecurityException( |
| "Unable to find app for caller " + caller |
| + " (pid=" + Binder.getCallingPid() |
| + ") when registering receiver " + receiver); |
| } |
| if (callerApp.info.uid != SYSTEM_UID && |
| !callerApp.pkgList.containsKey(callerPackage) && |
| !"android".equals(callerPackage)) { |
| throw new SecurityException("Given caller package " + callerPackage |
| + " is not running in process " + callerApp); |
| } |
| callingUid = callerApp.info.uid; |
| callingPid = callerApp.pid; |
| } else { |
| callerPackage = null; |
| callingUid = Binder.getCallingUid(); |
| callingPid = Binder.getCallingPid(); |
| } |
| |
| instantApp = isInstantApp(callerApp, callerPackage, callingUid); |
| userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true, |
| ALLOW_FULL_ONLY, "registerReceiver", callerPackage); |
| |
| Iterator<String> actions = filter.actionsIterator(); |
| if (actions == null) { |
| ArrayList<String> noAction = new ArrayList<String>(1); |
| noAction.add(null); |
| actions = noAction.iterator(); |
| } |
| |
| // Collect stickies of users |
| int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; |
| while (actions.hasNext()) { |
| String action = actions.next(); |
| for (int id : userIds) { |
| ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id); |
| if (stickies != null) { |
| ArrayList<Intent> intents = stickies.get(action); |
| if (intents != null) { |
| if (stickyIntents == null) { |
| stickyIntents = new ArrayList<Intent>(); |
| } |
| stickyIntents.addAll(intents); |
| } |
| } |
| } |
| } |
| } |
| |
| ArrayList<Intent> allSticky = null; |
| if (stickyIntents != null) { |
| final ContentResolver resolver = mContext.getContentResolver(); |
| // Look for any matching sticky broadcasts... |
| for (int i = 0, N = stickyIntents.size(); i < N; i++) { |
| Intent intent = stickyIntents.get(i); |
| // Don't provided intents that aren't available to instant apps. |
| if (instantApp && |
| (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) { |
| continue; |
| } |
| // If intent has scheme "content", it will need to acccess |
| // provider that needs to lock mProviderMap in ActivityThread |
| // and also it may need to wait application response, so we |
| // cannot lock ActivityManagerService here. |
| if (filter.match(resolver, intent, true, TAG) >= 0) { |
| if (allSticky == null) { |
| allSticky = new ArrayList<Intent>(); |
| } |
| allSticky.add(intent); |
| } |
| } |
| } |
| |
| // The first sticky in the list is returned directly back to the client. |
| Intent sticky = allSticky != null ? allSticky.get(0) : null; |
| if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky); |
| if (receiver == null) { |
| return sticky; |
| } |
| |
| synchronized (this) { |
| if (callerApp != null && (callerApp.thread == null |
| || callerApp.thread.asBinder() != caller.asBinder())) { |
| // Original caller already died |
| return null; |
| } |
| ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()); |
| if (rl == null) { |
| rl = new ReceiverList(this, callerApp, callingPid, callingUid, |
| userId, receiver); |
| if (rl.app != null) { |
| final int totalReceiversForApp = rl.app.receivers.size(); |
| if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) { |
| throw new IllegalStateException("Too many receivers, total of " |
| + totalReceiversForApp + ", registered for pid: " |
| + rl.pid + ", callerPackage: " + callerPackage); |
| } |
| rl.app.receivers.add(rl); |
| } else { |
| try { |
| receiver.asBinder().linkToDeath(rl, 0); |
| } catch (RemoteException e) { |
| return sticky; |
| } |
| rl.linkedToDeath = true; |
| } |
| mRegisteredReceivers.put(receiver.asBinder(), rl); |
| } else if (rl.uid != callingUid) { |
| throw new IllegalArgumentException( |
| "Receiver requested to register for uid " + callingUid |
| + " was previously registered for uid " + rl.uid |
| + " callerPackage is " + callerPackage); |
| } else if (rl.pid != callingPid) { |
| throw new IllegalArgumentException( |
| "Receiver requested to register for pid " + callingPid |
| + " was previously registered for pid " + rl.pid |
| + " callerPackage is " + callerPackage); |
| } else if (rl.userId != userId) { |
| throw new IllegalArgumentException( |
| "Receiver requested to register for user " + userId |
| + " was previously registered for user " + rl.userId |
| + " callerPackage is " + callerPackage); |
| } |
| BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, |
| permission, callingUid, userId, instantApp, visibleToInstantApps); |
| if (rl.containsFilter(filter)) { |
| Slog.w(TAG, "Receiver with filter " + filter |
| + " already registered for pid " + rl.pid |
| + ", callerPackage is " + callerPackage); |
| } else { |
| rl.add(bf); |
| if (!bf.debugCheck()) { |
| Slog.w(TAG, "==> For Dynamic broadcast"); |
| } |
| mReceiverResolver.addFilter(bf); |
| } |
| |
| // Enqueue broadcasts for all existing stickies that match |
| // this filter. |
| if (allSticky != null) { |
| ArrayList receivers = new ArrayList(); |
| receivers.add(bf); |
| |
| final int stickyCount = allSticky.size(); |
| for (int i = 0; i < stickyCount; i++) { |
| Intent intent = allSticky.get(i); |
| BroadcastQueue queue = broadcastQueueForIntent(intent); |
| BroadcastRecord r = new BroadcastRecord(queue, intent, null, |
| null, -1, -1, false, null, null, OP_NONE, null, receivers, |
| null, 0, null, null, false, true, true, -1); |
| queue.enqueueParallelBroadcastLocked(r); |
| queue.scheduleBroadcastsLocked(); |
| } |
| } |
| |
| return sticky; |
| } |
| } |
| |
| public void unregisterReceiver(IIntentReceiver receiver) { |
| if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Unregister receiver: " + receiver); |
| |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| boolean doTrim = false; |
| |
| synchronized(this) { |
| ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()); |
| if (rl != null) { |
| final BroadcastRecord r = rl.curBroadcast; |
| if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) { |
| final boolean doNext = r.queue.finishReceiverLocked( |
| r, r.resultCode, r.resultData, r.resultExtras, |
| r.resultAbort, false); |
| if (doNext) { |
| doTrim = true; |
| r.queue.processNextBroadcast(false); |
| } |
| } |
| |
| if (rl.app != null) { |
| rl.app.receivers.remove(rl); |
| } |
| removeReceiverLocked(rl); |
| if (rl.linkedToDeath) { |
| rl.linkedToDeath = false; |
| rl.receiver.asBinder().unlinkToDeath(rl, 0); |
| } |
| } |
| } |
| |
| // If we actually concluded any broadcasts, we might now be able |
| // to trim the recipients' apps from our working set |
| if (doTrim) { |
| trimApplications(); |
| return; |
| } |
| |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| void removeReceiverLocked(ReceiverList rl) { |
| mRegisteredReceivers.remove(rl.receiver.asBinder()); |
| for (int i = rl.size() - 1; i >= 0; i--) { |
| mReceiverResolver.removeFilter(rl.get(i)); |
| } |
| } |
| |
| private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) { |
| for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { |
| ProcessRecord r = mLruProcesses.get(i); |
| if (r.thread != null && (userId == UserHandle.USER_ALL || r.userId == userId)) { |
| try { |
| r.thread.dispatchPackageBroadcast(cmd, packages); |
| } catch (RemoteException ex) { |
| } |
| } |
| } |
| } |
| |
| private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType, |
| int callingUid, int[] users) { |
| // TODO: come back and remove this assumption to triage all broadcasts |
| int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING; |
| |
| List<ResolveInfo> receivers = null; |
| try { |
| HashSet<ComponentName> singleUserReceivers = null; |
| boolean scannedFirstReceivers = false; |
| for (int user : users) { |
| // Skip users that have Shell restrictions, with exception of always permitted |
| // Shell broadcasts |
| if (callingUid == SHELL_UID |
| && mUserController.hasUserRestriction( |
| UserManager.DISALLOW_DEBUGGING_FEATURES, user) |
| && !isPermittedShellBroadcast(intent)) { |
| continue; |
| } |
| List<ResolveInfo> newReceivers = AppGlobals.getPackageManager() |
| .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList(); |
| if (user != UserHandle.USER_SYSTEM && newReceivers != null) { |
| // If this is not the system user, we need to check for |
| // any receivers that should be filtered out. |
| for (int i=0; i<newReceivers.size(); i++) { |
| ResolveInfo ri = newReceivers.get(i); |
| if ((ri.activityInfo.flags&ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) { |
| newReceivers.remove(i); |
| i--; |
| } |
| } |
| } |
| if (newReceivers != null && newReceivers.size() == 0) { |
| newReceivers = null; |
| } |
| if (receivers == null) { |
| receivers = newReceivers; |
| } else if (newReceivers != null) { |
| // We need to concatenate the additional receivers |
| // found with what we have do far. This would be easy, |
| // but we also need to de-dup any receivers that are |
| // singleUser. |
| if (!scannedFirstReceivers) { |
| // Collect any single user receivers we had already retrieved. |
| scannedFirstReceivers = true; |
| for (int i=0; i<receivers.size(); i++) { |
| ResolveInfo ri = receivers.get(i); |
| if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) { |
| ComponentName cn = new ComponentName( |
| ri.activityInfo.packageName, ri.activityInfo.name); |
| if (singleUserReceivers == null) { |
| singleUserReceivers = new HashSet<ComponentName>(); |
| } |
| singleUserReceivers.add(cn); |
| } |
| } |
| } |
| // Add the new results to the existing results, tracking |
| // and de-dupping single user receivers. |
| for (int i=0; i<newReceivers.size(); i++) { |
| ResolveInfo ri = newReceivers.get(i); |
| if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) { |
| ComponentName cn = new ComponentName( |
| ri.activityInfo.packageName, ri.activityInfo.name); |
| if (singleUserReceivers == null) { |
| singleUserReceivers = new HashSet<ComponentName>(); |
| } |
| if (!singleUserReceivers.contains(cn)) { |
| singleUserReceivers.add(cn); |
| receivers.add(ri); |
| } |
| } else { |
| receivers.add(ri); |
| } |
| } |
| } |
| } |
| } catch (RemoteException ex) { |
| // pm is in same process, this will never happen. |
| } |
| return receivers; |
| } |
| |
| private boolean isPermittedShellBroadcast(Intent intent) { |
| // remote bugreport should always be allowed to be taken |
| return INTENT_REMOTE_BUGREPORT_FINISHED.equals(intent.getAction()); |
| } |
| |
| private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp, |
| String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) { |
| if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) { |
| // Don't yell about broadcasts sent via shell |
| return; |
| } |
| |
| final String action = intent.getAction(); |
| if (isProtectedBroadcast |
| || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) |
| || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action) |
| || Intent.ACTION_MEDIA_BUTTON.equals(action) |
| || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action) |
| || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action) |
| || Intent.ACTION_MASTER_CLEAR.equals(action) |
| || Intent.ACTION_FACTORY_RESET.equals(action) |
| || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action) |
| || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action) |
| || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action) |
| || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action) |
| || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action) |
| || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action) |
| || AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.equals(action)) { |
| // Broadcast is either protected, or it's a public action that |
| // we've relaxed, so it's fine for system internals to send. |
| return; |
| } |
| |
| // This broadcast may be a problem... but there are often system components that |
| // want to send an internal broadcast to themselves, which is annoying to have to |
| // explicitly list each action as a protected broadcast, so we will check for that |
| // one safe case and allow it: an explicit broadcast, only being received by something |
| // that has protected itself. |
| if (intent.getPackage() != null || intent.getComponent() != null) { |
| if (receivers == null || receivers.size() == 0) { |
| // Intent is explicit and there's no receivers. |
| // This happens, e.g. , when a system component sends a broadcast to |
| // its own runtime receiver, and there's no manifest receivers for it, |
| // because this method is called twice for each broadcast, |
| // for runtime receivers and manifest receivers and the later check would find |
| // no receivers. |
| return; |
| } |
| boolean allProtected = true; |
| for (int i = receivers.size()-1; i >= 0; i--) { |
| Object target = receivers.get(i); |
| if (target instanceof ResolveInfo) { |
| ResolveInfo ri = (ResolveInfo)target; |
| if (ri.activityInfo.exported && ri.activityInfo.permission == null) { |
| allProtected = false; |
| break; |
| } |
| } else { |
| BroadcastFilter bf = (BroadcastFilter)target; |
| if (bf.requiredPermission == null) { |
| allProtected = false; |
| break; |
| } |
| } |
| } |
| if (allProtected) { |
| // All safe! |
| return; |
| } |
| } |
| |
| // The vast majority of broadcasts sent from system internals |
| // should be protected to avoid security holes, so yell loudly |
| // to ensure we examine these cases. |
| if (callerApp != null) { |
| Log.wtf(TAG, "Sending non-protected broadcast " + action |
| + " from system " + callerApp.toShortString() + " pkg " + callerPackage, |
| new Throwable()); |
| } else { |
| Log.wtf(TAG, "Sending non-protected broadcast " + action |
| + " from system uid " + UserHandle.formatUid(callingUid) |
| + " pkg " + callerPackage, |
| new Throwable()); |
| } |
| } |
| |
| @GuardedBy("this") |
| final int broadcastIntentLocked(ProcessRecord callerApp, |
| String callerPackage, Intent intent, String resolvedType, |
| IIntentReceiver resultTo, int resultCode, String resultData, |
| Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, |
| boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { |
| intent = new Intent(intent); |
| |
| final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid); |
| // Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS |
| if (callerInstantApp) { |
| intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); |
| } |
| |
| // By default broadcasts do not go to stopped apps. |
| intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); |
| |
| // If we have not finished booting, don't allow this to launch new processes. |
| if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) { |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); |
| } |
| |
| if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, |
| (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent |
| + " ordered=" + ordered + " userid=" + userId); |
| if ((resultTo != null) && !ordered) { |
| Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!"); |
| } |
| |
| userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true, |
| ALLOW_NON_FULL, "broadcast", callerPackage); |
| |
| // Make sure that the user who is receiving this broadcast or its parent is running. |
| // If not, we will just skip it. Make an exception for shutdown broadcasts, upgrade steps. |
| if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) { |
| if ((callingUid != SYSTEM_UID |
| || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) |
| && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { |
| Slog.w(TAG, "Skipping broadcast of " + intent |
| + ": user " + userId + " and its parent (if any) are stopped"); |
| return ActivityManager.BROADCAST_FAILED_USER_STOPPED; |
| } |
| } |
| |
| final String action = intent.getAction(); |
| BroadcastOptions brOptions = null; |
| if (bOptions != null) { |
| brOptions = new BroadcastOptions(bOptions); |
| if (brOptions.getTemporaryAppWhitelistDuration() > 0) { |
| // See if the caller is allowed to do this. Note we are checking against |
| // the actual real caller (not whoever provided the operation as say a |
| // PendingIntent), because that who is actually supplied the arguments. |
| if (checkComponentPermission( |
| android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, |
| Binder.getCallingPid(), Binder.getCallingUid(), -1, true) |
| != PackageManager.PERMISSION_GRANTED) { |
| String msg = "Permission Denial: " + intent.getAction() |
| + " broadcast from " + callerPackage + " (pid=" + callingPid |
| + ", uid=" + callingUid + ")" |
| + " requires " |
| + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| } |
| if (brOptions.isDontSendToRestrictedApps() |
| && !isUidActiveLocked(callingUid) |
| && isBackgroundRestrictedNoCheck(callingUid, callerPackage)) { |
| Slog.i(TAG, "Not sending broadcast " + action + " - app " + callerPackage |
| + " has background restrictions"); |
| return ActivityManager.START_CANCELED; |
| } |
| } |
| |
| // Verify that protected broadcasts are only being sent by system code, |
| // and that system code is only sending protected broadcasts. |
| final boolean isProtectedBroadcast; |
| try { |
| isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "Remote exception", e); |
| return ActivityManager.BROADCAST_SUCCESS; |
| } |
| |
| final boolean isCallerSystem; |
| switch (UserHandle.getAppId(callingUid)) { |
| case ROOT_UID: |
| case SYSTEM_UID: |
| case PHONE_UID: |
| case BLUETOOTH_UID: |
| case NFC_UID: |
| case SE_UID: |
| isCallerSystem = true; |
| break; |
| default: |
| isCallerSystem = (callerApp != null) && callerApp.persistent; |
| break; |
| } |
| |
| // First line security check before anything else: stop non-system apps from |
| // sending protected broadcasts. |
| if (!isCallerSystem) { |
| if (isProtectedBroadcast) { |
| String msg = "Permission Denial: not allowed to send broadcast " |
| + action + " from pid=" |
| + callingPid + ", uid=" + callingUid; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| |
| } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action) |
| || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) { |
| // Special case for compatibility: we don't want apps to send this, |
| // but historically it has not been protected and apps may be using it |
| // to poke their own app widget. So, instead of making it protected, |
| // just limit it to the caller. |
| if (callerPackage == null) { |
| String msg = "Permission Denial: not allowed to send broadcast " |
| + action + " from unknown caller."; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } else if (intent.getComponent() != null) { |
| // They are good enough to send to an explicit component... verify |
| // it is being sent to the calling app. |
| if (!intent.getComponent().getPackageName().equals( |
| callerPackage)) { |
| String msg = "Permission Denial: not allowed to send broadcast " |
| + action + " to " |
| + intent.getComponent().getPackageName() + " from " |
| + callerPackage; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| } else { |
| // Limit broadcast to their own package. |
| intent.setPackage(callerPackage); |
| } |
| } |
| } |
| |
| if (action != null) { |
| if (getBackgroundLaunchBroadcasts().contains(action)) { |
| if (DEBUG_BACKGROUND_CHECK) { |
| Slog.i(TAG, "Broadcast action " + action + " forcing include-background"); |
| } |
| intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); |
| } |
| |
| switch (action) { |
| case Intent.ACTION_UID_REMOVED: |
| case Intent.ACTION_PACKAGE_REMOVED: |
| case Intent.ACTION_PACKAGE_CHANGED: |
| case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: |
| case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: |
| case Intent.ACTION_PACKAGES_SUSPENDED: |
| case Intent.ACTION_PACKAGES_UNSUSPENDED: |
| // Handle special intents: if this broadcast is from the package |
| // manager about a package being removed, we need to remove all of |
| // its activities from the history stack. |
| if (checkComponentPermission( |
| android.Manifest.permission.BROADCAST_PACKAGE_REMOVED, |
| callingPid, callingUid, -1, true) |
| != PackageManager.PERMISSION_GRANTED) { |
| String msg = "Permission Denial: " + intent.getAction() |
| + " broadcast from " + callerPackage + " (pid=" + callingPid |
| + ", uid=" + callingUid + ")" |
| + " requires " |
| + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| switch (action) { |
| case Intent.ACTION_UID_REMOVED: |
| final int uid = getUidFromIntent(intent); |
| if (uid >= 0) { |
| mBatteryStatsService.removeUid(uid); |
| mAppOpsService.uidRemoved(uid); |
| } |
| break; |
| case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: |
| // If resources are unavailable just force stop all those packages |
| // and flush the attribute cache as well. |
| String list[] = |
| intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); |
| if (list != null && list.length > 0) { |
| for (int i = 0; i < list.length; i++) { |
| forceStopPackageLocked(list[i], -1, false, true, true, |
| false, false, userId, "storage unmount"); |
| } |
| mActivityTaskManager.getRecentTasks().cleanupLocked( |
| UserHandle.USER_ALL); |
| sendPackageBroadcastLocked( |
| ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE, |
| list, userId); |
| } |
| break; |
| case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: |
| mActivityTaskManager.getRecentTasks().cleanupLocked( |
| UserHandle.USER_ALL); |
| break; |
| case Intent.ACTION_PACKAGE_REMOVED: |
| case Intent.ACTION_PACKAGE_CHANGED: |
| Uri data = intent.getData(); |
| String ssp; |
| if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { |
| boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action); |
| final boolean replacing = |
| intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); |
| final boolean killProcess = |
| !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false); |
| final boolean fullUninstall = removed && !replacing; |
| if (removed) { |
| if (killProcess) { |
| forceStopPackageLocked(ssp, UserHandle.getAppId( |
| intent.getIntExtra(Intent.EXTRA_UID, -1)), |
| false, true, true, false, fullUninstall, userId, |
| removed ? "pkg removed" : "pkg changed"); |
| } |
| final int cmd = killProcess |
| ? ApplicationThreadConstants.PACKAGE_REMOVED |
| : ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL; |
| sendPackageBroadcastLocked(cmd, |
| new String[] {ssp}, userId); |
| if (fullUninstall) { |
| mAppOpsService.packageRemoved( |
| intent.getIntExtra(Intent.EXTRA_UID, -1), ssp); |
| |
| // Remove all permissions granted from/to this package |
| removeUriPermissionsForPackageLocked(ssp, userId, true, |
| false); |
| |
| mActivityTaskManager.getRecentTasks().removeTasksByPackageName(ssp, userId); |
| |
| mServices.forceStopPackageLocked(ssp, userId); |
| mAppWarnings.onPackageUninstalled(ssp); |
| mCompatModePackages.handlePackageUninstalledLocked(ssp); |
| mBatteryStatsService.notePackageUninstalled(ssp); |
| } |
| } else { |
| if (killProcess) { |
| killPackageProcessesLocked(ssp, UserHandle.getAppId( |
| intent.getIntExtra(Intent.EXTRA_UID, -1)), |
| userId, ProcessList.INVALID_ADJ, |
| false, true, true, false, "change " + ssp); |
| } |
| cleanupDisabledPackageComponentsLocked(ssp, userId, killProcess, |
| intent.getStringArrayExtra( |
| Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST)); |
| } |
| } |
| break; |
| case Intent.ACTION_PACKAGES_SUSPENDED: |
| case Intent.ACTION_PACKAGES_UNSUSPENDED: |
| final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals( |
| intent.getAction()); |
| final String[] packageNames = intent.getStringArrayExtra( |
| Intent.EXTRA_CHANGED_PACKAGE_LIST); |
| final int userHandle = intent.getIntExtra( |
| Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); |
| |
| synchronized(ActivityManagerService.this) { |
| mActivityTaskManager.getRecentTasks().onPackagesSuspendedChanged( |
| packageNames, suspended, userHandle); |
| } |
| break; |
| } |
| break; |
| case Intent.ACTION_PACKAGE_REPLACED: |
| { |
| final Uri data = intent.getData(); |
| final String ssp; |
| if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { |
| ApplicationInfo aInfo = null; |
| try { |
| aInfo = AppGlobals.getPackageManager() |
| .getApplicationInfo(ssp, STOCK_PM_FLAGS, userId); |
| } catch (RemoteException ignore) {} |
| if (aInfo == null) { |
| Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:" |
| + " ssp=" + ssp + " data=" + data); |
| return ActivityManager.BROADCAST_SUCCESS; |
| } |
| mStackSupervisor.updateActivityApplicationInfoLocked(aInfo); |
| mServices.updateServiceApplicationInfoLocked(aInfo); |
| sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED, |
| new String[] {ssp}, userId); |
| } |
| break; |
| } |
| case Intent.ACTION_PACKAGE_ADDED: |
| { |
| // Special case for adding a package: by default turn on compatibility mode. |
| Uri data = intent.getData(); |
| String ssp; |
| if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { |
| final boolean replacing = |
| intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); |
| mCompatModePackages.handlePackageAddedLocked(ssp, replacing); |
| |
| try { |
| ApplicationInfo ai = AppGlobals.getPackageManager(). |
| getApplicationInfo(ssp, STOCK_PM_FLAGS, 0); |
| mBatteryStatsService.notePackageInstalled(ssp, |
| ai != null ? ai.versionCode : 0); |
| } catch (RemoteException e) { |
| } |
| } |
| break; |
| } |
| case Intent.ACTION_PACKAGE_DATA_CLEARED: |
| { |
| Uri data = intent.getData(); |
| String ssp; |
| if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { |
| mCompatModePackages.handlePackageDataClearedLocked(ssp); |
| mAppWarnings.onPackageDataCleared(ssp); |
| } |
| break; |
| } |
| case Intent.ACTION_TIMEZONE_CHANGED: |
| // If this is the time zone changed action, queue up a message that will reset |
| // the timezone of all currently running processes. This message will get |
| // queued up before the broadcast happens. |
| mHandler.sendEmptyMessage(UPDATE_TIME_ZONE); |
| break; |
| case Intent.ACTION_TIME_CHANGED: |
| // EXTRA_TIME_PREF_24_HOUR_FORMAT is optional so we must distinguish between |
| // the tri-state value it may contain and "unknown". |
| // For convenience we re-use the Intent extra values. |
| final int NO_EXTRA_VALUE_FOUND = -1; |
| final int timeFormatPreferenceMsgValue = intent.getIntExtra( |
| Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, |
| NO_EXTRA_VALUE_FOUND /* defaultValue */); |
| // Only send a message if the time preference is available. |
| if (timeFormatPreferenceMsgValue != NO_EXTRA_VALUE_FOUND) { |
| Message updateTimePreferenceMsg = |
| mHandler.obtainMessage(UPDATE_TIME_PREFERENCE_MSG, |
| timeFormatPreferenceMsgValue, 0); |
| mHandler.sendMessage(updateTimePreferenceMsg); |
| } |
| BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); |
| synchronized (stats) { |
| stats.noteCurrentTimeChangedLocked(); |
| } |
| break; |
| case Intent.ACTION_CLEAR_DNS_CACHE: |
| mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG); |
| break; |
| case Proxy.PROXY_CHANGE_ACTION: |
| ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO); |
| mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy)); |
| break; |
| case android.hardware.Camera.ACTION_NEW_PICTURE: |
| case android.hardware.Camera.ACTION_NEW_VIDEO: |
| // In N we just turned these off; in O we are turing them back on partly, |
| // only for registered receivers. This will still address the main problem |
| // (a spam of apps waking up when a picture is taken putting significant |
| // memory pressure on the system at a bad point), while still allowing apps |
| // that are already actively running to know about this happening. |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); |
| break; |
| case android.security.KeyChain.ACTION_TRUST_STORE_CHANGED: |
| mHandler.sendEmptyMessage(HANDLE_TRUST_STORAGE_UPDATE_MSG); |
| break; |
| case "com.android.launcher.action.INSTALL_SHORTCUT": |
| // As of O, we no longer support this broadcasts, even for pre-O apps. |
| // Apps should now be using ShortcutManager.pinRequestShortcut(). |
| Log.w(TAG, "Broadcast " + action |
| + " no longer supported. It will not be delivered."); |
| return ActivityManager.BROADCAST_SUCCESS; |
| } |
| |
| if (Intent.ACTION_PACKAGE_ADDED.equals(action) || |
| Intent.ACTION_PACKAGE_REMOVED.equals(action) || |
| Intent.ACTION_PACKAGE_REPLACED.equals(action)) { |
| final int uid = getUidFromIntent(intent); |
| if (uid != -1) { |
| final UidRecord uidRec = mActiveUids.get(uid); |
| if (uidRec != null) { |
| uidRec.updateHasInternetPermission(); |
| } |
| } |
| } |
| } |
| |
| // Add to the sticky list if requested. |
| if (sticky) { |
| if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, |
| callingPid, callingUid) |
| != PackageManager.PERMISSION_GRANTED) { |
| String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid=" |
| + callingPid + ", uid=" + callingUid |
| + " requires " + android.Manifest.permission.BROADCAST_STICKY; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| if (requiredPermissions != null && requiredPermissions.length > 0) { |
| Slog.w(TAG, "Can't broadcast sticky intent " + intent |
| + " and enforce permissions " + Arrays.toString(requiredPermissions)); |
| return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION; |
| } |
| if (intent.getComponent() != null) { |
| throw new SecurityException( |
| "Sticky broadcasts can't target a specific component"); |
| } |
| // We use userId directly here, since the "all" target is maintained |
| // as a separate set of sticky broadcasts. |
| if (userId != UserHandle.USER_ALL) { |
| // But first, if this is not a broadcast to all users, then |
| // make sure it doesn't conflict with an existing broadcast to |
| // all users. |
| ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get( |
| UserHandle.USER_ALL); |
| if (stickies != null) { |
| ArrayList<Intent> list = stickies.get(intent.getAction()); |
| if (list != null) { |
| int N = list.size(); |
| int i; |
| for (i=0; i<N; i++) { |
| if (intent.filterEquals(list.get(i))) { |
| throw new IllegalArgumentException( |
| "Sticky broadcast " + intent + " for user " |
| + userId + " conflicts with existing global broadcast"); |
| } |
| } |
| } |
| } |
| } |
| ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId); |
| if (stickies == null) { |
| stickies = new ArrayMap<>(); |
| mStickyBroadcasts.put(userId, stickies); |
| } |
| ArrayList<Intent> list = stickies.get(intent.getAction()); |
| if (list == null) { |
| list = new ArrayList<>(); |
| stickies.put(intent.getAction(), list); |
| } |
| final int stickiesCount = list.size(); |
| int i; |
| for (i = 0; i < stickiesCount; i++) { |
| if (intent.filterEquals(list.get(i))) { |
| // This sticky already exists, replace it. |
| list.set(i, new Intent(intent)); |
| break; |
| } |
| } |
| if (i >= stickiesCount) { |
| list.add(new Intent(intent)); |
| } |
| } |
| |
| int[] users; |
| if (userId == UserHandle.USER_ALL) { |
| // Caller wants broadcast to go to all started users. |
| users = mUserController.getStartedUserArray(); |
| } else { |
| // Caller wants broadcast to go to one specific user. |
| users = new int[] {userId}; |
| } |
| |
| // Figure out who all will receive this broadcast. |
| List receivers = null; |
| List<BroadcastFilter> registeredReceivers = null; |
| // Need to resolve the intent to interested receivers... |
| if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) |
| == 0) { |
| receivers = collectReceiverComponents(intent, resolvedType, callingUid, users); |
| } |
| if (intent.getComponent() == null) { |
| if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) { |
| // Query one target user at a time, excluding shell-restricted users |
| for (int i = 0; i < users.length; i++) { |
| if (mUserController.hasUserRestriction( |
| UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) { |
| continue; |
| } |
| List<BroadcastFilter> registeredReceiversForUser = |
| mReceiverResolver.queryIntent(intent, |
| resolvedType, false /*defaultOnly*/, users[i]); |
| if (registeredReceivers == null) { |
| registeredReceivers = registeredReceiversForUser; |
| } else if (registeredReceiversForUser != null) { |
| registeredReceivers.addAll(registeredReceiversForUser); |
| } |
| } |
| } else { |
| registeredReceivers = mReceiverResolver.queryIntent(intent, |
| resolvedType, false /*defaultOnly*/, userId); |
| } |
| } |
| |
| final boolean replacePending = |
| (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0; |
| |
| if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction() |
| + " replacePending=" + replacePending); |
| |
| int NR = registeredReceivers != null ? registeredReceivers.size() : 0; |
| if (!ordered && NR > 0) { |
| // If we are not serializing this broadcast, then send the |
| // registered receivers separately so they don't wait for the |
| // components to be launched. |
| if (isCallerSystem) { |
| checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid, |
| isProtectedBroadcast, registeredReceivers); |
| } |
| final BroadcastQueue queue = broadcastQueueForIntent(intent); |
| BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, |
| callerPackage, callingPid, callingUid, callerInstantApp, resolvedType, |
| requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, |
| resultCode, resultData, resultExtras, ordered, sticky, false, userId); |
| if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); |
| final boolean replaced = replacePending |
| && (queue.replaceParallelBroadcastLocked(r) != null); |
| // Note: We assume resultTo is null for non-ordered broadcasts. |
| if (!replaced) { |
| queue.enqueueParallelBroadcastLocked(r); |
| queue.scheduleBroadcastsLocked(); |
| } |
| registeredReceivers = null; |
| NR = 0; |
| } |
| |
| // Merge into one list. |
| int ir = 0; |
| if (receivers != null) { |
| // A special case for PACKAGE_ADDED: do not allow the package |
| // being added to see this broadcast. This prevents them from |
| // using this as a back door to get run as soon as they are |
| // installed. Maybe in the future we want to have a special install |
| // broadcast or such for apps, but we'd like to deliberately make |
| // this decision. |
| String skipPackages[] = null; |
| if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction()) |
| || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction()) |
| || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) { |
| Uri data = intent.getData(); |
| if (data != null) { |
| String pkgName = data.getSchemeSpecificPart(); |
| if (pkgName != null) { |
| skipPackages = new String[] { pkgName }; |
| } |
| } |
| } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) { |
| skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); |
| } |
| if (skipPackages != null && (skipPackages.length > 0)) { |
| for (String skipPackage : skipPackages) { |
| if (skipPackage != null) { |
| int NT = receivers.size(); |
| for (int it=0; it<NT; it++) { |
| ResolveInfo curt = (ResolveInfo)receivers.get(it); |
| if (curt.activityInfo.packageName.equals(skipPackage)) { |
| receivers.remove(it); |
| it--; |
| NT--; |
| } |
| } |
| } |
| } |
| } |
| |
| int NT = receivers != null ? receivers.size() : 0; |
| int it = 0; |
| ResolveInfo curt = null; |
| BroadcastFilter curr = null; |
| while (it < NT && ir < NR) { |
| if (curt == null) { |
| curt = (ResolveInfo)receivers.get(it); |
| } |
| if (curr == null) { |
| curr = registeredReceivers.get(ir); |
| } |
| if (curr.getPriority() >= curt.priority) { |
| // Insert this broadcast record into the final list. |
| receivers.add(it, curr); |
| ir++; |
| curr = null; |
| it++; |
| NT++; |
| } else { |
| // Skip to the next ResolveInfo in the final list. |
| it++; |
| curt = null; |
| } |
| } |
| } |
| while (ir < NR) { |
| if (receivers == null) { |
| receivers = new ArrayList(); |
| } |
| receivers.add(registeredReceivers.get(ir)); |
| ir++; |
| } |
| |
| if (isCallerSystem) { |
| checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid, |
| isProtectedBroadcast, receivers); |
| } |
| |
| if ((receivers != null && receivers.size() > 0) |
| || resultTo != null) { |
| BroadcastQueue queue = broadcastQueueForIntent(intent); |
| BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, |
| callerPackage, callingPid, callingUid, callerInstantApp, resolvedType, |
| requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, |
| resultData, resultExtras, ordered, sticky, false, userId); |
| |
| if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r |
| + ": prev had " + queue.mOrderedBroadcasts.size()); |
| if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST, |
| "Enqueueing broadcast " + r.intent.getAction()); |
| |
| final BroadcastRecord oldRecord = |
| replacePending ? queue.replaceOrderedBroadcastLocked(r) : null; |
| if (oldRecord != null) { |
| // Replaced, fire the result-to receiver. |
| if (oldRecord.resultTo != null) { |
| final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent); |
| try { |
| oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo, |
| oldRecord.intent, |
| Activity.RESULT_CANCELED, null, null, |
| false, false, oldRecord.userId); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "Failure [" |
| + queue.mQueueName + "] sending broadcast result of " |
| + intent, e); |
| |
| } |
| } |
| } else { |
| queue.enqueueOrderedBroadcastLocked(r); |
| queue.scheduleBroadcastsLocked(); |
| } |
| } else { |
| // There was nobody interested in the broadcast, but we still want to record |
| // that it happened. |
| if (intent.getComponent() == null && intent.getPackage() == null |
| && (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { |
| // This was an implicit broadcast... let's record it for posterity. |
| addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0); |
| } |
| } |
| |
| return ActivityManager.BROADCAST_SUCCESS; |
| } |
| |
| /** |
| * @return uid from the extra field {@link Intent#EXTRA_UID} if present, Otherwise -1 |
| */ |
| private int getUidFromIntent(Intent intent) { |
| if (intent == null) { |
| return -1; |
| } |
| final Bundle intentExtras = intent.getExtras(); |
| return intent.hasExtra(Intent.EXTRA_UID) |
| ? intentExtras.getInt(Intent.EXTRA_UID) : -1; |
| } |
| |
| final void rotateBroadcastStatsIfNeededLocked() { |
| final long now = SystemClock.elapsedRealtime(); |
| if (mCurBroadcastStats == null || |
| (mCurBroadcastStats.mStartRealtime +(24*60*60*1000) < now)) { |
| mLastBroadcastStats = mCurBroadcastStats; |
| if (mLastBroadcastStats != null) { |
| mLastBroadcastStats.mEndRealtime = SystemClock.elapsedRealtime(); |
| mLastBroadcastStats.mEndUptime = SystemClock.uptimeMillis(); |
| } |
| mCurBroadcastStats = new BroadcastStats(); |
| } |
| } |
| |
| final void addBroadcastStatLocked(String action, String srcPackage, int receiveCount, |
| int skipCount, long dispatchTime) { |
| rotateBroadcastStatsIfNeededLocked(); |
| mCurBroadcastStats.addBroadcast(action, srcPackage, receiveCount, skipCount, dispatchTime); |
| } |
| |
| final void addBackgroundCheckViolationLocked(String action, String targetPackage) { |
| rotateBroadcastStatsIfNeededLocked(); |
| mCurBroadcastStats.addBackgroundCheckViolation(action, targetPackage); |
| } |
| |
| final Intent verifyBroadcastLocked(Intent intent) { |
| // Refuse possible leaked file descriptors |
| if (intent != null && intent.hasFileDescriptors() == true) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| int flags = intent.getFlags(); |
| |
| if (!mProcessesReady) { |
| // if the caller really truly claims to know what they're doing, go |
| // ahead and allow the broadcast without launching any receivers |
| if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) { |
| // This will be turned into a FLAG_RECEIVER_REGISTERED_ONLY later on if needed. |
| } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { |
| Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent |
| + " before boot completion"); |
| throw new IllegalStateException("Cannot broadcast before boot completed"); |
| } |
| } |
| |
| if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) { |
| throw new IllegalArgumentException( |
| "Can't use FLAG_RECEIVER_BOOT_UPGRADE here"); |
| } |
| |
| if ((flags & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) { |
| switch (Binder.getCallingUid()) { |
| case ROOT_UID: |
| case SHELL_UID: |
| break; |
| default: |
| Slog.w(TAG, "Removing FLAG_RECEIVER_FROM_SHELL because caller is UID " |
| + Binder.getCallingUid()); |
| intent.removeFlags(Intent.FLAG_RECEIVER_FROM_SHELL); |
| break; |
| } |
| } |
| |
| return intent; |
| } |
| |
| public final int broadcastIntent(IApplicationThread caller, |
| Intent intent, String resolvedType, IIntentReceiver resultTo, |
| int resultCode, String resultData, Bundle resultExtras, |
| String[] requiredPermissions, int appOp, Bundle bOptions, |
| boolean serialized, boolean sticky, int userId) { |
| enforceNotIsolatedCaller("broadcastIntent"); |
| synchronized(this) { |
| intent = verifyBroadcastLocked(intent); |
| |
| final ProcessRecord callerApp = getRecordForAppLocked(caller); |
| final int callingPid = Binder.getCallingPid(); |
| final int callingUid = Binder.getCallingUid(); |
| final long origId = Binder.clearCallingIdentity(); |
| int res = broadcastIntentLocked(callerApp, |
| callerApp != null ? callerApp.info.packageName : null, |
| intent, resolvedType, resultTo, resultCode, resultData, resultExtras, |
| requiredPermissions, appOp, bOptions, serialized, sticky, |
| callingPid, callingUid, userId); |
| Binder.restoreCallingIdentity(origId); |
| return res; |
| } |
| } |
| |
| |
| int broadcastIntentInPackage(String packageName, int uid, |
| Intent intent, String resolvedType, IIntentReceiver resultTo, |
| int resultCode, String resultData, Bundle resultExtras, |
| String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky, |
| int userId) { |
| synchronized(this) { |
| intent = verifyBroadcastLocked(intent); |
| |
| final long origId = Binder.clearCallingIdentity(); |
| String[] requiredPermissions = requiredPermission == null ? null |
| : new String[] {requiredPermission}; |
| int res = broadcastIntentLocked(null, packageName, intent, resolvedType, |
| resultTo, resultCode, resultData, resultExtras, |
| requiredPermissions, OP_NONE, bOptions, serialized, |
| sticky, -1, uid, userId); |
| Binder.restoreCallingIdentity(origId); |
| return res; |
| } |
| } |
| |
| public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) { |
| // Refuse possible leaked file descriptors |
| if (intent != null && intent.hasFileDescriptors() == true) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), |
| userId, true, ALLOW_NON_FULL, "removeStickyBroadcast", null); |
| |
| synchronized(this) { |
| if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY) |
| != PackageManager.PERMISSION_GRANTED) { |
| String msg = "Permission Denial: unbroadcastIntent() from pid=" |
| + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingUid() |
| + " requires " + android.Manifest.permission.BROADCAST_STICKY; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId); |
| if (stickies != null) { |
| ArrayList<Intent> list = stickies.get(intent.getAction()); |
| if (list != null) { |
| int N = list.size(); |
| int i; |
| for (i=0; i<N; i++) { |
| if (intent.filterEquals(list.get(i))) { |
| list.remove(i); |
| break; |
| } |
| } |
| if (list.size() <= 0) { |
| stickies.remove(intent.getAction()); |
| } |
| } |
| if (stickies.size() <= 0) { |
| mStickyBroadcasts.remove(userId); |
| } |
| } |
| } |
| } |
| |
| void backgroundServicesFinishedLocked(int userId) { |
| for (BroadcastQueue queue : mBroadcastQueues) { |
| queue.backgroundServicesFinishedLocked(userId); |
| } |
| } |
| |
| public void finishReceiver(IBinder who, int resultCode, String resultData, |
| Bundle resultExtras, boolean resultAbort, int flags) { |
| if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + who); |
| |
| // Refuse possible leaked file descriptors |
| if (resultExtras != null && resultExtras.hasFileDescriptors()) { |
| throw new IllegalArgumentException("File descriptors passed in Bundle"); |
| } |
| |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| boolean doNext = false; |
| BroadcastRecord r; |
| |
| synchronized(this) { |
| BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0 |
| ? mFgBroadcastQueue : mBgBroadcastQueue; |
| r = queue.getMatchingOrderedReceiver(who); |
| if (r != null) { |
| doNext = r.queue.finishReceiverLocked(r, resultCode, |
| resultData, resultExtras, resultAbort, true); |
| } |
| if (doNext) { |
| r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true); |
| } |
| // updateOomAdjLocked() will be done here |
| trimApplicationsLocked(); |
| } |
| |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| // ========================================================= |
| // INSTRUMENTATION |
| // ========================================================= |
| |
| public boolean startInstrumentation(ComponentName className, |
| String profileFile, int flags, Bundle arguments, |
| IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection, |
| int userId, String abiOverride) { |
| enforceNotIsolatedCaller("startInstrumentation"); |
| userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), |
| userId, false, ALLOW_FULL_ONLY, "startInstrumentation", null); |
| // Refuse possible leaked file descriptors |
| if (arguments != null && arguments.hasFileDescriptors()) { |
| throw new IllegalArgumentException("File descriptors passed in Bundle"); |
| } |
| |
| synchronized(this) { |
| InstrumentationInfo ii = null; |
| ApplicationInfo ai = null; |
| try { |
| ii = mContext.getPackageManager().getInstrumentationInfo( |
| className, STOCK_PM_FLAGS); |
| ai = AppGlobals.getPackageManager().getApplicationInfo( |
| ii.targetPackage, STOCK_PM_FLAGS, userId); |
| } catch (PackageManager.NameNotFoundException e) { |
| } catch (RemoteException e) { |
| } |
| if (ii == null) { |
| reportStartInstrumentationFailureLocked(watcher, className, |
| "Unable to find instrumentation info for: " + className); |
| return false; |
| } |
| if (ai == null) { |
| reportStartInstrumentationFailureLocked(watcher, className, |
| "Unable to find instrumentation target package: " + ii.targetPackage); |
| return false; |
| } |
| if (!ai.hasCode()) { |
| reportStartInstrumentationFailureLocked(watcher, className, |
| "Instrumentation target has no code: " + ii.targetPackage); |
| return false; |
| } |
| |
| int match = mContext.getPackageManager().checkSignatures( |
| ii.targetPackage, ii.packageName); |
| if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) { |
| String msg = "Permission Denial: starting instrumentation " |
| + className + " from pid=" |
| + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingPid() |
| + " not allowed because package " + ii.packageName |
| + " does not have a signature matching the target " |
| + ii.targetPackage; |
| reportStartInstrumentationFailureLocked(watcher, className, msg); |
| throw new SecurityException(msg); |
| } |
| |
| ActiveInstrumentation activeInstr = new ActiveInstrumentation(this); |
| activeInstr.mClass = className; |
| String defProcess = ai.processName;; |
| if (ii.targetProcesses == null) { |
| activeInstr.mTargetProcesses = new String[]{ai.processName}; |
| } else if (ii.targetProcesses.equals("*")) { |
| activeInstr.mTargetProcesses = new String[0]; |
| } else { |
| activeInstr.mTargetProcesses = ii.targetProcesses.split(","); |
| defProcess = activeInstr.mTargetProcesses[0]; |
| } |
| activeInstr.mTargetInfo = ai; |
| activeInstr.mProfileFile = profileFile; |
| activeInstr.mArguments = arguments; |
| activeInstr.mWatcher = watcher; |
| activeInstr.mUiAutomationConnection = uiAutomationConnection; |
| activeInstr.mResultClass = className; |
| |
| boolean disableHiddenApiChecks = |
| (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0; |
| if (disableHiddenApiChecks) { |
| enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS, |
| "disable hidden API checks"); |
| } |
| |
| final long origId = Binder.clearCallingIdentity(); |
| // Instrumentation can kill and relaunch even persistent processes |
| forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId, |
| "start instr"); |
| // Inform usage stats to make the target package active |
| if (mUsageStatsService != null) { |
| mUsageStatsService.reportEvent(ii.targetPackage, userId, |
| UsageEvents.Event.SYSTEM_INTERACTION); |
| } |
| |
| ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, |
| abiOverride); |
| app.instr = activeInstr; |
| activeInstr.mFinished = false; |
| activeInstr.mRunningProcesses.add(app); |
| if (!mActiveInstrumentation.contains(activeInstr)) { |
| mActiveInstrumentation.add(activeInstr); |
| } |
| Binder.restoreCallingIdentity(origId); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Report errors that occur while attempting to start Instrumentation. Always writes the |
| * error to the logs, but if somebody is watching, send the report there too. This enables |
| * the "am" command to report errors with more information. |
| * |
| * @param watcher The IInstrumentationWatcher. Null if there isn't one. |
| * @param cn The component name of the instrumentation. |
| * @param report The error report. |
| */ |
| private void reportStartInstrumentationFailureLocked(IInstrumentationWatcher watcher, |
| ComponentName cn, String report) { |
| Slog.w(TAG, report); |
| if (watcher != null) { |
| Bundle results = new Bundle(); |
| results.putString(Instrumentation.REPORT_KEY_IDENTIFIER, "ActivityManagerService"); |
| results.putString("Error", report); |
| mInstrumentationReporter.reportStatus(watcher, cn, -1, results); |
| } |
| } |
| |
| void addInstrumentationResultsLocked(ProcessRecord app, Bundle results) { |
| if (app.instr == null) { |
| Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app); |
| return; |
| } |
| |
| if (!app.instr.mFinished && results != null) { |
| if (app.instr.mCurResults == null) { |
| app.instr.mCurResults = new Bundle(results); |
| } else { |
| app.instr.mCurResults.putAll(results); |
| } |
| } |
| } |
| |
| public void addInstrumentationResults(IApplicationThread target, Bundle results) { |
| int userId = UserHandle.getCallingUserId(); |
| // Refuse possible leaked file descriptors |
| if (results != null && results.hasFileDescriptors()) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| synchronized(this) { |
| ProcessRecord app = getRecordForAppLocked(target); |
| if (app == null) { |
| Slog.w(TAG, "addInstrumentationResults: no app for " + target); |
| return; |
| } |
| final long origId = Binder.clearCallingIdentity(); |
| addInstrumentationResultsLocked(app, results); |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| @GuardedBy("this") |
| void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) { |
| if (app.instr == null) { |
| Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app); |
| return; |
| } |
| |
| if (!app.instr.mFinished) { |
| if (app.instr.mWatcher != null) { |
| Bundle finalResults = app.instr.mCurResults; |
| if (finalResults != null) { |
| if (app.instr.mCurResults != null && results != null) { |
| finalResults.putAll(results); |
| } |
| } else { |
| finalResults = results; |
| } |
| mInstrumentationReporter.reportFinished(app.instr.mWatcher, |
| app.instr.mClass, resultCode, finalResults); |
| } |
| |
| // Can't call out of the system process with a lock held, so post a message. |
| if (app.instr.mUiAutomationConnection != null) { |
| mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG, |
| app.instr.mUiAutomationConnection).sendToTarget(); |
| } |
| app.instr.mFinished = true; |
| } |
| |
| app.instr.removeProcess(app); |
| app.instr = null; |
| |
| forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false, app.userId, |
| "finished inst"); |
| } |
| |
| public void finishInstrumentation(IApplicationThread target, |
| int resultCode, Bundle results) { |
| int userId = UserHandle.getCallingUserId(); |
| // Refuse possible leaked file descriptors |
| if (results != null && results.hasFileDescriptors()) { |
| throw new IllegalArgumentException("File descriptors passed in Intent"); |
| } |
| |
| synchronized(this) { |
| ProcessRecord app = getRecordForAppLocked(target); |
| if (app == null) { |
| Slog.w(TAG, "finishInstrumentation: no app for " + target); |
| return; |
| } |
| final long origId = Binder.clearCallingIdentity(); |
| finishInstrumentationLocked(app, resultCode, results); |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| // ========================================================= |
| // CONFIGURATION |
| // ========================================================= |
| |
| public ConfigurationInfo getDeviceConfigurationInfo() { |
| ConfigurationInfo config = new ConfigurationInfo(); |
| synchronized (this) { |
| final Configuration globalConfig = getGlobalConfiguration(); |
| config.reqTouchScreen = globalConfig.touchscreen; |
| config.reqKeyboardType = globalConfig.keyboard; |
| config.reqNavigation = globalConfig.navigation; |
| if (globalConfig.navigation == Configuration.NAVIGATION_DPAD |
| || globalConfig.navigation == Configuration.NAVIGATION_TRACKBALL) { |
| config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; |
| } |
| if (globalConfig.keyboard != Configuration.KEYBOARD_UNDEFINED |
| && globalConfig.keyboard != Configuration.KEYBOARD_NOKEYS) { |
| config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; |
| } |
| config.reqGlEsVersion = GL_ES_VERSION; |
| } |
| return config; |
| } |
| |
| ActivityStack getFocusedStack() { |
| return mStackSupervisor.getFocusedStack(); |
| } |
| |
| @Override |
| public StackInfo getFocusedStackInfo() throws RemoteException { |
| return mActivityTaskManager.getFocusedStackInfo(); |
| } |
| |
| public Configuration getConfiguration() { |
| Configuration ci; |
| synchronized(this) { |
| ci = new Configuration(getGlobalConfiguration()); |
| ci.userSetLocale = false; |
| } |
| return ci; |
| } |
| |
| @Override |
| public void suppressResizeConfigChanges(boolean suppress) throws RemoteException { |
| mActivityTaskManager.suppressResizeConfigChanges(suppress); |
| } |
| |
| @Override |
| public void updatePersistentConfiguration(Configuration values) { |
| enforceCallingPermission(CHANGE_CONFIGURATION, "updatePersistentConfiguration()"); |
| enforceWriteSettingsPermission("updatePersistentConfiguration()"); |
| if (values == null) { |
| throw new NullPointerException("Configuration must not be null"); |
| } |
| |
| int userId = UserHandle.getCallingUserId(); |
| |
| synchronized(this) { |
| updatePersistentConfigurationLocked(values, userId); |
| } |
| } |
| |
| private void updatePersistentConfigurationLocked(Configuration values, @UserIdInt int userId) { |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| updateConfigurationLocked(values, null, false, true, userId, false /* deferResume */); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| |
| private void updateFontScaleIfNeeded(@UserIdInt int userId) { |
| final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(), |
| FONT_SCALE, 1.0f, userId); |
| |
| synchronized (this) { |
| if (getGlobalConfiguration().fontScale == scaleFactor) { |
| return; |
| } |
| |
| final Configuration configuration |
| = mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY); |
| configuration.fontScale = scaleFactor; |
| updatePersistentConfigurationLocked(configuration, userId); |
| } |
| } |
| |
| private void enforceWriteSettingsPermission(String func) { |
| int uid = Binder.getCallingUid(); |
| if (uid == ROOT_UID) { |
| return; |
| } |
| |
| if (Settings.checkAndNoteWriteSettingsOperation(mContext, uid, |
| Settings.getPackageNameForUid(mContext, uid), false)) { |
| return; |
| } |
| |
| String msg = "Permission Denial: " + func + " from pid=" |
| + Binder.getCallingPid() |
| + ", uid=" + uid |
| + " requires " + android.Manifest.permission.WRITE_SETTINGS; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| |
| @Override |
| public boolean updateConfiguration(Configuration values) { |
| return mActivityTaskManager.updateConfiguration(values); |
| } |
| |
| void updateUserConfigurationLocked() { |
| final Configuration configuration = new Configuration(getGlobalConfiguration()); |
| final int currentUserId = mUserController.getCurrentUserId(); |
| Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration, |
| currentUserId, Settings.System.canWrite(mContext)); |
| updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */, |
| false /* persistent */, currentUserId, false /* deferResume */); |
| } |
| |
| boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, |
| boolean initLocale) { |
| return updateConfigurationLocked(values, starting, initLocale, false /* deferResume */); |
| } |
| |
| boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, |
| boolean initLocale, boolean deferResume) { |
| // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user |
| return updateConfigurationLocked(values, starting, initLocale, false /* persistent */, |
| UserHandle.USER_NULL, deferResume); |
| } |
| |
| // To cache the list of supported system locales |
| private String[] mSupportedSystemLocales = null; |
| |
| private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, |
| boolean initLocale, boolean persistent, int userId, boolean deferResume) { |
| return updateConfigurationLocked(values, starting, initLocale, persistent, userId, |
| deferResume, null /* result */); |
| } |
| |
| /** |
| * Do either or both things: (1) change the current configuration, and (2) |
| * make sure the given activity is running with the (now) current |
| * configuration. Returns true if the activity has been left running, or |
| * false if <var>starting</var> is being destroyed to match the new |
| * configuration. |
| * |
| * @param userId is only used when persistent parameter is set to true to persist configuration |
| * for that particular user |
| */ |
| boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, |
| boolean initLocale, boolean persistent, int userId, boolean deferResume, |
| ActivityTaskManagerService.UpdateConfigurationResult result) { |
| int changes = 0; |
| boolean kept = true; |
| |
| if (mWindowManager != null) { |
| mWindowManager.deferSurfaceLayout(); |
| } |
| try { |
| if (values != null) { |
| changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId, |
| deferResume); |
| } |
| |
| kept = ensureConfigAndVisibilityAfterUpdate(starting, changes); |
| } finally { |
| if (mWindowManager != null) { |
| mWindowManager.continueSurfaceLayout(); |
| } |
| } |
| |
| if (result != null) { |
| result.changes = changes; |
| result.activityRelaunched = !kept; |
| } |
| return kept; |
| } |
| |
| /** |
| * Returns true if this configuration change is interesting enough to send an |
| * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast. |
| */ |
| private static boolean isSplitConfigurationChange(int configDiff) { |
| return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0; |
| } |
| |
| /** Update default (global) configuration and notify listeners about changes. */ |
| private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale, |
| boolean persistent, int userId, boolean deferResume) { |
| mActivityTaskManager.mTempConfig.setTo(getGlobalConfiguration()); |
| final int changes = mActivityTaskManager.mTempConfig.updateFrom(values); |
| if (changes == 0) { |
| // Since calling to Activity.setRequestedOrientation leads to freezing the window with |
| // setting WindowManagerService.mWaitingForConfig to true, it is important that we call |
| // performDisplayOverrideConfigUpdate in order to send the new display configuration |
| // (even if there are no actual changes) to unfreeze the window. |
| performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY); |
| return 0; |
| } |
| |
| if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION, |
| "Updating global configuration to: " + values); |
| |
| EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED, changes); |
| StatsLog.write(StatsLog.RESOURCE_CONFIGURATION_CHANGED, |
| values.colorMode, |
| values.densityDpi, |
| values.fontScale, |
| values.hardKeyboardHidden, |
| values.keyboard, |
| values.keyboardHidden, |
| values.mcc, |
| values.mnc, |
| values.navigation, |
| values.navigationHidden, |
| values.orientation, |
| values.screenHeightDp, |
| values.screenLayout, |
| values.screenWidthDp, |
| values.smallestScreenWidthDp, |
| values.touchscreen, |
| values.uiMode); |
| |
| |
| if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) { |
| final LocaleList locales = values.getLocales(); |
| int bestLocaleIndex = 0; |
| if (locales.size() > 1) { |
| if (mSupportedSystemLocales == null) { |
| mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales(); |
| } |
| bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales)); |
| } |
| SystemProperties.set("persist.sys.locale", |
| locales.get(bestLocaleIndex).toLanguageTag()); |
| LocaleList.setDefault(locales, bestLocaleIndex); |
| mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG, |
| locales.get(bestLocaleIndex))); |
| } |
| |
| mActivityTaskManager.mConfigurationSeq = Math.max(++mActivityTaskManager.mConfigurationSeq, 1); |
| mActivityTaskManager.mTempConfig.seq = mActivityTaskManager.mConfigurationSeq; |
| |
| // Update stored global config and notify everyone about the change. |
| mStackSupervisor.onConfigurationChanged(mActivityTaskManager.mTempConfig); |
| |
| Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mActivityTaskManager.mTempConfig); |
| // TODO(multi-display): Update UsageEvents#Event to include displayId. |
| mUsageStatsService.reportConfigurationChange(mActivityTaskManager.mTempConfig, |
| mUserController.getCurrentUserId()); |
| |
| // TODO: If our config changes, should we auto dismiss any currently showing dialogs? |
| updateShouldShowDialogsLocked(mActivityTaskManager.mTempConfig); |
| |
| AttributeCache ac = AttributeCache.instance(); |
| if (ac != null) { |
| ac.updateConfiguration(mActivityTaskManager.mTempConfig); |
| } |
| |
| // Make sure all resources in our process are updated right now, so that anyone who is going |
| // to retrieve resource values after we return will be sure to get the new ones. This is |
| // especially important during boot, where the first config change needs to guarantee all |
| // resources have that config before following boot code is executed. |
| mSystemThread.applyConfigurationToResources(mActivityTaskManager.mTempConfig); |
| |
| // We need another copy of global config because we're scheduling some calls instead of |
| // running them in place. We need to be sure that object we send will be handled unchanged. |
| final Configuration configCopy = new Configuration(mActivityTaskManager.mTempConfig); |
| if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) { |
| Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); |
| msg.obj = configCopy; |
| msg.arg1 = userId; |
| mHandler.sendMessage(msg); |
| } |
| |
| for (int i = mLruProcesses.size() - 1; i >= 0; i--) { |
| ProcessRecord app = mLruProcesses.get(i); |
| try { |
| if (app.thread != null) { |
| if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc " |
| + app.processName + " new config " + configCopy); |
| mActivityTaskManager.getLifecycleManager().scheduleTransaction(app.thread, |
| ConfigurationChangeItem.obtain(configCopy)); |
| } |
| } catch (Exception e) { |
| Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e); |
| } |
| } |
| |
| Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING |
| | Intent.FLAG_RECEIVER_FOREGROUND |
| | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); |
| broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, |
| OP_NONE, null, false, false, MY_PID, SYSTEM_UID, |
| UserHandle.USER_ALL); |
| if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { |
| intent = new Intent(Intent.ACTION_LOCALE_CHANGED); |
| intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND |
| | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND |
| | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); |
| if (initLocale || !mProcessesReady) { |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); |
| } |
| broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, |
| OP_NONE, null, false, false, MY_PID, SYSTEM_UID, |
| UserHandle.USER_ALL); |
| } |
| |
| // Send a broadcast to PackageInstallers if the configuration change is interesting |
| // for the purposes of installing additional splits. |
| if (!initLocale && isSplitConfigurationChange(changes)) { |
| intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED); |
| intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
| | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); |
| |
| // Typically only app stores will have this permission. |
| String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES }; |
| broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions, |
| OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); |
| } |
| |
| // Override configuration of the default display duplicates global config, so we need to |
| // update it also. This will also notify WindowManager about changes. |
| performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume, |
| DEFAULT_DISPLAY); |
| |
| return changes; |
| } |
| |
| boolean updateDisplayOverrideConfigurationLocked(Configuration values, ActivityRecord starting, |
| boolean deferResume, int displayId) { |
| return updateDisplayOverrideConfigurationLocked(values, starting, deferResume /* deferResume */, |
| displayId, null /* result */); |
| } |
| |
| /** |
| * Updates override configuration specific for the selected display. If no config is provided, |
| * new one will be computed in WM based on current display info. |
| */ |
| boolean updateDisplayOverrideConfigurationLocked(Configuration values, |
| ActivityRecord starting, boolean deferResume, int displayId, |
| ActivityTaskManagerService.UpdateConfigurationResult result) { |
| int changes = 0; |
| boolean kept = true; |
| |
| if (mWindowManager != null) { |
| mWindowManager.deferSurfaceLayout(); |
| } |
| try { |
| if (values != null) { |
| if (displayId == DEFAULT_DISPLAY) { |
| // Override configuration of the default display duplicates global config, so |
| // we're calling global config update instead for default display. It will also |
| // apply the correct override config. |
| changes = updateGlobalConfigurationLocked(values, false /* initLocale */, |
| false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume); |
| } else { |
| changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId); |
| } |
| } |
| |
| kept = ensureConfigAndVisibilityAfterUpdate(starting, changes); |
| } finally { |
| if (mWindowManager != null) { |
| mWindowManager.continueSurfaceLayout(); |
| } |
| } |
| |
| if (result != null) { |
| result.changes = changes; |
| result.activityRelaunched = !kept; |
| } |
| return kept; |
| } |
| |
| private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume, |
| int displayId) { |
| mActivityTaskManager.mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId)); |
| final int changes = mActivityTaskManager.mTempConfig.updateFrom(values); |
| if (changes != 0) { |
| Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " " |
| + mActivityTaskManager.mTempConfig + " for displayId=" + displayId); |
| mStackSupervisor.setDisplayOverrideConfiguration(mActivityTaskManager.mTempConfig, displayId); |
| |
| final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0; |
| if (isDensityChange && displayId == DEFAULT_DISPLAY) { |
| mAppWarnings.onDensityChanged(); |
| |
| killAllBackgroundProcessesExcept(N, |
| ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE); |
| } |
| } |
| |
| // Update the configuration with WM first and check if any of the stacks need to be resized |
| // due to the configuration change. If so, resize the stacks now and do any relaunches if |
| // necessary. This way we don't need to relaunch again afterwards in |
| // ensureActivityConfiguration(). |
| if (mWindowManager != null) { |
| final int[] resizedStacks = |
| mWindowManager.setNewDisplayOverrideConfiguration(mActivityTaskManager.mTempConfig, displayId); |
| if (resizedStacks != null) { |
| for (int stackId : resizedStacks) { |
| resizeStackWithBoundsFromWindowManager(stackId, deferResume); |
| } |
| } |
| } |
| |
| return changes; |
| } |
| |
| /** Applies latest configuration and/or visibility updates if needed. */ |
| private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) { |
| boolean kept = true; |
| final ActivityStack mainStack = mStackSupervisor.getFocusedStack(); |
| // mainStack is null during startup. |
| if (mainStack != null) { |
| if (changes != 0 && starting == null) { |
| // If the configuration changed, and the caller is not already |
| // in the process of starting an activity, then find the top |
| // activity to check if its configuration needs to change. |
| starting = mainStack.topRunningActivityLocked(); |
| } |
| |
| if (starting != null) { |
| kept = starting.ensureActivityConfiguration(changes, |
| false /* preserveWindow */); |
| // And we need to make sure at this point that all other activities |
| // are made visible with the correct configuration. |
| mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes, |
| !PRESERVE_WINDOWS); |
| } |
| } |
| |
| return kept; |
| } |
| |
| /** Helper method that requests bounds from WM and applies them to stack. */ |
| private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) { |
| final Rect newStackBounds = new Rect(); |
| final ActivityStack stack = mStackSupervisor.getStack(stackId); |
| |
| // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found. |
| if (stack == null) { |
| final StringWriter writer = new StringWriter(); |
| final PrintWriter printWriter = new PrintWriter(writer); |
| mStackSupervisor.dumpDisplays(printWriter); |
| printWriter.flush(); |
| |
| Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer); |
| } |
| |
| stack.getBoundsForNewConfiguration(newStackBounds); |
| mStackSupervisor.resizeStackLocked( |
| stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */, |
| null /* tempTaskBounds */, null /* tempTaskInsetBounds */, |
| false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume); |
| } |
| |
| /** |
| * Decide based on the configuration whether we should show the ANR, |
| * crash, etc dialogs. The idea is that if there is no affordance to |
| * press the on-screen buttons, or the user experience would be more |
| * greatly impacted than the crash itself, we shouldn't show the dialog. |
| * |
| * A thought: SystemUI might also want to get told about this, the Power |
| * dialog / global actions also might want different behaviors. |
| */ |
| private void updateShouldShowDialogsLocked(Configuration config) { |
| final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS |
| && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH |
| && config.navigation == Configuration.NAVIGATION_NONAV); |
| int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK; |
| final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR |
| && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER) |
| && modeType != Configuration.UI_MODE_TYPE_TELEVISION |
| && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET); |
| final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(), |
| HIDE_ERROR_DIALOGS, 0) != 0; |
| mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet; |
| } |
| |
| @Override |
| public int getLaunchedFromUid(IBinder activityToken) { |
| return mActivityTaskManager.getLaunchedFromUid(activityToken); |
| } |
| |
| public String getLaunchedFromPackage(IBinder activityToken) { |
| return mActivityTaskManager.getLaunchedFromPackage(activityToken); |
| } |
| |
| // ========================================================= |
| // LIFETIME MANAGEMENT |
| // ========================================================= |
| |
| // Returns whether the app is receiving broadcast. |
| // If receiving, fetch all broadcast queues which the app is |
| // the current [or imminent] receiver on. |
| private boolean isReceivingBroadcastLocked(ProcessRecord app, |
| ArraySet<BroadcastQueue> receivingQueues) { |
| final int N = app.curReceivers.size(); |
| if (N > 0) { |
| for (int i = 0; i < N; i++) { |
| receivingQueues.add(app.curReceivers.valueAt(i).queue); |
| } |
| return true; |
| } |
| |
| // It's not the current receiver, but it might be starting up to become one |
| for (BroadcastQueue queue : mBroadcastQueues) { |
| final BroadcastRecord r = queue.mPendingBroadcast; |
| if (r != null && r.curApp == app) { |
| // found it; report which queue it's in |
| receivingQueues.add(queue); |
| } |
| } |
| |
| return !receivingQueues.isEmpty(); |
| } |
| |
| Association startAssociationLocked(int sourceUid, String sourceProcess, int sourceState, |
| int targetUid, ComponentName targetComponent, String targetProcess) { |
| if (!mTrackingAssociations) { |
| return null; |
| } |
| ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> components |
| = mAssociations.get(targetUid); |
| if (components == null) { |
| components = new ArrayMap<>(); |
| mAssociations.put(targetUid, components); |
| } |
| SparseArray<ArrayMap<String, Association>> sourceUids = components.get(targetComponent); |
| if (sourceUids == null) { |
| sourceUids = new SparseArray<>(); |
| components.put(targetComponent, sourceUids); |
| } |
| ArrayMap<String, Association> sourceProcesses = sourceUids.get(sourceUid); |
| if (sourceProcesses == null) { |
| sourceProcesses = new ArrayMap<>(); |
| sourceUids.put(sourceUid, sourceProcesses); |
| } |
| Association ass = sourceProcesses.get(sourceProcess); |
| if (ass == null) { |
| ass = new Association(sourceUid, sourceProcess, targetUid, targetComponent, |
| targetProcess); |
| sourceProcesses.put(sourceProcess, ass); |
| } |
| ass.mCount++; |
| ass.mNesting++; |
| if (ass.mNesting == 1) { |
| ass.mStartTime = ass.mLastStateUptime = SystemClock.uptimeMillis(); |
| ass.mLastState = sourceState; |
| } |
| return ass; |
| } |
| |
| void stopAssociationLocked(int sourceUid, String sourceProcess, int targetUid, |
| ComponentName targetComponent) { |
| if (!mTrackingAssociations) { |
| return; |
| } |
| ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> components |
| = mAssociations.get(targetUid); |
| if (components == null) { |
| return; |
| } |
| SparseArray<ArrayMap<String, Association>> sourceUids = components.get(targetComponent); |
| if (sourceUids == null) { |
| return; |
| } |
| ArrayMap<String, Association> sourceProcesses = sourceUids.get(sourceUid); |
| if (sourceProcesses == null) { |
| return; |
| } |
| Association ass = sourceProcesses.get(sourceProcess); |
| if (ass == null || ass.mNesting <= 0) { |
| return; |
| } |
| ass.mNesting--; |
| if (ass.mNesting == 0) { |
| long uptime = SystemClock.uptimeMillis(); |
| ass.mTime += uptime - ass.mStartTime; |
| ass.mStateTimes[ass.mLastState-ActivityManager.MIN_PROCESS_STATE] |
| += uptime - ass.mLastStateUptime; |
| ass.mLastState = ActivityManager.MAX_PROCESS_STATE + 2; |
| } |
| } |
| |
| private void noteUidProcessState(final int uid, final int state) { |
| mBatteryStatsService.noteUidProcessState(uid, state); |
| mAppOpsService.updateUidProcState(uid, state); |
| if (mTrackingAssociations) { |
| for (int i1=0, N1=mAssociations.size(); i1<N1; i1++) { |
| ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> targetComponents |
| = mAssociations.valueAt(i1); |
| for (int i2=0, N2=targetComponents.size(); i2<N2; i2++) { |
| SparseArray<ArrayMap<String, Association>> sourceUids |
| = targetComponents.valueAt(i2); |
| ArrayMap<String, Association> sourceProcesses = sourceUids.get(uid); |
| if (sourceProcesses != null) { |
| for (int i4=0, N4=sourceProcesses.size(); i4<N4; i4++) { |
| Association ass = sourceProcesses.valueAt(i4); |
| if (ass.mNesting >= 1) { |
| // currently associated |
| long uptime = SystemClock.uptimeMillis(); |
| ass.mStateTimes[ass.mLastState-ActivityManager.MIN_PROCESS_STATE] |
| += uptime - ass.mLastStateUptime; |
| ass.mLastState = state; |
| ass.mLastStateUptime = uptime; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP, |
| boolean doingAll, long now) { |
| if (mAdjSeq == app.adjSeq) { |
| if (app.adjSeq == app.completedAdjSeq) { |
| // This adjustment has already been computed successfully. |
| return false; |
| } else { |
| // The process is being computed, so there is a cycle. We cannot |
| // rely on this process's state. |
| app.containsCycle = true; |
| return false; |
| } |
| } |
| |
| if (app.thread == null) { |
| app.adjSeq = mAdjSeq; |
| app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND; |
| app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; |
| app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ; |
| app.completedAdjSeq = app.adjSeq; |
| return false; |
| } |
| |
| app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; |
| app.adjSource = null; |
| app.adjTarget = null; |
| app.empty = false; |
| app.cached = false; |
| |
| final int activitiesSize = app.activities.size(); |
| final int appUid = app.info.uid; |
| final int logUid = mCurOomAdjUid; |
| |
| int prevAppAdj = app.curAdj; |
| |
| if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) { |
| // The max adjustment doesn't allow this app to be anything |
| // below foreground, so it is not worth doing work for it. |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making fixed: " + app); |
| } |
| app.adjType = "fixed"; |
| app.adjSeq = mAdjSeq; |
| app.curRawAdj = app.maxAdj; |
| app.foregroundActivities = false; |
| app.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT; |
| // System processes can do UI, and when they do we want to have |
| // them trim their memory after the user leaves the UI. To |
| // facilitate this, here we need to determine whether or not it |
| // is currently showing UI. |
| app.systemNoUi = true; |
| if (app == TOP_APP) { |
| app.systemNoUi = false; |
| app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP; |
| app.adjType = "pers-top-activity"; |
| } else if (app.hasTopUi) { |
| // sched group/proc state adjustment is below |
| app.systemNoUi = false; |
| app.adjType = "pers-top-ui"; |
| } else if (activitiesSize > 0) { |
| for (int j = 0; j < activitiesSize; j++) { |
| final ActivityRecord r = app.activities.get(j); |
| if (r.visible) { |
| app.systemNoUi = false; |
| } |
| } |
| } |
| if (!app.systemNoUi) { |
| if (mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE) { |
| // screen on, promote UI |
| app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI; |
| app.curSchedGroup = ProcessList.SCHED_GROUP_TOP_APP; |
| } else { |
| // screen off, restrict UI scheduling |
| app.curProcState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; |
| app.curSchedGroup = ProcessList.SCHED_GROUP_RESTRICTED; |
| } |
| } |
| app.curAdj = app.maxAdj; |
| app.completedAdjSeq = app.adjSeq; |
| // if curAdj is less than prevAppAdj, then this process was promoted |
| return app.curAdj < prevAppAdj; |
| } |
| |
| app.systemNoUi = false; |
| |
| final int PROCESS_STATE_CUR_TOP = mTopProcessState; |
| |
| // Determine the importance of the process, starting with most |
| // important to least, and assign an appropriate OOM adjustment. |
| int adj; |
| int schedGroup; |
| int procState; |
| int cachedAdjSeq; |
| |
| boolean foregroundActivities = false; |
| mTmpBroadcastQueue.clear(); |
| if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) { |
| // The last app on the list is the foreground app. |
| adj = ProcessList.FOREGROUND_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_TOP_APP; |
| app.adjType = "top-activity"; |
| foregroundActivities = true; |
| procState = PROCESS_STATE_CUR_TOP; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app); |
| } |
| } else if (app.runningRemoteAnimation) { |
| adj = ProcessList.VISIBLE_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_TOP_APP; |
| app.adjType = "running-remote-anim"; |
| procState = PROCESS_STATE_CUR_TOP; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app); |
| } |
| } else if (app.instr != null) { |
| // Don't want to kill running instrumentation. |
| adj = ProcessList.FOREGROUND_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| app.adjType = "instrumentation"; |
| procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app); |
| } |
| } else if (isReceivingBroadcastLocked(app, mTmpBroadcastQueue)) { |
| // An app that is currently receiving a broadcast also |
| // counts as being in the foreground for OOM killer purposes. |
| // It's placed in a sched group based on the nature of the |
| // broadcast as reflected by which queue it's active in. |
| adj = ProcessList.FOREGROUND_APP_ADJ; |
| schedGroup = (mTmpBroadcastQueue.contains(mFgBroadcastQueue)) |
| ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND; |
| app.adjType = "broadcast"; |
| procState = ActivityManager.PROCESS_STATE_RECEIVER; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app); |
| } |
| } else if (app.executingServices.size() > 0) { |
| // An app that is currently executing a service callback also |
| // counts as being in the foreground. |
| adj = ProcessList.FOREGROUND_APP_ADJ; |
| schedGroup = app.execServicesFg ? |
| ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND; |
| app.adjType = "exec-service"; |
| procState = ActivityManager.PROCESS_STATE_SERVICE; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making exec-service: " + app); |
| } |
| //Slog.i(TAG, "EXEC " + (app.execServicesFg ? "FG" : "BG") + ": " + app); |
| } else if (app == TOP_APP) { |
| adj = ProcessList.FOREGROUND_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; |
| app.adjType = "top-sleeping"; |
| foregroundActivities = true; |
| procState = PROCESS_STATE_CUR_TOP; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top (sleeping): " + app); |
| } |
| } else { |
| // As far as we know the process is empty. We may change our mind later. |
| schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; |
| // At this point we don't actually know the adjustment. Use the cached adj |
| // value that the caller wants us to. |
| adj = cachedAdj; |
| procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; |
| app.cached = true; |
| app.empty = true; |
| app.adjType = "cch-empty"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making empty: " + app); |
| } |
| } |
| |
| // Examine all activities if not already foreground. |
| if (!foregroundActivities && activitiesSize > 0) { |
| int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX; |
| for (int j = 0; j < activitiesSize; j++) { |
| final ActivityRecord r = app.activities.get(j); |
| if (r.app != app) { |
| Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app |
| + " instead of expected " + app); |
| if (r.app == null || (r.app.uid == app.uid)) { |
| // Only fix things up when they look sane |
| r.setProcess(app); |
| } else { |
| continue; |
| } |
| } |
| if (r.visible) { |
| // App has a visible activity; only upgrade adjustment. |
| if (adj > ProcessList.VISIBLE_APP_ADJ) { |
| adj = ProcessList.VISIBLE_APP_ADJ; |
| app.adjType = "vis-activity"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise adj to vis-activity: " + app); |
| } |
| } |
| if (procState > PROCESS_STATE_CUR_TOP) { |
| procState = PROCESS_STATE_CUR_TOP; |
| app.adjType = "vis-activity"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise procstate to vis-activity (top): " + app); |
| } |
| } |
| if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) { |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| } |
| app.cached = false; |
| app.empty = false; |
| foregroundActivities = true; |
| final TaskRecord task = r.getTask(); |
| if (task != null && minLayer > 0) { |
| final int layer = task.mLayerRank; |
| if (layer >= 0 && minLayer > layer) { |
| minLayer = layer; |
| } |
| } |
| break; |
| } else if (r.isState(ActivityState.PAUSING, ActivityState.PAUSED)) { |
| if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { |
| adj = ProcessList.PERCEPTIBLE_APP_ADJ; |
| app.adjType = "pause-activity"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise adj to pause-activity: " + app); |
| } |
| } |
| if (procState > PROCESS_STATE_CUR_TOP) { |
| procState = PROCESS_STATE_CUR_TOP; |
| app.adjType = "pause-activity"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise procstate to pause-activity (top): " + app); |
| } |
| } |
| if (schedGroup < ProcessList.SCHED_GROUP_DEFAULT) { |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| } |
| app.cached = false; |
| app.empty = false; |
| foregroundActivities = true; |
| } else if (r.isState(ActivityState.STOPPING)) { |
| if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { |
| adj = ProcessList.PERCEPTIBLE_APP_ADJ; |
| app.adjType = "stop-activity"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise adj to stop-activity: " + app); |
| } |
| } |
| // For the process state, we will at this point consider the |
| // process to be cached. It will be cached either as an activity |
| // or empty depending on whether the activity is finishing. We do |
| // this so that we can treat the process as cached for purposes of |
| // memory trimming (determing current memory level, trim command to |
| // send to process) since there can be an arbitrary number of stopping |
| // processes and they should soon all go into the cached state. |
| if (!r.finishing) { |
| if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) { |
| procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY; |
| app.adjType = "stop-activity"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise procstate to stop-activity: " + app); |
| } |
| } |
| } |
| app.cached = false; |
| app.empty = false; |
| foregroundActivities = true; |
| } else { |
| if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) { |
| procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; |
| app.adjType = "cch-act"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise procstate to cached activity: " + app); |
| } |
| } |
| } |
| } |
| if (adj == ProcessList.VISIBLE_APP_ADJ) { |
| adj += minLayer; |
| } |
| } |
| if (procState > ActivityManager.PROCESS_STATE_CACHED_RECENT && app.recentTasks.size() > 0) { |
| procState = ActivityManager.PROCESS_STATE_CACHED_RECENT; |
| app.adjType = "cch-rec"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to cached recent: " + app); |
| } |
| } |
| |
| if (adj > ProcessList.PERCEPTIBLE_APP_ADJ |
| || procState > ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { |
| if (app.foregroundServices) { |
| // The user is aware of this app, so make it visible. |
| adj = ProcessList.PERCEPTIBLE_APP_ADJ; |
| procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; |
| app.cached = false; |
| app.adjType = "fg-service"; |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to fg service: " + app); |
| } |
| } else if (app.hasOverlayUi) { |
| // The process is display an overlay UI. |
| adj = ProcessList.PERCEPTIBLE_APP_ADJ; |
| procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; |
| app.cached = false; |
| app.adjType = "has-overlay-ui"; |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to overlay ui: " + app); |
| } |
| } |
| } |
| |
| if (adj > ProcessList.PERCEPTIBLE_APP_ADJ |
| || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) { |
| if (app.forcingToImportant != null) { |
| // This is currently used for toasts... they are not interactive, and |
| // we don't want them to cause the app to become fully foreground (and |
| // thus out of background check), so we yes the best background level we can. |
| adj = ProcessList.PERCEPTIBLE_APP_ADJ; |
| procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; |
| app.cached = false; |
| app.adjType = "force-imp"; |
| app.adjSource = app.forcingToImportant; |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to force imp: " + app); |
| } |
| } |
| } |
| |
| if (app == mHeavyWeightProcess) { |
| if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) { |
| // We don't want to kill the current heavy-weight process. |
| adj = ProcessList.HEAVY_WEIGHT_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; |
| app.cached = false; |
| app.adjType = "heavy"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app); |
| } |
| } |
| if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) { |
| procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT; |
| app.adjType = "heavy"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to heavy: " + app); |
| } |
| } |
| } |
| |
| if (app == mHomeProcess) { |
| if (adj > ProcessList.HOME_APP_ADJ) { |
| // This process is hosting what we currently consider to be the |
| // home app, so we don't want to let it go into the background. |
| adj = ProcessList.HOME_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; |
| app.cached = false; |
| app.adjType = "home"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app); |
| } |
| } |
| if (procState > ActivityManager.PROCESS_STATE_HOME) { |
| procState = ActivityManager.PROCESS_STATE_HOME; |
| app.adjType = "home"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app); |
| } |
| } |
| } |
| |
| if (app == mPreviousProcess && app.activities.size() > 0) { |
| if (adj > ProcessList.PREVIOUS_APP_ADJ) { |
| // This was the previous process that showed UI to the user. |
| // We want to try to keep it around more aggressively, to give |
| // a good experience around switching between two apps. |
| adj = ProcessList.PREVIOUS_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; |
| app.cached = false; |
| app.adjType = "previous"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app); |
| } |
| } |
| if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) { |
| procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY; |
| app.adjType = "previous"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app); |
| } |
| } |
| } |
| |
| if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj |
| + " reason=" + app.adjType); |
| |
| // By default, we use the computed adjustment. It may be changed if |
| // there are applications dependent on our services or providers, but |
| // this gives us a baseline and makes sure we don't get into an |
| // infinite recursion. |
| app.curRawAdj = adj; |
| app.hasStartedServices = false; |
| app.adjSeq = mAdjSeq; |
| |
| if (mBackupTarget != null && app == mBackupTarget.app) { |
| // If possible we want to avoid killing apps while they're being backed up |
| if (adj > ProcessList.BACKUP_APP_ADJ) { |
| if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app); |
| adj = ProcessList.BACKUP_APP_ADJ; |
| if (procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) { |
| procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; |
| } |
| app.adjType = "backup"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app); |
| } |
| app.cached = false; |
| } |
| if (procState > ActivityManager.PROCESS_STATE_BACKUP) { |
| procState = ActivityManager.PROCESS_STATE_BACKUP; |
| app.adjType = "backup"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to backup: " + app); |
| } |
| } |
| } |
| |
| boolean mayBeTop = false; |
| String mayBeTopType = null; |
| Object mayBeTopSource = null; |
| Object mayBeTopTarget = null; |
| |
| for (int is = app.services.size()-1; |
| is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ |
| || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND |
| || procState > ActivityManager.PROCESS_STATE_TOP); |
| is--) { |
| ServiceRecord s = app.services.valueAt(is); |
| if (s.startRequested) { |
| app.hasStartedServices = true; |
| if (procState > ActivityManager.PROCESS_STATE_SERVICE) { |
| procState = ActivityManager.PROCESS_STATE_SERVICE; |
| app.adjType = "started-services"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise procstate to started service: " + app); |
| } |
| } |
| if (app.hasShownUi && app != mHomeProcess) { |
| // If this process has shown some UI, let it immediately |
| // go to the LRU list because it may be pretty heavy with |
| // UI stuff. We'll tag it with a label just to help |
| // debug and understand what is going on. |
| if (adj > ProcessList.SERVICE_ADJ) { |
| app.adjType = "cch-started-ui-services"; |
| } |
| } else { |
| if (now < (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) { |
| // This service has seen some activity within |
| // recent memory, so we will keep its process ahead |
| // of the background processes. |
| if (adj > ProcessList.SERVICE_ADJ) { |
| adj = ProcessList.SERVICE_ADJ; |
| app.adjType = "started-services"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise adj to started service: " + app); |
| } |
| app.cached = false; |
| } |
| } |
| // If we have let the service slide into the background |
| // state, still have some text describing what it is doing |
| // even though the service no longer has an impact. |
| if (adj > ProcessList.SERVICE_ADJ) { |
| app.adjType = "cch-started-services"; |
| } |
| } |
| } |
| |
| for (int conni = s.connections.size()-1; |
| conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ |
| || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND |
| || procState > ActivityManager.PROCESS_STATE_TOP); |
| conni--) { |
| ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni); |
| for (int i = 0; |
| i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ |
| || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND |
| || procState > ActivityManager.PROCESS_STATE_TOP); |
| i++) { |
| // XXX should compute this based on the max of |
| // all connected clients. |
| ConnectionRecord cr = clist.get(i); |
| if (cr.binding.client == app) { |
| // Binding to ourself is not interesting. |
| continue; |
| } |
| |
| if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) { |
| ProcessRecord client = cr.binding.client; |
| computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now); |
| if (client.containsCycle) { |
| // We've detected a cycle. We should ignore this connection and allow |
| // this process to retry computeOomAdjLocked later in case a later-checked |
| // connection from a client would raise its priority legitimately. |
| app.containsCycle = true; |
| continue; |
| } |
| int clientAdj = client.curRawAdj; |
| int clientProcState = client.curProcState; |
| if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) { |
| // If the other app is cached for any reason, for purposes here |
| // we are going to consider it empty. The specific cached state |
| // doesn't propagate except under certain conditions. |
| clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; |
| } |
| String adjType = null; |
| if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) { |
| // Not doing bind OOM management, so treat |
| // this guy more like a started service. |
| if (app.hasShownUi && app != mHomeProcess) { |
| // If this process has shown some UI, let it immediately |
| // go to the LRU list because it may be pretty heavy with |
| // UI stuff. We'll tag it with a label just to help |
| // debug and understand what is going on. |
| if (adj > clientAdj) { |
| adjType = "cch-bound-ui-services"; |
| } |
| app.cached = false; |
| clientAdj = adj; |
| clientProcState = procState; |
| } else { |
| if (now >= (s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY)) { |
| // This service has not seen activity within |
| // recent memory, so allow it to drop to the |
| // LRU list if there is no other reason to keep |
| // it around. We'll also tag it with a label just |
| // to help debug and undertand what is going on. |
| if (adj > clientAdj) { |
| adjType = "cch-bound-services"; |
| } |
| clientAdj = adj; |
| } |
| } |
| } |
| if (adj > clientAdj) { |
| // If this process has recently shown UI, and |
| // the process that is binding to it is less |
| // important than being visible, then we don't |
| // care about the binding as much as we care |
| // about letting this process get into the LRU |
| // list to be killed and restarted if needed for |
| // memory. |
| if (app.hasShownUi && app != mHomeProcess |
| && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { |
| if (adj >= ProcessList.CACHED_APP_MIN_ADJ) { |
| adjType = "cch-bound-ui-services"; |
| } |
| } else { |
| int newAdj; |
| if ((cr.flags&(Context.BIND_ABOVE_CLIENT |
| |Context.BIND_IMPORTANT)) != 0) { |
| if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) { |
| newAdj = clientAdj; |
| } else { |
| // make this service persistent |
| newAdj = ProcessList.PERSISTENT_SERVICE_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| procState = ActivityManager.PROCESS_STATE_PERSISTENT; |
| } |
| } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0 |
| && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ |
| && adj > ProcessList.PERCEPTIBLE_APP_ADJ) { |
| newAdj = ProcessList.PERCEPTIBLE_APP_ADJ; |
| } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) { |
| newAdj = clientAdj; |
| } else { |
| if (adj > ProcessList.VISIBLE_APP_ADJ) { |
| newAdj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ); |
| } else { |
| newAdj = adj; |
| } |
| } |
| if (!client.cached) { |
| app.cached = false; |
| } |
| if (adj > newAdj) { |
| adj = newAdj; |
| adjType = "service"; |
| } |
| } |
| } |
| if ((cr.flags & (Context.BIND_NOT_FOREGROUND |
| | Context.BIND_IMPORTANT_BACKGROUND)) == 0) { |
| // This will treat important bound services identically to |
| // the top app, which may behave differently than generic |
| // foreground work. |
| if (client.curSchedGroup > schedGroup) { |
| if ((cr.flags&Context.BIND_IMPORTANT) != 0) { |
| schedGroup = client.curSchedGroup; |
| } else { |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| } |
| } |
| if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) { |
| if (clientProcState == ActivityManager.PROCESS_STATE_TOP) { |
| // Special handling of clients who are in the top state. |
| // We *may* want to consider this process to be in the |
| // top state as well, but only if there is not another |
| // reason for it to be running. Being on the top is a |
| // special state, meaning you are specifically running |
| // for the current top app. If the process is already |
| // running in the background for some other reason, it |
| // is more important to continue considering it to be |
| // in the background state. |
| mayBeTop = true; |
| mayBeTopType = "service"; |
| mayBeTopSource = cr.binding.client; |
| mayBeTopTarget = s.name; |
| clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; |
| } else { |
| // Special handling for above-top states (persistent |
| // processes). These should not bring the current process |
| // into the top state, since they are not on top. Instead |
| // give them the best state after that. |
| if ((cr.flags&Context.BIND_FOREGROUND_SERVICE) != 0) { |
| clientProcState = |
| ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; |
| } else if (mWakefulness |
| == PowerManagerInternal.WAKEFULNESS_AWAKE && |
| (cr.flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) |
| != 0) { |
| clientProcState = |
| ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; |
| } else { |
| clientProcState = |
| ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; |
| } |
| } |
| } |
| } else if ((cr.flags & Context.BIND_IMPORTANT_BACKGROUND) == 0) { |
| if (clientProcState < |
| ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) { |
| clientProcState = |
| ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; |
| } |
| } else { |
| if (clientProcState < |
| ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) { |
| clientProcState = |
| ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND; |
| } |
| } |
| if (procState > clientProcState) { |
| procState = clientProcState; |
| if (adjType == null) { |
| adjType = "service"; |
| } |
| } |
| if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND |
| && (cr.flags&Context.BIND_SHOWING_UI) != 0) { |
| app.pendingUiClean = true; |
| } |
| if (adjType != null) { |
| app.adjType = adjType; |
| app.adjTypeCode = ActivityManager.RunningAppProcessInfo |
| .REASON_SERVICE_IN_USE; |
| app.adjSource = cr.binding.client; |
| app.adjSourceProcState = clientProcState; |
| app.adjTarget = s.name; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType |
| + ": " + app + ", due to " + cr.binding.client |
| + " adj=" + adj + " procState=" |
| + ProcessList.makeProcStateString(procState)); |
| } |
| } |
| } |
| if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { |
| app.treatLikeActivity = true; |
| } |
| final ActivityRecord a = cr.activity; |
| if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) { |
| if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && (a.visible |
| || a.isState(ActivityState.RESUMED, ActivityState.PAUSING))) { |
| adj = ProcessList.FOREGROUND_APP_ADJ; |
| if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { |
| if ((cr.flags&Context.BIND_IMPORTANT) != 0) { |
| schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND; |
| } else { |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| } |
| } |
| app.cached = false; |
| app.adjType = "service"; |
| app.adjTypeCode = ActivityManager.RunningAppProcessInfo |
| .REASON_SERVICE_IN_USE; |
| app.adjSource = a; |
| app.adjSourceProcState = procState; |
| app.adjTarget = s.name; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise to service w/activity: " + app); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| for (int provi = app.pubProviders.size()-1; |
| provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ |
| || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND |
| || procState > ActivityManager.PROCESS_STATE_TOP); |
| provi--) { |
| ContentProviderRecord cpr = app.pubProviders.valueAt(provi); |
| for (int i = cpr.connections.size()-1; |
| i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ |
| || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND |
| || procState > ActivityManager.PROCESS_STATE_TOP); |
| i--) { |
| ContentProviderConnection conn = cpr.connections.get(i); |
| ProcessRecord client = conn.client; |
| if (client == app) { |
| // Being our own client is not interesting. |
| continue; |
| } |
| computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now); |
| if (client.containsCycle) { |
| // We've detected a cycle. We should ignore this connection and allow |
| // this process to retry computeOomAdjLocked later in case a later-checked |
| // connection from a client would raise its priority legitimately. |
| app.containsCycle = true; |
| continue; |
| } |
| int clientAdj = client.curRawAdj; |
| int clientProcState = client.curProcState; |
| if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) { |
| // If the other app is cached for any reason, for purposes here |
| // we are going to consider it empty. |
| clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; |
| } |
| String adjType = null; |
| if (adj > clientAdj) { |
| if (app.hasShownUi && app != mHomeProcess |
| && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) { |
| adjType = "cch-ui-provider"; |
| } else { |
| adj = clientAdj > ProcessList.FOREGROUND_APP_ADJ |
| ? clientAdj : ProcessList.FOREGROUND_APP_ADJ; |
| adjType = "provider"; |
| } |
| app.cached &= client.cached; |
| } |
| if (clientProcState <= ActivityManager.PROCESS_STATE_TOP) { |
| if (clientProcState == ActivityManager.PROCESS_STATE_TOP) { |
| // Special handling of clients who are in the top state. |
| // We *may* want to consider this process to be in the |
| // top state as well, but only if there is not another |
| // reason for it to be running. Being on the top is a |
| // special state, meaning you are specifically running |
| // for the current top app. If the process is already |
| // running in the background for some other reason, it |
| // is more important to continue considering it to be |
| // in the background state. |
| mayBeTop = true; |
| clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; |
| mayBeTopType = adjType = "provider-top"; |
| mayBeTopSource = client; |
| mayBeTopTarget = cpr.name; |
| } else { |
| // Special handling for above-top states (persistent |
| // processes). These should not bring the current process |
| // into the top state, since they are not on top. Instead |
| // give them the best state after that. |
| clientProcState = |
| ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; |
| if (adjType == null) { |
| adjType = "provider"; |
| } |
| } |
| } |
| if (procState > clientProcState) { |
| procState = clientProcState; |
| } |
| if (client.curSchedGroup > schedGroup) { |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| } |
| if (adjType != null) { |
| app.adjType = adjType; |
| app.adjTypeCode = ActivityManager.RunningAppProcessInfo |
| .REASON_PROVIDER_IN_USE; |
| app.adjSource = client; |
| app.adjSourceProcState = clientProcState; |
| app.adjTarget = cpr.name; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to " + adjType |
| + ": " + app + ", due to " + client |
| + " adj=" + adj + " procState=" |
| + ProcessList.makeProcStateString(procState)); |
| } |
| } |
| } |
| // If the provider has external (non-framework) process |
| // dependencies, ensure that its adjustment is at least |
| // FOREGROUND_APP_ADJ. |
| if (cpr.hasExternalProcessHandles()) { |
| if (adj > ProcessList.FOREGROUND_APP_ADJ) { |
| adj = ProcessList.FOREGROUND_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| app.cached = false; |
| app.adjType = "ext-provider"; |
| app.adjTarget = cpr.name; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise adj to external provider: " + app); |
| } |
| } |
| if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { |
| procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise procstate to external provider: " + app); |
| } |
| } |
| } |
| |
| if (app.lastProviderTime > 0 && |
| (app.lastProviderTime+mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) { |
| if (adj > ProcessList.PREVIOUS_APP_ADJ) { |
| adj = ProcessList.PREVIOUS_APP_ADJ; |
| schedGroup = ProcessList.SCHED_GROUP_BACKGROUND; |
| app.cached = false; |
| app.adjType = "recent-provider"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise adj to recent provider: " + app); |
| } |
| } |
| if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) { |
| procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY; |
| app.adjType = "recent-provider"; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, |
| "Raise procstate to recent provider: " + app); |
| } |
| } |
| } |
| |
| if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) { |
| // A client of one of our services or providers is in the top state. We |
| // *may* want to be in the top state, but not if we are already running in |
| // the background for some other reason. For the decision here, we are going |
| // to pick out a few specific states that we want to remain in when a client |
| // is top (states that tend to be longer-term) and otherwise allow it to go |
| // to the top state. |
| switch (procState) { |
| case ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE: |
| case ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE: |
| // Something else is keeping it at this level, just leave it. |
| break; |
| case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: |
| case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: |
| case ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND: |
| case ActivityManager.PROCESS_STATE_SERVICE: |
| // These all are longer-term states, so pull them up to the top |
| // of the background states, but not all the way to the top state. |
| procState = ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; |
| app.adjType = mayBeTopType; |
| app.adjSource = mayBeTopSource; |
| app.adjTarget = mayBeTopTarget; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType |
| + ": " + app + ", due to " + mayBeTopSource |
| + " adj=" + adj + " procState=" |
| + ProcessList.makeProcStateString(procState)); |
| } |
| break; |
| default: |
| // Otherwise, top is a better choice, so take it. |
| procState = ActivityManager.PROCESS_STATE_TOP; |
| app.adjType = mayBeTopType; |
| app.adjSource = mayBeTopSource; |
| app.adjTarget = mayBeTopTarget; |
| if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, "May be top raise to " + mayBeTopType |
| + ": " + app + ", due to " + mayBeTopSource |
| + " adj=" + adj + " procState=" |
| + ProcessList.makeProcStateString(procState)); |
| } |
| break; |
| } |
| } |
| |
| if (procState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) { |
| if (app.hasClientActivities) { |
| // This is a cached process, but with client activities. Mark it so. |
| procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT; |
| app.adjType = "cch-client-act"; |
| } else if (app.treatLikeActivity) { |
| // This is a cached process, but somebody wants us to treat it like it has |
| // an activity, okay! |
| procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; |
| app.adjType = "cch-as-act"; |
| } |
| } |
| |
| if (adj == ProcessList.SERVICE_ADJ) { |
| if (doingAll) { |
| app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3); |
| mNewNumServiceProcs++; |
| //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb); |
| if (!app.serviceb) { |
| // This service isn't far enough down on the LRU list to |
| // normally be a B service, but if we are low on RAM and it |
| // is large we want to force it down since we would prefer to |
| // keep launcher over it. |
| if (mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL |
| && app.lastPss >= mProcessList.getCachedRestoreThresholdKb()) { |
| app.serviceHighRam = true; |
| app.serviceb = true; |
| //Slog.i(TAG, "ADJ " + app + " high ram!"); |
| } else { |
| mNewNumAServiceProcs++; |
| //Slog.i(TAG, "ADJ " + app + " not high ram!"); |
| } |
| } else { |
| app.serviceHighRam = false; |
| } |
| } |
| if (app.serviceb) { |
| adj = ProcessList.SERVICE_B_ADJ; |
| } |
| } |
| |
| app.curRawAdj = adj; |
| |
| //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid + |
| // " adj=" + adj + " curAdj=" + app.curAdj + " maxAdj=" + app.maxAdj); |
| if (adj > app.maxAdj) { |
| adj = app.maxAdj; |
| if (app.maxAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { |
| schedGroup = ProcessList.SCHED_GROUP_DEFAULT; |
| } |
| } |
| |
| // Put bound foreground services in a special sched group for additional |
| // restrictions on screen off |
| if (procState >= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE && |
| mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE) { |
| if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) { |
| schedGroup = ProcessList.SCHED_GROUP_RESTRICTED; |
| } |
| } |
| |
| // Do final modification to adj. Everything we do between here and applying |
| // the final setAdj must be done in this function, because we will also use |
| // it when computing the final cached adj later. Note that we don't need to |
| // worry about this for max adj above, since max adj will always be used to |
| // keep it out of the cached vaues. |
| app.curAdj = app.modifyRawOomAdj(adj); |
| app.curSchedGroup = schedGroup; |
| app.curProcState = procState; |
| app.foregroundActivities = foregroundActivities; |
| app.completedAdjSeq = mAdjSeq; |
| |
| // if curAdj is less than prevAppAdj, then this process was promoted |
| return app.curAdj < prevAppAdj; |
| } |
| |
| /** |
| * Record new PSS sample for a process. |
| */ |
| void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long swapPss, |
| long rss, int statType, long pssDuration, long now) { |
| EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024, |
| swapPss * 1024, rss * 1024, statType, procState, pssDuration); |
| proc.lastPssTime = now; |
| proc.baseProcessTracker.addPss(pss, uss, rss, true, statType, pssDuration, proc.pkgList); |
| if (DEBUG_PSS) Slog.d(TAG_PSS, |
| "pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss |
| + " state=" + ProcessList.makeProcStateString(procState)); |
| if (proc.initialIdlePss == 0) { |
| proc.initialIdlePss = pss; |
| } |
| proc.lastPss = pss; |
| proc.lastSwapPss = swapPss; |
| if (procState >= ActivityManager.PROCESS_STATE_HOME) { |
| proc.lastCachedPss = pss; |
| proc.lastCachedSwapPss = swapPss; |
| } |
| |
| final SparseArray<Pair<Long, String>> watchUids |
| = mMemWatchProcesses.getMap().get(proc.processName); |
| Long check = null; |
| if (watchUids != null) { |
| Pair<Long, String> val = watchUids.get(proc.uid); |
| if (val == null) { |
| val = watchUids.get(0); |
| } |
| if (val != null) { |
| check = val.first; |
| } |
| } |
| if (check != null) { |
| if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) { |
| boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); |
| if (!isDebuggable) { |
| if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0) { |
| isDebuggable = true; |
| } |
| } |
| if (isDebuggable) { |
| Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting"); |
| final ProcessRecord myProc = proc; |
| final File heapdumpFile = DumpHeapProvider.getJavaFile(); |
| mMemWatchDumpProcName = proc.processName; |
| mMemWatchDumpFile = heapdumpFile.toString(); |
| mMemWatchDumpPid = proc.pid; |
| mMemWatchDumpUid = proc.uid; |
| BackgroundThread.getHandler().post(new Runnable() { |
| @Override |
| public void run() { |
| revokeUriPermission(ActivityThread.currentActivityThread() |
| .getApplicationThread(), |
| null, DumpHeapActivity.JAVA_URI, |
| Intent.FLAG_GRANT_READ_URI_PERMISSION |
| | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, |
| UserHandle.myUserId()); |
| ParcelFileDescriptor fd = null; |
| try { |
| heapdumpFile.delete(); |
| fd = ParcelFileDescriptor.open(heapdumpFile, |
| ParcelFileDescriptor.MODE_CREATE | |
| ParcelFileDescriptor.MODE_TRUNCATE | |
| ParcelFileDescriptor.MODE_WRITE_ONLY | |
| ParcelFileDescriptor.MODE_APPEND); |
| IApplicationThread thread = myProc.thread; |
| if (thread != null) { |
| try { |
| if (DEBUG_PSS) Slog.d(TAG_PSS, |
| "Requesting dump heap from " |
| + myProc + " to " + heapdumpFile); |
| thread.dumpHeap(/* managed= */ true, |
| /* mallocInfo= */ false, /* runGc= */ false, |
| heapdumpFile.toString(), fd, |
| /* finishCallback= */ null); |
| } catch (RemoteException e) { |
| } |
| } |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| } finally { |
| if (fd != null) { |
| try { |
| fd.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| }); |
| } else { |
| Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check |
| + ", but debugging not enabled"); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Schedule PSS collection of a process. |
| */ |
| boolean requestPssLocked(ProcessRecord proc, int procState) { |
| if (mPendingPssProcesses.contains(proc)) { |
| return false; |
| } |
| if (mPendingPssProcesses.size() == 0) { |
| mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG); |
| } |
| if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc); |
| proc.pssProcState = procState; |
| proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE; |
| mPendingPssProcesses.add(proc); |
| return true; |
| } |
| |
| /** |
| * Schedule PSS collection of all processes. |
| */ |
| void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) { |
| if (!always) { |
| if (now < (mLastFullPssTime + |
| (memLowered ? mConstants.FULL_PSS_LOWERED_INTERVAL |
| : mConstants.FULL_PSS_MIN_INTERVAL))) { |
| return; |
| } |
| } |
| if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered); |
| mLastFullPssTime = now; |
| mFullPssPending = true; |
| for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) { |
| ProcessList.abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);; |
| } |
| mPendingPssProcesses.ensureCapacity(mLruProcesses.size()); |
| mPendingPssProcesses.clear(); |
| for (int i = mLruProcesses.size() - 1; i >= 0; i--) { |
| ProcessRecord app = mLruProcesses.get(i); |
| if (app.thread == null |
| || app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) { |
| continue; |
| } |
| if (memLowered || (always && now > |
| app.lastStateTime+ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) |
| || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) { |
| app.pssProcState = app.setProcState; |
| app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL |
| : ProcessStats.ADD_PSS_INTERNAL_ALL_MEM; |
| app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, |
| app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now); |
| mPendingPssProcesses.add(app); |
| } |
| } |
| if (!mBgHandler.hasMessages(COLLECT_PSS_BG_MSG)) { |
| mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG); |
| } |
| } |
| |
| public void setTestPssMode(boolean enabled) { |
| synchronized (this) { |
| mTestPssMode = enabled; |
| if (enabled) { |
| // Whenever we enable the mode, we want to take a snapshot all of current |
| // process mem use. |
| requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, true); |
| } |
| } |
| } |
| |
| /** |
| * Ask a given process to GC right now. |
| */ |
| final void performAppGcLocked(ProcessRecord app) { |
| try { |
| app.lastRequestedGc = SystemClock.uptimeMillis(); |
| if (app.thread != null) { |
| if (app.reportLowMemory) { |
| app.reportLowMemory = false; |
| app.thread.scheduleLowMemory(); |
| } else { |
| app.thread.processInBackground(); |
| } |
| } |
| } catch (Exception e) { |
| // whatever. |
| } |
| } |
| |
| /** |
| * Returns true if things are idle enough to perform GCs. |
| */ |
| private final boolean canGcNowLocked() { |
| boolean processingBroadcasts = false; |
| for (BroadcastQueue q : mBroadcastQueues) { |
| if (q.mParallelBroadcasts.size() != 0 || q.mOrderedBroadcasts.size() != 0) { |
| processingBroadcasts = true; |
| } |
| } |
| return !processingBroadcasts |
| && (isSleepingLocked() || mStackSupervisor.allResumedActivitiesIdle()); |
| } |
| |
| /** |
| * Perform GCs on all processes that are waiting for it, but only |
| * if things are idle. |
| */ |
| final void performAppGcsLocked() { |
| final int N = mProcessesToGc.size(); |
| if (N <= 0) { |
| return; |
| } |
| if (canGcNowLocked()) { |
| while (mProcessesToGc.size() > 0) { |
| ProcessRecord proc = mProcessesToGc.remove(0); |
| if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) { |
| if ((proc.lastRequestedGc+mConstants.GC_MIN_INTERVAL) |
| <= SystemClock.uptimeMillis()) { |
| // To avoid spamming the system, we will GC processes one |
| // at a time, waiting a few seconds between each. |
| performAppGcLocked(proc); |
| scheduleAppGcsLocked(); |
| return; |
| } else { |
| // It hasn't been long enough since we last GCed this |
| // process... put it in the list to wait for its time. |
| addProcessToGcListLocked(proc); |
| break; |
| } |
| } |
| } |
| |
| scheduleAppGcsLocked(); |
| } |
| } |
| |
| /** |
| * If all looks good, perform GCs on all processes waiting for them. |
| */ |
| final void performAppGcsIfAppropriateLocked() { |
| if (canGcNowLocked()) { |
| performAppGcsLocked(); |
| return; |
| } |
| // Still not idle, wait some more. |
| scheduleAppGcsLocked(); |
| } |
| |
| /** |
| * Schedule the execution of all pending app GCs. |
| */ |
| final void scheduleAppGcsLocked() { |
| mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG); |
| |
| if (mProcessesToGc.size() > 0) { |
| // Schedule a GC for the time to the next process. |
| ProcessRecord proc = mProcessesToGc.get(0); |
| Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG); |
| |
| long when = proc.lastRequestedGc + mConstants.GC_MIN_INTERVAL; |
| long now = SystemClock.uptimeMillis(); |
| if (when < (now+mConstants.GC_TIMEOUT)) { |
| when = now + mConstants.GC_TIMEOUT; |
| } |
| mHandler.sendMessageAtTime(msg, when); |
| } |
| } |
| |
| /** |
| * Add a process to the array of processes waiting to be GCed. Keeps the |
| * list in sorted order by the last GC time. The process can't already be |
| * on the list. |
| */ |
| final void addProcessToGcListLocked(ProcessRecord proc) { |
| boolean added = false; |
| for (int i=mProcessesToGc.size()-1; i>=0; i--) { |
| if (mProcessesToGc.get(i).lastRequestedGc < |
| proc.lastRequestedGc) { |
| added = true; |
| mProcessesToGc.add(i+1, proc); |
| break; |
| } |
| } |
| if (!added) { |
| mProcessesToGc.add(0, proc); |
| } |
| } |
| |
| /** |
| * Set up to ask a process to GC itself. This will either do it |
| * immediately, or put it on the list of processes to gc the next |
| * time things are idle. |
| */ |
| final void scheduleAppGcLocked(ProcessRecord app) { |
| long now = SystemClock.uptimeMillis(); |
| if ((app.lastRequestedGc+mConstants.GC_MIN_INTERVAL) > now) { |
| return; |
| } |
| if (!mProcessesToGc.contains(app)) { |
| addProcessToGcListLocked(app); |
| scheduleAppGcsLocked(); |
| } |
| } |
| |
| final void checkExcessivePowerUsageLocked() { |
| updateCpuStatsNow(); |
| |
| BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); |
| boolean doCpuKills = true; |
| if (mLastPowerCheckUptime == 0) { |
| doCpuKills = false; |
| } |
| final long curUptime = SystemClock.uptimeMillis(); |
| final long uptimeSince = curUptime - mLastPowerCheckUptime; |
| mLastPowerCheckUptime = curUptime; |
| int i = mLruProcesses.size(); |
| while (i > 0) { |
| i--; |
| ProcessRecord app = mLruProcesses.get(i); |
| if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { |
| if (app.lastCpuTime <= 0) { |
| continue; |
| } |
| long cputimeUsed = app.curCpuTime - app.lastCpuTime; |
| if (DEBUG_POWER) { |
| StringBuilder sb = new StringBuilder(128); |
| sb.append("CPU for "); |
| app.toShortString(sb); |
| sb.append(": over "); |
| TimeUtils.formatDuration(uptimeSince, sb); |
| sb.append(" used "); |
| TimeUtils.formatDuration(cputimeUsed, sb); |
| sb.append(" ("); |
| sb.append((cputimeUsed*100)/uptimeSince); |
| sb.append("%)"); |
| Slog.i(TAG_POWER, sb.toString()); |
| } |
| // If the process has used too much CPU over the last duration, the |
| // user probably doesn't want this, so kill! |
| if (doCpuKills && uptimeSince > 0) { |
| // What is the limit for this process? |
| int cpuLimit; |
| long checkDur = curUptime - app.whenUnimportant; |
| if (checkDur <= mConstants.POWER_CHECK_INTERVAL) { |
| cpuLimit = mConstants.POWER_CHECK_MAX_CPU_1; |
| } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL*2) |
| || app.setProcState <= ActivityManager.PROCESS_STATE_HOME) { |
| cpuLimit = mConstants.POWER_CHECK_MAX_CPU_2; |
| } else if (checkDur <= (mConstants.POWER_CHECK_INTERVAL*3)) { |
| cpuLimit = mConstants.POWER_CHECK_MAX_CPU_3; |
| } else { |
| cpuLimit = mConstants.POWER_CHECK_MAX_CPU_4; |
| } |
| if (((cputimeUsed*100)/uptimeSince) >= cpuLimit) { |
| synchronized (stats) { |
| stats.reportExcessiveCpuLocked(app.info.uid, app.processName, |
| uptimeSince, cputimeUsed); |
| } |
| app.kill("excessive cpu " + cputimeUsed + " during " + uptimeSince |
| + " dur=" + checkDur + " limit=" + cpuLimit, true); |
| app.baseProcessTracker.reportExcessiveCpu(app.pkgList); |
| } |
| } |
| app.lastCpuTime = app.curCpuTime; |
| } |
| } |
| } |
| |
| private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now, |
| long nowElapsed) { |
| boolean success = true; |
| |
| if (app.curRawAdj != app.setRawAdj) { |
| app.setRawAdj = app.curRawAdj; |
| } |
| |
| int changes = 0; |
| |
| if (app.curAdj != app.setAdj) { |
| ProcessList.setOomAdj(app.pid, app.uid, app.curAdj); |
| if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.info.uid) { |
| String msg = "Set " + app.pid + " " + app.processName + " adj " |
| + app.curAdj + ": " + app.adjType; |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, msg); |
| } |
| app.setAdj = app.curAdj; |
| app.verifiedAdj = ProcessList.INVALID_ADJ; |
| } |
| |
| if (app.setSchedGroup != app.curSchedGroup) { |
| int oldSchedGroup = app.setSchedGroup; |
| app.setSchedGroup = app.curSchedGroup; |
| if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) { |
| String msg = "Setting sched group of " + app.processName |
| + " to " + app.curSchedGroup + ": " + app.adjType; |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, msg); |
| } |
| if (app.waitingToKill != null && app.curReceivers.isEmpty() |
| && app.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND) { |
| app.kill(app.waitingToKill, true); |
| success = false; |
| } else { |
| int processGroup; |
| switch (app.curSchedGroup) { |
| case ProcessList.SCHED_GROUP_BACKGROUND: |
| processGroup = THREAD_GROUP_BG_NONINTERACTIVE; |
| break; |
| case ProcessList.SCHED_GROUP_TOP_APP: |
| case ProcessList.SCHED_GROUP_TOP_APP_BOUND: |
| processGroup = THREAD_GROUP_TOP_APP; |
| break; |
| case ProcessList.SCHED_GROUP_RESTRICTED: |
| processGroup = THREAD_GROUP_RESTRICTED; |
| break; |
| default: |
| processGroup = THREAD_GROUP_DEFAULT; |
| break; |
| } |
| long oldId = Binder.clearCallingIdentity(); |
| try { |
| setProcessGroup(app.pid, processGroup); |
| if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { |
| // do nothing if we already switched to RT |
| if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { |
| mActivityTaskManager.onTopProcChangedLocked(app); |
| if (mUseFifoUiScheduling) { |
| // Switch UI pipeline for app to SCHED_FIFO |
| app.savedPriority = Process.getThreadPriority(app.pid); |
| scheduleAsFifoPriority(app.pid, /* suppressLogs */true); |
| if (app.renderThreadTid != 0) { |
| scheduleAsFifoPriority(app.renderThreadTid, |
| /* suppressLogs */true); |
| if (DEBUG_OOM_ADJ) { |
| Slog.d("UI_FIFO", "Set RenderThread (TID " + |
| app.renderThreadTid + ") to FIFO"); |
| } |
| } else { |
| if (DEBUG_OOM_ADJ) { |
| Slog.d("UI_FIFO", "Not setting RenderThread TID"); |
| } |
| } |
| } else { |
| // Boost priority for top app UI and render threads |
| setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST); |
| if (app.renderThreadTid != 0) { |
| try { |
| setThreadPriority(app.renderThreadTid, |
| TOP_APP_PRIORITY_BOOST); |
| } catch (IllegalArgumentException e) { |
| // thread died, ignore |
| } |
| } |
| } |
| } |
| } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && |
| app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { |
| mActivityTaskManager.onTopProcChangedLocked(app); |
| if (mUseFifoUiScheduling) { |
| try { |
| // Reset UI pipeline to SCHED_OTHER |
| setThreadScheduler(app.pid, SCHED_OTHER, 0); |
| setThreadPriority(app.pid, app.savedPriority); |
| if (app.renderThreadTid != 0) { |
| setThreadScheduler(app.renderThreadTid, |
| SCHED_OTHER, 0); |
| setThreadPriority(app.renderThreadTid, -4); |
| } |
| } catch (IllegalArgumentException e) { |
| Slog.w(TAG, |
| "Failed to set scheduling policy, thread does not exist:\n" |
| + e); |
| } catch (SecurityException e) { |
| Slog.w(TAG, "Failed to set scheduling policy, not allowed:\n" + e); |
| } |
| } else { |
| // Reset priority for top app UI and render threads |
| setThreadPriority(app.pid, 0); |
| if (app.renderThreadTid != 0) { |
| setThreadPriority(app.renderThreadTid, 0); |
| } |
| } |
| } |
| } catch (Exception e) { |
| if (false) { |
| Slog.w(TAG, "Failed setting process group of " + app.pid |
| + " to " + app.curSchedGroup); |
| Slog.w(TAG, "at location", e); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(oldId); |
| } |
| } |
| } |
| if (app.repForegroundActivities != app.foregroundActivities) { |
| app.repForegroundActivities = app.foregroundActivities; |
| changes |= ProcessChangeItem.CHANGE_ACTIVITIES; |
| } |
| if (app.repProcState != app.curProcState) { |
| app.repProcState = app.curProcState; |
| if (app.thread != null) { |
| try { |
| if (false) { |
| //RuntimeException h = new RuntimeException("here"); |
| Slog.i(TAG, "Sending new process state " + app.repProcState |
| + " to " + app /*, h*/); |
| } |
| app.thread.setProcessState(app.repProcState); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| if (app.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT |
| || ProcessList.procStatesDifferForMem(app.curProcState, app.setProcState)) { |
| if (false && mTestPssMode && app.setProcState >= 0 && app.lastStateTime <= (now-200)) { |
| // Experimental code to more aggressively collect pss while |
| // running test... the problem is that this tends to collect |
| // the data right when a process is transitioning between process |
| // states, which will tend to give noisy data. |
| long start = SystemClock.uptimeMillis(); |
| long startTime = SystemClock.currentThreadTimeMillis(); |
| long pss = Debug.getPss(app.pid, mTmpLong, null); |
| long endTime = SystemClock.currentThreadTimeMillis(); |
| recordPssSampleLocked(app, app.curProcState, pss, mTmpLong[0], mTmpLong[1], |
| mTmpLong[2], ProcessStats.ADD_PSS_INTERNAL_SINGLE, endTime-startTime, now); |
| mPendingPssProcesses.remove(app); |
| Slog.i(TAG, "Recorded pss for " + app + " state " + app.setProcState |
| + " to " + app.curProcState + ": " |
| + (SystemClock.uptimeMillis()-start) + "ms"); |
| } |
| app.lastStateTime = now; |
| app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, |
| app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now); |
| if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from " |
| + ProcessList.makeProcStateString(app.setProcState) + " to " |
| + ProcessList.makeProcStateString(app.curProcState) + " next pss in " |
| + (app.nextPssTime-now) + ": " + app); |
| } else { |
| if (now > app.nextPssTime || (now > (app.lastPssTime+ProcessList.PSS_MAX_INTERVAL) |
| && now > (app.lastStateTime+ProcessList.minTimeFromStateChange( |
| mTestPssMode)))) { |
| if (requestPssLocked(app, app.setProcState)) { |
| app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, |
| app.procStateMemTracker, mTestPssMode, isSleepingLocked(), now); |
| } |
| } else if (false && DEBUG_PSS) Slog.d(TAG_PSS, |
| "Not requesting pss of " + app + ": next=" + (app.nextPssTime-now)); |
| } |
| if (app.setProcState != app.curProcState) { |
| if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mCurOomAdjUid == app.uid) { |
| String msg = "Proc state change of " + app.processName |
| + " to " + ProcessList.makeProcStateString(app.curProcState) |
| + " (" + app.curProcState + ")" + ": " + app.adjType; |
| reportOomAdjMessageLocked(TAG_OOM_ADJ, msg); |
| } |
| boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE; |
| boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE; |
| if (setImportant && !curImportant) { |
| // This app is no longer something we consider important enough to allow to |
| // use arbitrary amounts of battery power. Note |
| // its current CPU time to later know to kill it if |
| // it is not behaving well. |
| app.whenUnimportant = now; |
| app.lastCpuTime = 0; |
| } |
| // Inform UsageStats of important process state change |
| // Must be called before updating setProcState |
| maybeUpdateUsageStatsLocked(app, nowElapsed); |
| |
| app.setProcState = app.curProcState; |
| if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { |
| app.notCachedSinceIdle = false; |
| } |
| if (!doingAll) { |
| setProcessTrackerStateLocked(app, mProcessStats.getMemFactorLocked(), now); |
| } else { |
| app.procStateChanged = true; |
| } |
| } else if (app.reportedInteraction && (nowElapsed-app.interactionEventTime) |
| > mConstants.USAGE_STATS_INTERACTION_INTERVAL) { |
| // For apps that sit around for a long time in the interactive state, we need |
| // to report this at least once a day so they don't go idle. |
| maybeUpdateUsageStatsLocked(app, nowElapsed); |
| } |
| |
| if (changes != 0) { |
| if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, |
| "Changes in " + app + ": " + changes); |
| int i = mPendingProcessChanges.size()-1; |
| ProcessChangeItem item = null; |
| while (i >= 0) { |
| item = mPendingProcessChanges.get(i); |
| if (item.pid == app.pid) { |
| if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, |
| "Re-using existing item: " + item); |
| break; |
| } |
| i--; |
| } |
| if (i < 0) { |
| // No existing item in pending changes; need a new one. |
| final int NA = mAvailProcessChanges.size(); |
| if (NA > 0) { |
| item = mAvailProcessChanges.remove(NA-1); |
| if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, |
| "Retrieving available item: " + item); |
| } else { |
| item = new ProcessChangeItem(); |
| if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, |
| "Allocating new item: " + item); |
| } |
| item.changes = 0; |
| item.pid = app.pid; |
| item.uid = app.info.uid; |
| if (mPendingProcessChanges.size() == 0) { |
| if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, |
| "*** Enqueueing dispatch processes changed!"); |
| mUiHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED_UI_MSG).sendToTarget(); |
| } |
| mPendingProcessChanges.add(item); |
| } |
| item.changes |= changes; |
| item.foregroundActivities = app.repForegroundActivities; |
| if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, |
| "Item " + Integer.toHexString(System.identityHashCode(item)) |
| + " " + app.toShortString() + ": changes=" + item.changes |
| + " foreground=" + item.foregroundActivities |
| + " type=" + app.adjType + " source=" + app.adjSource |
| + " target=" + app.adjTarget); |
| } |
| |
| return success; |
| } |
| |
| private boolean isEphemeralLocked(int uid) { |
| String packages[] = mContext.getPackageManager().getPackagesForUid(uid); |
| if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid |
| return false; |
| } |
| return getPackageManagerInternalLocked().isPackageEphemeral(UserHandle.getUserId(uid), |
| packages[0]); |
| } |
| |
| @VisibleForTesting |
| final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) { |
| final UidRecord.ChangeItem pendingChange; |
| if (uidRec == null || uidRec.pendingChange == null) { |
| if (mPendingUidChanges.size() == 0) { |
| if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, |
| "*** Enqueueing dispatch uid changed!"); |
| mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED_UI_MSG).sendToTarget(); |
| } |
| final int NA = mAvailUidChanges.size(); |
| if (NA > 0) { |
| pendingChange = mAvailUidChanges.remove(NA-1); |
| if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, |
| "Retrieving available item: " + pendingChange); |
| } else { |
| pendingChange = new UidRecord.ChangeItem(); |
| if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, |
| "Allocating new item: " + pendingChange); |
| } |
| if (uidRec != null) { |
| uidRec.pendingChange = pendingChange; |
| if ((change & UidRecord.CHANGE_GONE) != 0 && !uidRec.idle) { |
| // If this uid is going away, and we haven't yet reported it is gone, |
| // then do so now. |
| change |= UidRecord.CHANGE_IDLE; |
| } |
| } else if (uid < 0) { |
| throw new IllegalArgumentException("No UidRecord or uid"); |
| } |
| pendingChange.uidRecord = uidRec; |
| pendingChange.uid = uidRec != null ? uidRec.uid : uid; |
| mPendingUidChanges.add(pendingChange); |
| } else { |
| pendingChange = uidRec.pendingChange; |
| // If there is no change in idle or active state, then keep whatever was pending. |
| if ((change & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) { |
| change |= (pendingChange.change & (UidRecord.CHANGE_IDLE |
| | UidRecord.CHANGE_ACTIVE)); |
| } |
| // If there is no change in cached or uncached state, then keep whatever was pending. |
| if ((change & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) { |
| change |= (pendingChange.change & (UidRecord.CHANGE_CACHED |
| | UidRecord.CHANGE_UNCACHED)); |
| } |
| // If this is a report of the UID being gone, then we shouldn't keep any previous |
| // report of it being active or cached. (That is, a gone uid is never active, |
| // and never cached.) |
| if ((change & UidRecord.CHANGE_GONE) != 0) { |
| change &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED); |
| if (!uidRec.idle) { |
| // If this uid is going away, and we haven't yet reported it is gone, |
| // then do so now. |
| change |= UidRecord.CHANGE_IDLE; |
| } |
| } |
| } |
| pendingChange.change = change; |
| pendingChange.processState = uidRec != null |
| ? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT; |
| pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid); |
| pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0; |
| if (uidRec != null) { |
| uidRec.lastReportedChange = change; |
| uidRec.updateLastDispatchedProcStateSeq(change); |
| } |
| |
| // Directly update the power manager, since we sit on top of it and it is critical |
| // it be kept in sync (so wake locks will be held as soon as appropriate). |
| if (mLocalPowerManager != null) { |
| // TO DO: dispatch cached/uncached changes here, so we don't need to report |
| // all proc state changes. |
| if ((change & UidRecord.CHANGE_ACTIVE) != 0) { |
| mLocalPowerManager.uidActive(pendingChange.uid); |
| } |
| if ((change & UidRecord.CHANGE_IDLE) != 0) { |
| mLocalPowerManager.uidIdle(pendingChange.uid); |
| } |
| if ((change & UidRecord.CHANGE_GONE) != 0) { |
| mLocalPowerManager.uidGone(pendingChange.uid); |
| } else { |
| mLocalPowerManager.updateUidProcState(pendingChange.uid, |
| pendingChange.processState); |
| } |
| } |
| } |
| |
| private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName, |
| String authority) { |
| if (app == null) return; |
| if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { |
| UserState userState = mUserController.getStartedUserState(app.userId); |
| if (userState == null) return; |
| final long now = SystemClock.elapsedRealtime(); |
| Long lastReported = userState.mProviderLastReportedFg.get(authority); |
| if (lastReported == null || lastReported < now - 60 * 1000L) { |
| if (mSystemReady) { |
| // Cannot touch the user stats if not system ready |
| mUsageStatsService.reportContentProviderUsage( |
| authority, providerPkgName, app.userId); |
| } |
| userState.mProviderLastReportedFg.put(authority, now); |
| } |
| } |
| } |
| |
| private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) { |
| if (DEBUG_USAGE_STATS) { |
| Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList()) |
| + "] state changes: old = " + app.setProcState + ", new = " |
| + app.curProcState); |
| } |
| if (mUsageStatsService == null) { |
| return; |
| } |
| boolean isInteraction; |
| // To avoid some abuse patterns, we are going to be careful about what we consider |
| // to be an app interaction. Being the top activity doesn't count while the display |
| // is sleeping, nor do short foreground services. |
| if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP) { |
| isInteraction = true; |
| app.fgInteractionTime = 0; |
| } else if (app.curProcState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { |
| if (app.fgInteractionTime == 0) { |
| app.fgInteractionTime = nowElapsed; |
| isInteraction = false; |
| } else { |
| isInteraction = nowElapsed > app.fgInteractionTime |
| + mConstants.SERVICE_USAGE_INTERACTION_TIME; |
| } |
| } else { |
| isInteraction = app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; |
| app.fgInteractionTime = 0; |
| } |
| if (isInteraction && (!app.reportedInteraction || (nowElapsed-app.interactionEventTime) |
| > mConstants.USAGE_STATS_INTERACTION_INTERVAL)) { |
| app.interactionEventTime = nowElapsed; |
| String[] packages = app.getPackageList(); |
| if (packages != null) { |
| for (int i = 0; i < packages.length; i++) { |
| mUsageStatsService.reportEvent(packages[i], app.userId, |
| UsageEvents.Event.SYSTEM_INTERACTION); |
| } |
| } |
| } |
| app.reportedInteraction = isInteraction; |
| if (!isInteraction) { |
| app.interactionEventTime = 0; |
| } |
| } |
| |
| private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) { |
| if (proc.thread != null) { |
| if (proc.baseProcessTracker != null) { |
| proc.baseProcessTracker.setState(proc.repProcState, memFactor, now, proc.pkgList); |
| } |
| } |
| } |
| |
| private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj, |
| ProcessRecord TOP_APP, boolean doingAll, long now) { |
| if (app.thread == null) { |
| return false; |
| } |
| |
| computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now); |
| |
| return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime()); |
| } |
| |
| @GuardedBy("this") |
| final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground, |
| boolean oomAdj) { |
| if (isForeground != proc.foregroundServices) { |
| proc.foregroundServices = isForeground; |
| ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName, |
| proc.info.uid); |
| if (isForeground) { |
| if (curProcs == null) { |
| curProcs = new ArrayList<ProcessRecord>(); |
| mForegroundPackages.put(proc.info.packageName, proc.info.uid, curProcs); |
| } |
| if (!curProcs.contains(proc)) { |
| curProcs.add(proc); |
| mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_FOREGROUND_START, |
| proc.info.packageName, proc.info.uid); |
| } |
| } else { |
| if (curProcs != null) { |
| if (curProcs.remove(proc)) { |
| mBatteryStatsService.noteEvent( |
| BatteryStats.HistoryItem.EVENT_FOREGROUND_FINISH, |
| proc.info.packageName, proc.info.uid); |
| if (curProcs.size() <= 0) { |
| mForegroundPackages.remove(proc.info.packageName, proc.info.uid); |
| } |
| } |
| } |
| } |
| if (oomAdj) { |
| updateOomAdjLocked(); |
| } |
| } |
| } |
| |
| private final ActivityRecord resumedAppLocked() { |
| final ActivityRecord act = |
| mStackSupervisor != null ? mStackSupervisor.getResumedActivityLocked() : null; |
| String pkg; |
| int uid; |
| if (act != null) { |
| pkg = act.packageName; |
| uid = act.info.applicationInfo.uid; |
| } else { |
| pkg = null; |
| uid = -1; |
| } |
| // Has the UID or resumed package name changed? |
| if (uid != mCurResumedUid || (pkg != mCurResumedPackage |
| && (pkg == null || !pkg.equals(mCurResumedPackage)))) { |
| if (mCurResumedPackage != null) { |
| mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_FINISH, |
| mCurResumedPackage, mCurResumedUid); |
| } |
| mCurResumedPackage = pkg; |
| mCurResumedUid = uid; |
| if (mCurResumedPackage != null) { |
| mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_TOP_START, |
| mCurResumedPackage, mCurResumedUid); |
| } |
| } |
| return act; |
| } |
| |
| /** |
| * Update OomAdj for a specific process. |
| * @param app The process to update |
| * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps |
| * if necessary, or skip. |
| * @return whether updateOomAdjLocked(app) was successful. |
| */ |
| @GuardedBy("this") |
| final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) { |
| final ActivityRecord TOP_ACT = resumedAppLocked(); |
| final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; |
| final boolean wasCached = app.cached; |
| |
| mAdjSeq++; |
| |
| // This is the desired cached adjusment we want to tell it to use. |
| // If our app is currently cached, we know it, and that is it. Otherwise, |
| // we don't know it yet, and it needs to now be cached we will then |
| // need to do a complete oom adj. |
| final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ |
| ? app.curRawAdj : ProcessList.UNKNOWN_ADJ; |
| boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false, |
| SystemClock.uptimeMillis()); |
| if (oomAdjAll |
| && (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ)) { |
| // Changed to/from cached state, so apps after it in the LRU |
| // list may also be changed. |
| updateOomAdjLocked(); |
| } |
| return success; |
| } |
| |
| @GuardedBy("this") |
| final void updateOomAdjLocked() { |
| final ActivityRecord TOP_ACT = resumedAppLocked(); |
| final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; |
| final long now = SystemClock.uptimeMillis(); |
| final long nowElapsed = SystemClock.elapsedRealtime(); |
| final long oldTime = now - ProcessList.MAX_EMPTY_TIME; |
| final int N = mLruProcesses.size(); |
| |
| if (false) { |
| RuntimeException e = new RuntimeException(); |
| e.fillInStackTrace(); |
| Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e); |
| } |
| |
| // Reset state in all uid records. |
| for (int i=mActiveUids.size()-1; i>=0; i--) { |
| final UidRecord uidRec = mActiveUids.valueAt(i); |
| if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, |
| "Starting update of " + uidRec); |
| uidRec.reset(); |
| } |
| |
| if (mStackSupervisor != null) { |
| mStackSupervisor.rankTaskLayersIfNeeded(); |
| } |
| |
| mAdjSeq++; |
| mNewNumServiceProcs = 0; |
| mNewNumAServiceProcs = 0; |
| |
| final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES; |
| final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit; |
| |
| // Let's determine how many processes we have running vs. |
| // how many slots we have for background processes; we may want |
| // to put multiple processes in a slot of there are enough of |
| // them. |
| int numSlots = (ProcessList.CACHED_APP_MAX_ADJ |
| - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2; |
| int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs; |
| if (numEmptyProcs > cachedProcessLimit) { |
| // If there are more empty processes than our limit on cached |
| // processes, then use the cached process limit for the factor. |
| // This ensures that the really old empty processes get pushed |
| // down to the bottom, so if we are running low on memory we will |
| // have a better chance at keeping around more cached processes |
| // instead of a gazillion empty processes. |
| numEmptyProcs = cachedProcessLimit; |
| } |
| int emptyFactor = numEmptyProcs/numSlots; |
| if (emptyFactor < 1) emptyFactor = 1; |
| int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots; |
| if (cachedFactor < 1) cachedFactor = 1; |
| int stepCached = 0; |
| int stepEmpty = 0; |
| int numCached = 0; |
| int numEmpty = 0; |
| int numTrimming = 0; |
| |
| mNumNonCachedProcs = 0; |
| mNumCachedHiddenProcs = 0; |
| |
| // First update the OOM adjustment for each of the |
| // application processes based on their current state. |
| int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ; |
| int nextCachedAdj = curCachedAdj+1; |
| int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ; |
| int nextEmptyAdj = curEmptyAdj+2; |
| |
| boolean retryCycles = false; |
| |
| // need to reset cycle state before calling computeOomAdjLocked because of service connections |
| for (int i=N-1; i>=0; i--) { |
| ProcessRecord app = mLruProcesses.get(i); |
| app.containsCycle = false; |
| } |
| for (int i=N-1; i>=0; i--) { |
| ProcessRecord app = mLruProcesses.get(i); |
| if (!app.killedByAm && app.thread != null) { |
| app.procStateChanged = false; |
| computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); |
| |
| // if any app encountered a cycle, we need to perform an additional loop later |
| retryCycles |= app.containsCycle; |
| |
| // If we haven't yet assigned the final cached adj |
| // to the process, do that now. |
| if (app.curAdj >= ProcessList.UNKNOWN_ADJ) { |
| switch (app.curProcState) { |
| case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: |
| case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: |
| case ActivityManager.PROCESS_STATE_CACHED_RECENT: |
| // This process is a cached process holding activities... |
| // assign it the next cached value for that type, and then |
| // step that cached level. |
| app.curRawAdj = curCachedAdj; |
| app.curAdj = app.modifyRawOomAdj(curCachedAdj); |
| if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i |
| + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj |
| + ")"); |
| if (curCachedAdj != nextCachedAdj) { |
| stepCached++; |
| if (stepCached >= cachedFactor) { |
| stepCached = 0; |
| curCachedAdj = nextCachedAdj; |
| nextCachedAdj += 2; |
| if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { |
| nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; |
| } |
| } |
| } |
| break; |
| default: |
| // For everything else, assign next empty cached process |
| // level and bump that up. Note that this means that |
| // long-running services that have dropped down to the |
| // cached level will be treated as empty (since their process |
| // state is still as a service), which is what we want. |
| app.curRawAdj = curEmptyAdj; |
| app.curAdj = app.modifyRawOomAdj(curEmptyAdj); |
| if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i |
| + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj |
| + ")"); |
| if (curEmptyAdj != nextEmptyAdj) { |
| stepEmpty++; |
| if (stepEmpty >= emptyFactor) { |
| stepEmpty = 0; |
| curEmptyAdj = nextEmptyAdj; |
| nextEmptyAdj += 2; |
| if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) { |
| nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ; |
| } |
| } |
| } |
| break; |
| } |
| } |
| |
| |
| } |
| } |
| |
| // Cycle strategy: |
| // - Retry computing any process that has encountered a cycle. |
| // - Continue retrying until no process was promoted. |
| // - Iterate from least important to most important. |
| int cycleCount = 0; |
| while (retryCycles) { |
| cycleCount++; |
| retryCycles = false; |
| |
| for (int i=0; i<N; i++) { |
| ProcessRecord app = mLruProcesses.get(i); |
| if (!app.killedByAm && app.thread != null && app.containsCycle == true) { |
| app.adjSeq--; |
| app.completedAdjSeq--; |
| } |
| } |
| |
| for (int i=0; i<N; i++) { |
| ProcessRecord app = mLruProcesses.get(i); |
| if (!app.killedByAm && app.thread != null && app.containsCycle == true) { |
| if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) { |
| retryCycles = true; |
| } |
| } |
| } |
| } |
| for (int i=N-1; i>=0; i--) { |
| ProcessRecord app = mLruProcesses.get(i); |
| if (!app.killedByAm && app.thread != null) { |
| applyOomAdjLocked(app, true, now, nowElapsed); |
| |
| // Count the number of process types. |
| switch (app.curProcState) { |
| case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: |
| case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: |
| mNumCachedHiddenProcs++; |
| numCached++; |
| if (numCached > cachedProcessLimit) { |
| app.kill("cached #" + numCached, true); |
| } |
| break; |
| case ActivityManager.PROCESS_STATE_CACHED_EMPTY: |
| if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES |
| && app.lastActivityTime < oldTime) { |
| app.kill("empty for " |
| + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime) |
| / 1000) + "s", true); |
| } else { |
| numEmpty++; |
| if (numEmpty > emptyProcessLimit) { |
| app.kill("empty #" + numEmpty, true); |
| } |
| } |
| break; |
| default: |
| mNumNonCachedProcs++; |
| break; |
| } |
| |
| if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) { |
| // If this is an isolated process, there are no services |
| // running in it, and it's not a special process with a |
| // custom entry point, then the process is no longer |
| // needed. We agressively kill these because we can by |
| // definition not re-use the same process again, and it is |
| // good to avoid having whatever code was running in them |
| // left sitting around after no longer needed. |
| app.kill("isolated not needed", true); |
| } else { |
| // Keeping this process, update its uid. |
| final UidRecord uidRec = app.uidRecord; |
| if (uidRec != null) { |
| uidRec.ephemeral = app.info.isInstantApp(); |
| if (uidRec.curProcState > app.curProcState) { |
| uidRec.curProcState = app.curProcState; |
| } |
| if (app.foregroundServices) { |
| uidRec.foregroundServices = true; |
| } |
| } |
| } |
| |
| if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME |
| && !app.killedByAm) { |
| numTrimming++; |
| } |
| } |
| } |
| |
| incrementProcStateSeqAndNotifyAppsLocked(); |
| |
| mNumServiceProcs = mNewNumServiceProcs; |
| |
| // Now determine the memory trimming level of background processes. |
| // Unfortunately we need to start at the back of the list to do this |
| // properly. We only do this if the number of background apps we |
| // are managing to keep around is less than half the maximum we desire; |
| // if we are keeping a good number around, we'll let them use whatever |
| // memory they want. |
| final int numCachedAndEmpty = numCached + numEmpty; |
| int memFactor; |
| if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES |
| && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) { |
| if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { |
| memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL; |
| } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { |
| memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW; |
| } else { |
| memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE; |
| } |
| } else { |
| memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL; |
| } |
| // We always allow the memory level to go up (better). We only allow it to go |
| // down if we are in a state where that is allowed, *and* the total number of processes |
| // has gone down since last time. |
| if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor |
| + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel |
| + " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses); |
| if (memFactor > mLastMemoryLevel) { |
| if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) { |
| memFactor = mLastMemoryLevel; |
| if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!"); |
| } |
| } |
| if (memFactor != mLastMemoryLevel) { |
| EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel); |
| } |
| mLastMemoryLevel = memFactor; |
| mLastNumProcesses = mLruProcesses.size(); |
| boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleepingLocked(), now); |
| final int trackerMemFactor = mProcessStats.getMemFactorLocked(); |
| if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) { |
| if (mLowRamStartTime == 0) { |
| mLowRamStartTime = now; |
| } |
| int step = 0; |
| int fgTrimLevel; |
| switch (memFactor) { |
| case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: |
| fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; |
| break; |
| case ProcessStats.ADJ_MEM_FACTOR_LOW: |
| fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; |
| break; |
| default: |
| fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; |
| break; |
| } |
| int factor = numTrimming/3; |
| int minFactor = 2; |
| if (mHomeProcess != null) minFactor++; |
| if (mPreviousProcess != null) minFactor++; |
| if (factor < minFactor) factor = minFactor; |
| int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; |
| for (int i=N-1; i>=0; i--) { |
| ProcessRecord app = mLruProcesses.get(i); |
| if (allChanged || app.procStateChanged) { |
| setProcessTrackerStateLocked(app, trackerMemFactor, now); |
| app.procStateChanged = false; |
| } |
| if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME |
| && !app.killedByAm) { |
| if (app.trimMemoryLevel < curLevel && app.thread != null) { |
| try { |
| if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, |
| "Trimming memory of " + app.processName + " to " + curLevel); |
| app.thread.scheduleTrimMemory(curLevel); |
| } catch (RemoteException e) { |
| } |
| if (false) { |
| // For now we won't do this; our memory trimming seems |
| // to be good enough at this point that destroying |
| // activities causes more harm than good. |
| if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE |
| && app != mHomeProcess && app != mPreviousProcess) { |
| // Need to do this on its own message because the stack may not |
| // be in a consistent state at this point. |
| // For these apps we will also finish their activities |
| // to help them free memory. |
| mStackSupervisor.scheduleDestroyAllActivities(app, "trim"); |
| } |
| } |
| } |
| app.trimMemoryLevel = curLevel; |
| step++; |
| if (step >= factor) { |
| step = 0; |
| switch (curLevel) { |
| case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: |
| curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE; |
| break; |
| case ComponentCallbacks2.TRIM_MEMORY_MODERATE: |
| curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; |
| break; |
| } |
| } |
| } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT |
| && !app.killedByAm) { |
| if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND |
| && app.thread != null) { |
| try { |
| if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, |
| "Trimming memory of heavy-weight " + app.processName |
| + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); |
| app.thread.scheduleTrimMemory( |
| ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); |
| } catch (RemoteException e) { |
| } |
| } |
| app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; |
| } else { |
| if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND |
| || app.systemNoUi) && app.pendingUiClean) { |
| // If this application is now in the background and it |
| // had done UI, then give it the special trim level to |
| // have it free UI resources. |
| final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; |
| if (app.trimMemoryLevel < level && app.thread != null) { |
| try { |
| if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, |
| "Trimming memory of bg-ui " + app.processName |
| + " to " + level); |
| app.thread.scheduleTrimMemory(level); |
| } catch (RemoteException e) { |
| } |
| } |
| app.pendingUiClean = false; |
| } |
| if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) { |
| try { |
| if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, |
| "Trimming memory of fg " + app.processName |
| + " to " + fgTrimLevel); |
| app.thread.scheduleTrimMemory(fgTrimLevel); |
| } catch (RemoteException e) { |
| } |
| } |
| app.trimMemoryLevel = fgTrimLevel; |
| } |
| } |
| } else { |
| if (mLowRamStartTime != 0) { |
| mLowRamTimeSinceLastIdle += now - mLowRamStartTime; |
| mLowRamStartTime = 0; |
| } |
| for (int i=N-1; i>=0; i--) { |
| ProcessRecord app = mLruProcesses.get(i); |
| if (allChanged || app.procStateChanged) { |
| setProcessTrackerStateLocked(app, trackerMemFactor, now); |
| app.procStateChanged = false; |
| } |
| if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND |
| || app.systemNoUi) && app.pendingUiClean) { |
| if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN |
| && app.thread != null) { |
| try { |
| if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, |
| "Trimming memory of ui hidden " + app.processName |
| + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); |
| app.thread.scheduleTrimMemory( |
| ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); |
| } catch (RemoteException e) { |
| } |
| } |
| app.pendingUiClean = false; |
| } |
| app.trimMemoryLevel = 0; |
| } |
| } |
| |
| if (mAlwaysFinishActivities) { |
| // Need to do this on its own message because the stack may not |
| // be in a consistent state at this point. |
| mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish"); |
| } |
| |
| if (allChanged) { |
| requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered()); |
| } |
| |
| ArrayList<UidRecord> becameIdle = null; |
| |
| // Update from any uid changes. |
| if (mLocalPowerManager != null) { |
| mLocalPowerManager.startUidChanges(); |
| } |
| for (int i=mActiveUids.size()-1; i>=0; i--) { |
| final UidRecord uidRec = mActiveUids.valueAt(i); |
| int uidChange = UidRecord.CHANGE_PROCSTATE; |
| if (uidRec.curProcState != ActivityManager.PROCESS_STATE_NONEXISTENT |
| && (uidRec.setProcState != uidRec.curProcState |
| || uidRec.setWhitelist != uidRec.curWhitelist)) { |
| if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, |
| "Changes in " + uidRec + ": proc state from " + uidRec.setProcState |
| + " to " + uidRec.curProcState + ", whitelist from " + uidRec.setWhitelist |
| + " to " + uidRec.curWhitelist); |
| if (ActivityManager.isProcStateBackground(uidRec.curProcState) |
| && !uidRec.curWhitelist) { |
| // UID is now in the background (and not on the temp whitelist). Was it |
| // previously in the foreground (or on the temp whitelist)? |
| if (!ActivityManager.isProcStateBackground(uidRec.setProcState) |
| || uidRec.setWhitelist) { |
| uidRec.lastBackgroundTime = nowElapsed; |
| if (!mHandler.hasMessages(IDLE_UIDS_MSG)) { |
| // Note: the background settle time is in elapsed realtime, while |
| // the handler time base is uptime. All this means is that we may |
| // stop background uids later than we had intended, but that only |
| // happens because the device was sleeping so we are okay anyway. |
| mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, |
| mConstants.BACKGROUND_SETTLE_TIME); |
| } |
| } |
| if (uidRec.idle && !uidRec.setIdle) { |
| uidChange = UidRecord.CHANGE_IDLE; |
| if (becameIdle == null) { |
| becameIdle = new ArrayList<>(); |
| } |
| becameIdle.add(uidRec); |
| } |
| } else { |
| if (uidRec.idle) { |
| uidChange = UidRecord.CHANGE_ACTIVE; |
| EventLogTags.writeAmUidActive(uidRec.uid); |
| uidRec.idle = false; |
| } |
| uidRec.lastBackgroundTime = 0; |
| } |
| final boolean wasCached = uidRec.setProcState |
| > ActivityManager.PROCESS_STATE_RECEIVER; |
| final boolean isCached = uidRec.curProcState |
| > ActivityManager.PROCESS_STATE_RECEIVER; |
| if (wasCached != isCached || |
| uidRec.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) { |
| uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED; |
| } |
| uidRec.setProcState = uidRec.curProcState; |
| uidRec.setWhitelist = uidRec.curWhitelist; |
| uidRec.setIdle = uidRec.idle; |
| enqueueUidChangeLocked(uidRec, -1, uidChange); |
| noteUidProcessState(uidRec.uid, uidRec.curProcState); |
| if (uidRec.foregroundServices) { |
| mServices.foregroundServiceProcStateChangedLocked(uidRec); |
| } |
| } |
| } |
| if (mLocalPowerManager != null) { |
| mLocalPowerManager.finishUidChanges(); |
| } |
| |
| if (becameIdle != null) { |
| // If we have any new uids that became idle this time, we need to make sure |
| // they aren't left with running services. |
| for (int i = becameIdle.size() - 1; i >= 0; i--) { |
| mServices.stopInBackgroundLocked(becameIdle.get(i).uid); |
| } |
| } |
| |
| if (mProcessStats.shouldWriteNowLocked(now)) { |
| mHandler.post(new Runnable() { |
| @Override public void run() { |
| synchronized (ActivityManagerService.this) { |
| mProcessStats.writeStateAsyncLocked(); |
| } |
| } |
| }); |
| } |
| |
| if (DEBUG_OOM_ADJ) { |
| final long duration = SystemClock.uptimeMillis() - now; |
| if (false) { |
| Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms", |
| new RuntimeException("here").fillInStackTrace()); |
| } else { |
| Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms"); |
| } |
| } |
| } |
| |
| @Override |
| public void makePackageIdle(String packageName, int userId) { |
| if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) |
| != PackageManager.PERMISSION_GRANTED) { |
| String msg = "Permission Denial: makePackageIdle() 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, "makePackageIdle", null); |
| long callingId = Binder.clearCallingIdentity(); |
| synchronized(this) { |
| try { |
| IPackageManager pm = AppGlobals.getPackageManager(); |
| int pkgUid = -1; |
| try { |
| pkgUid = pm.getPackageUid(packageName, MATCH_UNINSTALLED_PACKAGES |
| | MATCH_DEBUG_TRIAGED_MISSING, UserHandle.USER_SYSTEM); |
| } catch (RemoteException e) { |
| } |
| if (pkgUid == -1) { |
| throw new IllegalArgumentException("Unknown package name " + packageName); |
| } |
| |
| if (mLocalPowerManager != null) { |
| mLocalPowerManager.startUidChanges(); |
| } |
| final int appId = UserHandle.getAppId(pkgUid); |
| final int N = mActiveUids.size(); |
| for (int i=N-1; i>=0; i--) { |
| final UidRecord uidRec = mActiveUids.valueAt(i); |
| final long bgTime = uidRec.lastBackgroundTime; |
| if (bgTime > 0 && !uidRec.idle) { |
| if (UserHandle.getAppId(uidRec.uid) == appId) { |
| if (userId == UserHandle.USER_ALL || |
| userId == UserHandle.getUserId(uidRec.uid)) { |
| EventLogTags.writeAmUidIdle(uidRec.uid); |
| uidRec.idle = true; |
| uidRec.setIdle = true; |
| Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid) |
| + " from package " + packageName + " user " + userId); |
| doStopUidLocked(uidRec.uid, uidRec); |
| } |
| } |
| } |
| } |
| } finally { |
| if (mLocalPowerManager != null) { |
| mLocalPowerManager.finishUidChanges(); |
| } |
| Binder.restoreCallingIdentity(callingId); |
| } |
| } |
| } |
| |
| final void idleUids() { |
| synchronized (this) { |
| final int N = mActiveUids.size(); |
| if (N <= 0) { |
| return; |
| } |
| final long nowElapsed = SystemClock.elapsedRealtime(); |
| final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME; |
| long nextTime = 0; |
| if (mLocalPowerManager != null) { |
| mLocalPowerManager.startUidChanges(); |
| } |
| for (int i=N-1; i>=0; i--) { |
| final UidRecord uidRec = mActiveUids.valueAt(i); |
| final long bgTime = uidRec.lastBackgroundTime; |
| if (bgTime > 0 && !uidRec.idle) { |
| if (bgTime <= maxBgTime) { |
| EventLogTags.writeAmUidIdle(uidRec.uid); |
| uidRec.idle = true; |
| uidRec.setIdle = true; |
| doStopUidLocked(uidRec.uid, uidRec); |
| } else { |
| if (nextTime == 0 || nextTime > bgTime) { |
| nextTime = bgTime; |
| } |
| } |
| } |
| } |
| if (mLocalPowerManager != null) { |
| mLocalPowerManager.finishUidChanges(); |
| } |
| if (nextTime > 0) { |
| mHandler.removeMessages(IDLE_UIDS_MSG); |
| mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, |
| nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed); |
| } |
| } |
| } |
| |
| /** |
| * Checks if any uid is coming from background to foreground or vice versa and if so, increments |
| * the {@link UidRecord#curProcStateSeq} corresponding to that uid using global seq counter |
| * {@link #mProcStateSeqCounter} and notifies the app if it needs to block. |
| */ |
| @VisibleForTesting |
| @GuardedBy("this") |
| void incrementProcStateSeqAndNotifyAppsLocked() { |
| if (mWaitForNetworkTimeoutMs <= 0) { |
| return; |
| } |
| // Used for identifying which uids need to block for network. |
| ArrayList<Integer> blockingUids = null; |
| for (int i = mActiveUids.size() - 1; i >= 0; --i) { |
| final UidRecord uidRec = mActiveUids.valueAt(i); |
| // If the network is not restricted for uid, then nothing to do here. |
| if (!mInjector.isNetworkRestrictedForUid(uidRec.uid)) { |
| continue; |
| } |
| if (!UserHandle.isApp(uidRec.uid) || !uidRec.hasInternetPermission) { |
| continue; |
| } |
| // If process state is not changed, then there's nothing to do. |
| if (uidRec.setProcState == uidRec.curProcState) { |
| continue; |
| } |
| final int blockState = getBlockStateForUid(uidRec); |
| // No need to inform the app when the blockState is NETWORK_STATE_NO_CHANGE as |
| // there's nothing the app needs to do in this scenario. |
| if (blockState == NETWORK_STATE_NO_CHANGE) { |
| continue; |
| } |
| synchronized (uidRec.networkStateLock) { |
| uidRec.curProcStateSeq = ++mProcStateSeqCounter; |
| if (blockState == NETWORK_STATE_BLOCK) { |
| if (blockingUids == null) { |
| blockingUids = new ArrayList<>(); |
| } |
| blockingUids.add(uidRec.uid); |
| } else { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking" |
| + " threads for uid: " + uidRec); |
| } |
| if (uidRec.waitingForNetwork) { |
| uidRec.networkStateLock.notifyAll(); |
| } |
| } |
| } |
| } |
| |
| // There are no uids that need to block, so nothing more to do. |
| if (blockingUids == null) { |
| return; |
| } |
| |
| for (int i = mLruProcesses.size() - 1; i >= 0; --i) { |
| final ProcessRecord app = mLruProcesses.get(i); |
| if (!blockingUids.contains(app.uid)) { |
| continue; |
| } |
| if (!app.killedByAm && app.thread != null) { |
| final UidRecord uidRec = mActiveUids.get(app.uid); |
| try { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "Informing app thread that it needs to block: " |
| + uidRec); |
| } |
| app.thread.setNetworkBlockSeq(uidRec.curProcStateSeq); |
| } catch (RemoteException ignored) { |
| } |
| } |
| } |
| } |
| |
| /** |
| * Checks if the uid is coming from background to foreground or vice versa and returns |
| * appropriate block state based on this. |
| * |
| * @return blockState based on whether the uid is coming from background to foreground or |
| * vice versa. If bg->fg or fg->bg, then {@link #NETWORK_STATE_BLOCK} or |
| * {@link #NETWORK_STATE_UNBLOCK} respectively, otherwise |
| * {@link #NETWORK_STATE_NO_CHANGE}. |
| */ |
| @VisibleForTesting |
| int getBlockStateForUid(UidRecord uidRec) { |
| // Denotes whether uid's process state is currently allowed network access. |
| final boolean isAllowed = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.curProcState) |
| || isProcStateAllowedWhileOnRestrictBackground(uidRec.curProcState); |
| // Denotes whether uid's process state was previously allowed network access. |
| final boolean wasAllowed = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.setProcState) |
| || isProcStateAllowedWhileOnRestrictBackground(uidRec.setProcState); |
| |
| // When the uid is coming to foreground, AMS should inform the app thread that it should |
| // block for the network rules to get updated before launching an activity. |
| if (!wasAllowed && isAllowed) { |
| return NETWORK_STATE_BLOCK; |
| } |
| // When the uid is going to background, AMS should inform the app thread that if an |
| // activity launch is blocked for the network rules to get updated, it should be unblocked. |
| if (wasAllowed && !isAllowed) { |
| return NETWORK_STATE_UNBLOCK; |
| } |
| return NETWORK_STATE_NO_CHANGE; |
| } |
| |
| final void runInBackgroundDisabled(int uid) { |
| synchronized (this) { |
| UidRecord uidRec = mActiveUids.get(uid); |
| if (uidRec != null) { |
| // This uid is actually running... should it be considered background now? |
| if (uidRec.idle) { |
| doStopUidLocked(uidRec.uid, uidRec); |
| } |
| } else { |
| // This uid isn't actually running... still send a report about it being "stopped". |
| doStopUidLocked(uid, null); |
| } |
| } |
| } |
| |
| /** |
| * Call {@link #doStopUidLocked} (which will also stop background services) for all idle UIDs. |
| */ |
| void doStopUidForIdleUidsLocked() { |
| final int size = mActiveUids.size(); |
| for (int i = 0; i < size; i++) { |
| final int uid = mActiveUids.keyAt(i); |
| if (UserHandle.isCore(uid)) { |
| continue; |
| } |
| final UidRecord uidRec = mActiveUids.valueAt(i); |
| if (!uidRec.idle) { |
| continue; |
| } |
| doStopUidLocked(uidRec.uid, uidRec); |
| } |
| } |
| |
| final void doStopUidLocked(int uid, final UidRecord uidRec) { |
| mServices.stopInBackgroundLocked(uid); |
| enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE); |
| } |
| |
| /** |
| * Whitelists {@code targetUid} to temporarily bypass Power Save mode. |
| */ |
| @GuardedBy("this") |
| void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid, |
| long duration, String tag) { |
| if (DEBUG_WHITELISTS) { |
| Slog.d(TAG, "tempWhitelistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", " |
| + targetUid + ", " + duration + ")"); |
| } |
| |
| synchronized (mPidsSelfLocked) { |
| final ProcessRecord pr = mPidsSelfLocked.get(callerPid); |
| if (pr == null) { |
| Slog.w(TAG, "tempWhitelistForPendingIntentLocked() no ProcessRecord for pid " |
| + callerPid); |
| return; |
| } |
| if (!pr.whitelistManager) { |
| if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid) |
| != PackageManager.PERMISSION_GRANTED) { |
| if (DEBUG_WHITELISTS) { |
| Slog.d(TAG, "tempWhitelistForPendingIntentLocked() for target " + targetUid |
| + ": pid " + callerPid + " is not allowed"); |
| } |
| return; |
| } |
| } |
| } |
| |
| tempWhitelistUidLocked(targetUid, duration, tag); |
| } |
| |
| /** |
| * Whitelists {@code targetUid} to temporarily bypass Power Save mode. |
| */ |
| @GuardedBy("this") |
| void tempWhitelistUidLocked(int targetUid, long duration, String tag) { |
| mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag)); |
| setUidTempWhitelistStateLocked(targetUid, true); |
| mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget(); |
| } |
| |
| void pushTempWhitelist() { |
| final int N; |
| final PendingTempWhitelist[] list; |
| |
| // First copy out the pending changes... we need to leave them in the map for now, |
| // in case someone needs to check what is coming up while we don't have the lock held. |
| synchronized(this) { |
| N = mPendingTempWhitelist.size(); |
| list = new PendingTempWhitelist[N]; |
| for (int i = 0; i < N; i++) { |
| list[i] = mPendingTempWhitelist.valueAt(i); |
| } |
| } |
| |
| // Now safely dispatch changes to device idle controller. |
| for (int i = 0; i < N; i++) { |
| PendingTempWhitelist ptw = list[i]; |
| mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid, |
| ptw.duration, true, ptw.tag); |
| } |
| |
| // And now we can safely remove them from the map. |
| synchronized(this) { |
| for (int i = 0; i < N; i++) { |
| PendingTempWhitelist ptw = list[i]; |
| int index = mPendingTempWhitelist.indexOfKey(ptw.targetUid); |
| if (index >= 0 && mPendingTempWhitelist.valueAt(index) == ptw) { |
| mPendingTempWhitelist.removeAt(index); |
| } |
| } |
| } |
| } |
| |
| @GuardedBy("this") |
| final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) { |
| boolean changed = false; |
| for (int i=mActiveUids.size()-1; i>=0; i--) { |
| final UidRecord uidRec = mActiveUids.valueAt(i); |
| if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) { |
| uidRec.curWhitelist = onWhitelist; |
| changed = true; |
| } |
| } |
| if (changed) { |
| updateOomAdjLocked(); |
| } |
| } |
| |
| @GuardedBy("this") |
| final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) { |
| boolean changed = false; |
| final UidRecord uidRec = mActiveUids.get(uid); |
| if (uidRec != null && uidRec.curWhitelist != onWhitelist) { |
| uidRec.curWhitelist = onWhitelist; |
| updateOomAdjLocked(); |
| } |
| } |
| |
| final void trimApplications() { |
| synchronized (this) { |
| trimApplicationsLocked(); |
| } |
| } |
| |
| final void trimApplicationsLocked() { |
| // First remove any unused application processes whose package |
| // has been removed. |
| for (int i=mRemovedProcesses.size()-1; i>=0; i--) { |
| final ProcessRecord app = mRemovedProcesses.get(i); |
| if (app.activities.size() == 0 && app.recentTasks.size() == 0 |
| && app.curReceivers.isEmpty() && app.services.size() == 0) { |
| Slog.i( |
| TAG, "Exiting empty application process " |
| + app.toShortString() + " (" |
| + (app.thread != null ? app.thread.asBinder() : null) |
| + ")\n"); |
| if (app.pid > 0 && app.pid != MY_PID) { |
| app.kill("empty", false); |
| } else if (app.thread != null) { |
| try { |
| app.thread.scheduleExit(); |
| } catch (Exception e) { |
| // Ignore exceptions. |
| } |
| } |
| cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/); |
| mRemovedProcesses.remove(i); |
| |
| if (app.persistent) { |
| addAppLocked(app.info, null, false, null /* ABI override */); |
| } |
| } |
| } |
| |
| // Now update the oom adj for all processes. Don't skip this, since other callers |
| // might be depending on it. |
| updateOomAdjLocked(); |
| } |
| |
| /** This method sends the specified signal to each of the persistent apps */ |
| public void signalPersistentProcesses(int sig) throws RemoteException { |
| if (sig != SIGNAL_USR1) { |
| throw new SecurityException("Only SIGNAL_USR1 is allowed"); |
| } |
| |
| synchronized (this) { |
| if (checkCallingPermission(android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Requires permission " |
| + android.Manifest.permission.SIGNAL_PERSISTENT_PROCESSES); |
| } |
| |
| for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { |
| ProcessRecord r = mLruProcesses.get(i); |
| if (r.thread != null && r.persistent) { |
| sendSignal(r.pid, sig); |
| } |
| } |
| } |
| } |
| |
| private void stopProfilerLocked(ProcessRecord proc, int profileType) { |
| if (proc == null || proc == mProfileProc) { |
| proc = mProfileProc; |
| profileType = mProfileType; |
| clearProfilerLocked(); |
| } |
| if (proc == null) { |
| return; |
| } |
| try { |
| proc.thread.profilerControl(false, null, profileType); |
| } catch (RemoteException e) { |
| throw new IllegalStateException("Process disappeared"); |
| } |
| } |
| |
| void clearProfilerLocked() { |
| if (mProfilerInfo !=null && mProfilerInfo.profileFd != null) { |
| try { |
| mProfilerInfo.profileFd.close(); |
| } catch (IOException e) { |
| } |
| } |
| mProfileApp = null; |
| mProfileProc = null; |
| mProfilerInfo = null; |
| } |
| |
| public boolean profileControl(String process, int userId, boolean start, |
| ProfilerInfo profilerInfo, int profileType) throws RemoteException { |
| |
| try { |
| 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 (start && (profilerInfo == null || profilerInfo.profileFd == null)) { |
| throw new IllegalArgumentException("null profile info or fd"); |
| } |
| |
| ProcessRecord proc = null; |
| if (process != null) { |
| proc = findProcessLocked(process, userId, "profileControl"); |
| } |
| |
| if (start && (proc == null || proc.thread == null)) { |
| throw new IllegalArgumentException("Unknown process: " + process); |
| } |
| |
| if (start) { |
| stopProfilerLocked(null, 0); |
| setProfileApp(proc.info, proc.processName, profilerInfo); |
| mProfileProc = proc; |
| mProfileType = profileType; |
| ParcelFileDescriptor fd = profilerInfo.profileFd; |
| try { |
| fd = fd.dup(); |
| } catch (IOException e) { |
| fd = null; |
| } |
| profilerInfo.profileFd = fd; |
| proc.thread.profilerControl(start, profilerInfo, profileType); |
| fd = null; |
| try { |
| mProfilerInfo.profileFd.close(); |
| } catch (IOException e) { |
| } |
| mProfilerInfo.profileFd = null; |
| |
| if (proc.pid == MY_PID) { |
| // When profiling the system server itself, avoid closing the file |
| // descriptor, as profilerControl will not create a copy. |
| // Note: it is also not correct to just set profileFd to null, as the |
| // whole ProfilerInfo instance is passed down! |
| profilerInfo = null; |
| } |
| } else { |
| stopProfilerLocked(proc, profileType); |
| if (profilerInfo != null && profilerInfo.profileFd != null) { |
| try { |
| profilerInfo.profileFd.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| |
| return true; |
| } |
| } catch (RemoteException e) { |
| throw new IllegalStateException("Process disappeared"); |
| } finally { |
| if (profilerInfo != null && profilerInfo.profileFd != null) { |
| try { |
| profilerInfo.profileFd.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| |
| private ProcessRecord findProcessLocked(String process, int userId, String callName) { |
| userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), |
| userId, true, ALLOW_FULL_ONLY, callName, null); |
| ProcessRecord proc = null; |
| try { |
| int pid = Integer.parseInt(process); |
| synchronized (mPidsSelfLocked) { |
| proc = mPidsSelfLocked.get(pid); |
| } |
| } catch (NumberFormatException e) { |
| } |
| |
| if (proc == null) { |
| ArrayMap<String, SparseArray<ProcessRecord>> all |
| = mProcessNames.getMap(); |
| SparseArray<ProcessRecord> procs = all.get(process); |
| if (procs != null && procs.size() > 0) { |
| proc = procs.valueAt(0); |
| if (userId != UserHandle.USER_ALL && proc.userId != userId) { |
| for (int i=1; i<procs.size(); i++) { |
| ProcessRecord thisProc = procs.valueAt(i); |
| if (thisProc.userId == userId) { |
| proc = thisProc; |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| return proc; |
| } |
| |
| @Override |
| public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo, |
| boolean runGc, String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) { |
| |
| try { |
| synchronized (this) { |
| // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to |
| // its own permission (same as profileControl). |
| if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Requires permission " |
| + android.Manifest.permission.SET_ACTIVITY_WATCHER); |
| } |
| |
| if (fd == null) { |
| throw new IllegalArgumentException("null fd"); |
| } |
| |
| ProcessRecord proc = findProcessLocked(process, userId, "dumpHeap"); |
| if (proc == null || proc.thread == null) { |
| throw new IllegalArgumentException("Unknown process: " + process); |
| } |
| |
| boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); |
| if (!isDebuggable) { |
| if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { |
| throw new SecurityException("Process not debuggable: " + proc); |
| } |
| } |
| |
| proc.thread.dumpHeap(managed, mallocInfo, runGc, path, fd, finishCallback); |
| fd = null; |
| return true; |
| } |
| } catch (RemoteException e) { |
| throw new IllegalStateException("Process disappeared"); |
| } finally { |
| if (fd != null) { |
| try { |
| fd.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize, |
| String reportPackage) { |
| if (processName != null) { |
| enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP, |
| "setDumpHeapDebugLimit()"); |
| } else { |
| synchronized (mPidsSelfLocked) { |
| ProcessRecord proc = mPidsSelfLocked.get(Binder.getCallingPid()); |
| if (proc == null) { |
| throw new SecurityException("No process found for calling pid " |
| + Binder.getCallingPid()); |
| } |
| if (!Build.IS_DEBUGGABLE |
| && (proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { |
| throw new SecurityException("Not running a debuggable build"); |
| } |
| processName = proc.processName; |
| uid = proc.uid; |
| if (reportPackage != null && !proc.pkgList.containsKey(reportPackage)) { |
| throw new SecurityException("Package " + reportPackage + " is not running in " |
| + proc); |
| } |
| } |
| } |
| synchronized (this) { |
| if (maxMemSize > 0) { |
| mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage)); |
| } else { |
| if (uid != 0) { |
| mMemWatchProcesses.remove(processName, uid); |
| } else { |
| mMemWatchProcesses.getMap().remove(processName); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void dumpHeapFinished(String path) { |
| synchronized (this) { |
| if (Binder.getCallingPid() != mMemWatchDumpPid) { |
| Slog.w(TAG, "dumpHeapFinished: Calling pid " + Binder.getCallingPid() |
| + " does not match last pid " + mMemWatchDumpPid); |
| return; |
| } |
| if (mMemWatchDumpFile == null || !mMemWatchDumpFile.equals(path)) { |
| Slog.w(TAG, "dumpHeapFinished: Calling path " + path |
| + " does not match last path " + mMemWatchDumpFile); |
| return; |
| } |
| if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path); |
| mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG); |
| |
| // Forced gc to clean up the remnant hprof fd. |
| Runtime.getRuntime().gc(); |
| } |
| } |
| |
| /** In this method we try to acquire our lock to make sure that we have not deadlocked */ |
| public void monitor() { |
| synchronized (this) { } |
| } |
| |
| void onCoreSettingsChange(Bundle settings) { |
| for (int i = mLruProcesses.size() - 1; i >= 0; i--) { |
| ProcessRecord processRecord = mLruProcesses.get(i); |
| try { |
| if (processRecord.thread != null) { |
| processRecord.thread.setCoreSettings(settings); |
| } |
| } catch (RemoteException re) { |
| /* ignore */ |
| } |
| } |
| } |
| |
| // Multi-user methods |
| |
| /** |
| * Start user, if its not already running, but don't bring it to foreground. |
| */ |
| @Override |
| public boolean startUserInBackground(final int userId) { |
| return startUserInBackgroundWithListener(userId, null); |
| } |
| |
| @Override |
| public boolean startUserInBackgroundWithListener(final int userId, |
| @Nullable IProgressListener unlockListener) { |
| return mUserController.startUser(userId, /* foreground */ false, unlockListener); |
| } |
| |
| @Override |
| public boolean unlockUser(int userId, byte[] token, byte[] secret, IProgressListener listener) { |
| return mUserController.unlockUser(userId, token, secret, listener); |
| } |
| |
| @Override |
| public boolean switchUser(final int targetUserId) { |
| return mUserController.switchUser(targetUserId); |
| } |
| |
| @Override |
| public int stopUser(final int userId, boolean force, final IStopUserCallback callback) { |
| return mUserController.stopUser(userId, force, callback); |
| } |
| |
| @Override |
| public UserInfo getCurrentUser() { |
| return mUserController.getCurrentUser(); |
| } |
| |
| String getStartedUserState(int userId) { |
| final UserState userState = mUserController.getStartedUserState(userId); |
| return UserState.stateToString(userState.state); |
| } |
| |
| @Override |
| public boolean isUserRunning(int userId, int flags) { |
| if (!mUserController.isSameProfileGroup(userId, UserHandle.getCallingUserId()) |
| && checkCallingPermission(INTERACT_ACROSS_USERS) |
| != PackageManager.PERMISSION_GRANTED) { |
| String msg = "Permission Denial: isUserRunning() from pid=" |
| + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingUid() |
| + " requires " + INTERACT_ACROSS_USERS; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| return mUserController.isUserRunning(userId, flags); |
| } |
| |
| @Override |
| public int[] getRunningUserIds() { |
| if (checkCallingPermission(INTERACT_ACROSS_USERS) |
| != PackageManager.PERMISSION_GRANTED) { |
| String msg = "Permission Denial: isUserRunning() from pid=" |
| + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingUid() |
| + " requires " + INTERACT_ACROSS_USERS; |
| Slog.w(TAG, msg); |
| throw new SecurityException(msg); |
| } |
| return mUserController.getStartedUserArray(); |
| } |
| |
| @Override |
| public void registerUserSwitchObserver(IUserSwitchObserver observer, String name) { |
| mUserController.registerUserSwitchObserver(observer, name); |
| } |
| |
| @Override |
| public void unregisterUserSwitchObserver(IUserSwitchObserver observer) { |
| mUserController.unregisterUserSwitchObserver(observer); |
| } |
| |
| ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) { |
| if (info == null) return null; |
| ApplicationInfo newInfo = new ApplicationInfo(info); |
| newInfo.initForUser(userId); |
| return newInfo; |
| } |
| |
| public boolean isUserStopped(int userId) { |
| return mUserController.getStartedUserState(userId) == null; |
| } |
| |
| ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) { |
| if (aInfo == null |
| || (userId < 1 && aInfo.applicationInfo.uid < UserHandle.PER_USER_RANGE)) { |
| return aInfo; |
| } |
| |
| ActivityInfo info = new ActivityInfo(aInfo); |
| info.applicationInfo = getAppInfoForUser(info.applicationInfo, userId); |
| return info; |
| } |
| |
| private boolean processSanityChecksLocked(ProcessRecord process) { |
| if (process == null || process.thread == null) { |
| return false; |
| } |
| |
| boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); |
| if (!isDebuggable) { |
| if ((process.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| public boolean startBinderTracking() throws RemoteException { |
| synchronized (this) { |
| mBinderTransactionTrackingEnabled = true; |
| // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own |
| // permission (same as profileControl). |
| if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Requires permission " |
| + android.Manifest.permission.SET_ACTIVITY_WATCHER); |
| } |
| |
| for (int i = 0; i < mLruProcesses.size(); i++) { |
| ProcessRecord process = mLruProcesses.get(i); |
| if (!processSanityChecksLocked(process)) { |
| continue; |
| } |
| try { |
| process.thread.startBinderTracking(); |
| } catch (RemoteException e) { |
| Log.v(TAG, "Process disappared"); |
| } |
| } |
| return true; |
| } |
| } |
| |
| public boolean stopBinderTrackingAndDump(ParcelFileDescriptor fd) throws RemoteException { |
| try { |
| synchronized (this) { |
| mBinderTransactionTrackingEnabled = false; |
| // TODO: hijacking SET_ACTIVITY_WATCHER, but should be changed to its own |
| // permission (same as profileControl). |
| if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) |
| != PackageManager.PERMISSION_GRANTED) { |
| throw new SecurityException("Requires permission " |
| + android.Manifest.permission.SET_ACTIVITY_WATCHER); |
| } |
| |
| if (fd == null) { |
| throw new IllegalArgumentException("null fd"); |
| } |
| |
| PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor())); |
| pw.println("Binder transaction traces for all processes.\n"); |
| for (ProcessRecord process : mLruProcesses) { |
| if (!processSanityChecksLocked(process)) { |
| continue; |
| } |
| |
| pw.println("Traces for process: " + process.processName); |
| pw.flush(); |
| try { |
| TransferPipe tp = new TransferPipe(); |
| try { |
| process.thread.stopBinderTrackingAndDump(tp.getWriteFd()); |
| tp.go(fd.getFileDescriptor()); |
| } finally { |
| tp.kill(); |
| } |
| } catch (IOException e) { |
| pw.println("Failure while dumping IPC traces from " + process + |
| ". Exception: " + e); |
| pw.flush(); |
| } catch (RemoteException e) { |
| pw.println("Got a RemoteException while dumping IPC traces from " + |
| process + ". Exception: " + e); |
| pw.flush(); |
| } |
| } |
| fd = null; |
| return true; |
| } |
| } finally { |
| if (fd != null) { |
| try { |
| fd.close(); |
| } catch (IOException e) { |
| } |
| } |
| } |
| } |
| |
| @VisibleForTesting |
| final class LocalService extends ActivityManagerInternal { |
| @Override |
| public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent, |
| int targetUserId) { |
| synchronized (ActivityManagerService.this) { |
| ActivityManagerService.this.grantUriPermissionFromIntentLocked(callingUid, |
| targetPkg, intent, null, targetUserId); |
| } |
| } |
| |
| @Override |
| public String checkContentProviderAccess(String authority, int userId) { |
| return ActivityManagerService.this.checkContentProviderAccess(authority, userId); |
| } |
| |
| @Override |
| public void onWakefulnessChanged(int wakefulness) { |
| ActivityManagerService.this.onWakefulnessChanged(wakefulness); |
| } |
| |
| @Override |
| public boolean startIsolatedProcess(String entryPoint, String[] entryPointArgs, |
| String processName, String abiOverride, int uid, Runnable crashHandler) { |
| return ActivityManagerService.this.startIsolatedProcess(entryPoint, entryPointArgs, |
| processName, abiOverride, uid, crashHandler); |
| } |
| |
| @Override |
| public void onUserRemoved(int userId) { |
| synchronized (ActivityManagerService.this) { |
| ActivityManagerService.this.onUserStoppedLocked(userId); |
| } |
| mBatteryStatsService.onUserRemoved(userId); |
| mUserController.onUserRemoved(userId); |
| } |
| |
| @Override |
| public void killForegroundAppsForUser(int userHandle) { |
| synchronized (ActivityManagerService.this) { |
| final ArrayList<ProcessRecord> procs = new ArrayList<>(); |
| final int NP = mProcessNames.getMap().size(); |
| for (int ip = 0; ip < NP; ip++) { |
| final SparseArray<ProcessRecord> 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.userId == userHandle && app.foregroundActivities) { |
| 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 fg"); |
| } |
| } |
| } |
| |
| @Override |
| public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken, |
| long duration) { |
| if (!(target instanceof PendingIntentRecord)) { |
| Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target); |
| return; |
| } |
| synchronized (ActivityManagerService.this) { |
| ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration); |
| } |
| } |
| |
| @Override |
| public void setDeviceIdleWhitelist(int[] allAppids, int[] exceptIdleAppids) { |
| synchronized (ActivityManagerService.this) { |
| mDeviceIdleWhitelist = allAppids; |
| mDeviceIdleExceptIdleWhitelist = exceptIdleAppids; |
| } |
| } |
| |
| @Override |
| public void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId, boolean adding) { |
| synchronized (ActivityManagerService.this) { |
| mDeviceIdleTempWhitelist = appids; |
| setAppIdTempWhitelistStateLocked(changingAppId, adding); |
| } |
| } |
| |
| @Override |
| public void updatePersistentConfigurationForUser(@NonNull Configuration values, |
| int userId) { |
| Preconditions.checkNotNull(values, "Configuration must not be null"); |
| Preconditions.checkArgumentNonnegative(userId, "userId " + userId + " not supported"); |
| synchronized (ActivityManagerService.this) { |
| updateConfigurationLocked(values, null, false, true, userId, |
| false /* deferResume */); |
| } |
| } |
| |
| @Override |
| public int getUidProcessState(int uid) { |
| return getUidState(uid); |
| } |
| |
| @Override |
| public boolean isSystemReady() { |
| // no need to synchronize(this) just to read & return the value |
| return mSystemReady; |
| } |
| |
| /** |
| * Sets if the given pid has an overlay UI or not. |
| * |
| * @param pid The pid we are setting overlay UI for. |
| * @param hasOverlayUi True if the process has overlay UI. |
| * @see android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY |
| */ |
| @Override |
| public void setHasOverlayUi(int pid, boolean hasOverlayUi) { |
| synchronized (ActivityManagerService.this) { |
| final ProcessRecord pr; |
| synchronized (mPidsSelfLocked) { |
| pr = mPidsSelfLocked.get(pid); |
| if (pr == null) { |
| Slog.w(TAG, "setHasOverlayUi called on unknown pid: " + pid); |
| return; |
| } |
| } |
| if (pr.hasOverlayUi == hasOverlayUi) { |
| return; |
| } |
| pr.hasOverlayUi = hasOverlayUi; |
| //Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid); |
| updateOomAdjLocked(pr, true); |
| } |
| } |
| |
| /** |
| * Called after the network policy rules are updated by |
| * {@link com.android.server.net.NetworkPolicyManagerService} for a specific {@param uid} |
| * and {@param procStateSeq}. |
| */ |
| @Override |
| public void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq) { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "Got update from NPMS for uid: " |
| + uid + " seq: " + procStateSeq); |
| } |
| UidRecord record; |
| synchronized (ActivityManagerService.this) { |
| record = mActiveUids.get(uid); |
| if (record == null) { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "No active uidRecord for uid: " + uid |
| + " procStateSeq: " + procStateSeq); |
| } |
| return; |
| } |
| } |
| synchronized (record.networkStateLock) { |
| if (record.lastNetworkUpdatedProcStateSeq >= procStateSeq) { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "procStateSeq: " + procStateSeq + " has already" |
| + " been handled for uid: " + uid); |
| } |
| return; |
| } |
| record.lastNetworkUpdatedProcStateSeq = procStateSeq; |
| if (record.curProcStateSeq > procStateSeq) { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "No need to handle older seq no., Uid: " + uid |
| + ", curProcstateSeq: " + record.curProcStateSeq |
| + ", procStateSeq: " + procStateSeq); |
| } |
| return; |
| } |
| if (record.waitingForNetwork) { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "Notifying all blocking threads for uid: " + uid |
| + ", procStateSeq: " + procStateSeq); |
| } |
| record.networkStateLock.notifyAll(); |
| } |
| } |
| } |
| |
| @Override |
| public void saveANRState(String reason) { |
| synchronized (ActivityManagerService.this) { |
| final StringWriter sw = new StringWriter(); |
| final PrintWriter pw = new FastPrintWriter(sw, false, 1024); |
| pw.println(" ANR time: " + DateFormat.getDateTimeInstance().format(new Date())); |
| if (reason != null) { |
| pw.println(" Reason: " + reason); |
| } |
| pw.println(); |
| mActivityTaskManager.getActivityStartController().dump(pw, " ", null); |
| pw.println(); |
| pw.println("-------------------------------------------------------------------------------"); |
| dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */, |
| true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */, |
| "" /* header */); |
| pw.println(); |
| pw.close(); |
| |
| mLastANRState = sw.toString(); |
| } |
| } |
| |
| @Override |
| public void clearSavedANRState() { |
| synchronized (ActivityManagerService.this) { |
| mLastANRState = null; |
| } |
| } |
| |
| @Override |
| public void setAllowAppSwitches(@NonNull String type, int uid, int userId) { |
| synchronized (ActivityManagerService.this) { |
| if (mUserController.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) { |
| ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId); |
| if (types == null) { |
| if (uid < 0) { |
| return; |
| } |
| types = new ArrayMap<>(); |
| mAllowAppSwitchUids.put(userId, types); |
| } |
| if (uid < 0) { |
| types.remove(type); |
| } else { |
| types.put(type, uid); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public boolean isRuntimeRestarted() { |
| return mSystemServiceManager.isRuntimeRestarted(); |
| } |
| |
| @Override |
| public boolean canStartMoreUsers() { |
| return mUserController.canStartMoreUsers(); |
| } |
| |
| @Override |
| public void setSwitchingFromSystemUserMessage(String switchingFromSystemUserMessage) { |
| mUserController.setSwitchingFromSystemUserMessage(switchingFromSystemUserMessage); |
| } |
| |
| @Override |
| public void setSwitchingToSystemUserMessage(String switchingToSystemUserMessage) { |
| mUserController.setSwitchingToSystemUserMessage(switchingToSystemUserMessage); |
| } |
| |
| @Override |
| public int getMaxRunningUsers() { |
| return mUserController.mMaxRunningUsers; |
| } |
| |
| @Override |
| public boolean isUidActive(int uid) { |
| synchronized (ActivityManagerService.this) { |
| return isUidActiveLocked(uid); |
| } |
| } |
| |
| @Override |
| public void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) { |
| ActivityManagerService.this.setRunningRemoteAnimation(pid, runningRemoteAnimation); |
| } |
| |
| @Override |
| public List<ProcessMemoryState> getMemoryStateForProcesses() { |
| List<ProcessMemoryState> processMemoryStates = new ArrayList<>(); |
| synchronized (mPidsSelfLocked) { |
| for (int i = 0, size = mPidsSelfLocked.size(); i < size; i++) { |
| final ProcessRecord r = mPidsSelfLocked.valueAt(i); |
| final int pid = r.pid; |
| final int uid = r.uid; |
| final MemoryStat memoryStat = readMemoryStatFromFilesystem(uid, pid); |
| if (memoryStat == null) { |
| continue; |
| } |
| ProcessMemoryState processMemoryState = |
| new ProcessMemoryState(uid, |
| r.processName, |
| r.maxAdj, |
| memoryStat.pgfault, |
| memoryStat.pgmajfault, |
| memoryStat.rssInBytes, |
| memoryStat.cacheInBytes, |
| memoryStat.swapInBytes); |
| processMemoryStates.add(processMemoryState); |
| } |
| } |
| return processMemoryStates; |
| } |
| } |
| |
| /** |
| * Called by app main thread to wait for the network policy rules to get updated. |
| * |
| * @param procStateSeq The sequence number indicating the process state change that the main |
| * thread is interested in. |
| */ |
| @Override |
| public void waitForNetworkStateUpdate(long procStateSeq) { |
| final int callingUid = Binder.getCallingUid(); |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "Called from " + callingUid + " to wait for seq: " + procStateSeq); |
| } |
| UidRecord record; |
| synchronized (this) { |
| record = mActiveUids.get(callingUid); |
| if (record == null) { |
| return; |
| } |
| } |
| synchronized (record.networkStateLock) { |
| if (record.lastDispatchedProcStateSeq < procStateSeq) { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "Uid state change for seq no. " + procStateSeq + " is not " |
| + "dispatched to NPMS yet, so don't wait. Uid: " + callingUid |
| + " lastProcStateSeqDispatchedToObservers: " |
| + record.lastDispatchedProcStateSeq); |
| } |
| return; |
| } |
| if (record.curProcStateSeq > procStateSeq) { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "Ignore the wait requests for older seq numbers. Uid: " |
| + callingUid + ", curProcStateSeq: " + record.curProcStateSeq |
| + ", procStateSeq: " + procStateSeq); |
| } |
| return; |
| } |
| if (record.lastNetworkUpdatedProcStateSeq >= procStateSeq) { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "Network rules have been already updated for seq no. " |
| + procStateSeq + ", so no need to wait. Uid: " |
| + callingUid + ", lastProcStateSeqWithUpdatedNetworkState: " |
| + record.lastNetworkUpdatedProcStateSeq); |
| } |
| return; |
| } |
| try { |
| if (DEBUG_NETWORK) { |
| Slog.d(TAG_NETWORK, "Starting to wait for the network rules update." |
| + " Uid: " + callingUid + " procStateSeq: " + procStateSeq); |
| } |
| final long startTime = SystemClock.uptimeMillis(); |
| record.waitingForNetwork = true; |
| record.networkStateLock.wait(mWaitForNetworkTimeoutMs); |
| record.waitingForNetwork = false; |
| final long totalTime = SystemClock.uptimeMillis() - startTime; |
| if (totalTime >= mWaitForNetworkTimeoutMs || DEBUG_NETWORK) { |
| Slog.w(TAG_NETWORK, "Total time waited for network rules to get updated: " |
| + totalTime + ". Uid: " + callingUid + " procStateSeq: " |
| + procStateSeq + " UidRec: " + record |
| + " validateUidRec: " + mValidateUids.get(callingUid)); |
| } |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| } |
| } |
| } |
| |
| public void waitForBroadcastIdle(PrintWriter pw) { |
| enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()"); |
| while (true) { |
| boolean idle = true; |
| synchronized (this) { |
| for (BroadcastQueue queue : mBroadcastQueues) { |
| if (!queue.isIdle()) { |
| final String msg = "Waiting for queue " + queue + " to become idle..."; |
| pw.println(msg); |
| pw.flush(); |
| Slog.v(TAG, msg); |
| idle = false; |
| } |
| } |
| } |
| |
| if (idle) { |
| final String msg = "All broadcast queues are idle!"; |
| pw.println(msg); |
| pw.flush(); |
| Slog.v(TAG, msg); |
| return; |
| } else { |
| SystemClock.sleep(1000); |
| } |
| } |
| } |
| |
| /** |
| * Kill processes for the user with id userId and that depend on the package named packageName |
| */ |
| @Override |
| public void killPackageDependents(String packageName, int userId) { |
| enforceCallingPermission(android.Manifest.permission.KILL_UID, "killPackageDependents()"); |
| if (packageName == null) { |
| throw new NullPointerException( |
| "Cannot kill the dependents of a package without its name."); |
| } |
| |
| long callingId = Binder.clearCallingIdentity(); |
| IPackageManager pm = AppGlobals.getPackageManager(); |
| int pkgUid = -1; |
| try { |
| pkgUid = pm.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId); |
| } catch (RemoteException e) { |
| } |
| if (userId != UserHandle.USER_ALL && pkgUid == -1) { |
| throw new IllegalArgumentException( |
| "Cannot kill dependents of non-existing package " + packageName); |
| } |
| try { |
| synchronized(this) { |
| killPackageProcessesLocked(packageName, UserHandle.getAppId(pkgUid), userId, |
| ProcessList.FOREGROUND_APP_ADJ, false, true, true, false, |
| "dep: " + packageName); |
| } |
| } finally { |
| Binder.restoreCallingIdentity(callingId); |
| } |
| } |
| |
| @Override |
| public int restartUserInBackground(final int userId) { |
| return mUserController.restartUser(userId, /* foreground */ false); |
| } |
| |
| @Override |
| public void scheduleApplicationInfoChanged(List<String> packageNames, int userId) { |
| enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, |
| "scheduleApplicationInfoChanged()"); |
| |
| synchronized (this) { |
| final long origId = Binder.clearCallingIdentity(); |
| try { |
| updateApplicationInfoLocked(packageNames, userId); |
| } finally { |
| Binder.restoreCallingIdentity(origId); |
| } |
| } |
| } |
| |
| void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) { |
| final boolean updateFrameworkRes = packagesToUpdate.contains("android"); |
| for (int i = mLruProcesses.size() - 1; i >= 0; i--) { |
| final ProcessRecord app = mLruProcesses.get(i); |
| if (app.thread == null) { |
| continue; |
| } |
| |
| if (userId != UserHandle.USER_ALL && app.userId != userId) { |
| continue; |
| } |
| |
| final int packageCount = app.pkgList.size(); |
| for (int j = 0; j < packageCount; j++) { |
| final String packageName = app.pkgList.keyAt(j); |
| if (updateFrameworkRes || packagesToUpdate.contains(packageName)) { |
| try { |
| final ApplicationInfo ai = AppGlobals.getPackageManager() |
| .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId); |
| if (ai != null) { |
| app.thread.scheduleApplicationInfoChanged(ai); |
| } |
| } catch (RemoteException e) { |
| Slog.w(TAG, String.format("Failed to update %s ApplicationInfo for %s", |
| packageName, app)); |
| } |
| } |
| } |
| } |
| if (updateFrameworkRes) { |
| // Update system server components that need to know about changed overlays. Because the |
| // overlay is applied in ActivityThread, we need to serialize through its thread too. |
| final Executor executor = ActivityThread.currentActivityThread().getExecutor(); |
| final DisplayManagerInternal display = |
| LocalServices.getService(DisplayManagerInternal.class); |
| if (display != null) { |
| executor.execute(display::onOverlayChanged); |
| } |
| if (mWindowManager != null) { |
| executor.execute(mWindowManager::onOverlayChanged); |
| } |
| } |
| } |
| |
| /** |
| * Attach an agent to the specified process (proces name or PID) |
| */ |
| public void attachAgent(String process, String path) { |
| try { |
| synchronized (this) { |
| ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM, "attachAgent"); |
| if (proc == null || proc.thread == null) { |
| throw new IllegalArgumentException("Unknown process: " + process); |
| } |
| |
| boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); |
| if (!isDebuggable) { |
| if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { |
| throw new SecurityException("Process not debuggable: " + proc); |
| } |
| } |
| |
| proc.thread.attachAgent(path); |
| } |
| } catch (RemoteException e) { |
| throw new IllegalStateException("Process disappeared"); |
| } |
| } |
| |
| @VisibleForTesting |
| public static class Injector { |
| private NetworkManagementInternal mNmi; |
| |
| public Context getContext() { |
| return null; |
| } |
| |
| public AppOpsService getAppOpsService(File file, Handler handler) { |
| return new AppOpsService(file, handler); |
| } |
| |
| public Handler getUiHandler(ActivityManagerService service) { |
| return service.new UiHandler(); |
| } |
| |
| public boolean isNetworkRestrictedForUid(int uid) { |
| if (ensureHasNetworkManagementInternal()) { |
| return mNmi.isNetworkRestrictedForUid(uid); |
| } |
| return false; |
| } |
| |
| private boolean ensureHasNetworkManagementInternal() { |
| if (mNmi == null) { |
| mNmi = LocalServices.getService(NetworkManagementInternal.class); |
| } |
| return mNmi != null; |
| } |
| } |
| } |