| /* |
| * Copyright (C) 2006 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 android.app; |
| |
| import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; |
| import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; |
| import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE; |
| import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY; |
| import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; |
| import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; |
| import static android.app.servertransaction.ActivityLifecycleItem.ON_START; |
| import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; |
| import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE; |
| import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS; |
| import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX; |
| import static android.content.res.Configuration.UI_MODE_TYPE_DESK; |
| import static android.content.res.Configuration.UI_MODE_TYPE_MASK; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| import static android.view.Display.INVALID_DISPLAY; |
| import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; |
| import static android.window.ConfigurationHelper.isDifferentDisplay; |
| import static android.window.ConfigurationHelper.shouldUpdateResources; |
| |
| import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; |
| import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; |
| import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext; |
| import static com.android.window.flags.Flags.activityWindowInfoFlag; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.ActivityOptions.SceneTransitionInfo; |
| import android.app.RemoteServiceException.BadForegroundServiceNotificationException; |
| import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException; |
| import android.app.RemoteServiceException.CannotPostForegroundServiceNotificationException; |
| import android.app.RemoteServiceException.CrashedByAdbException; |
| import android.app.RemoteServiceException.ForegroundServiceDidNotStartInTimeException; |
| import android.app.RemoteServiceException.ForegroundServiceDidNotStopInTimeException; |
| import android.app.RemoteServiceException.MissingRequestPasswordComplexityPermissionException; |
| import android.app.assist.AssistContent; |
| import android.app.assist.AssistStructure; |
| import android.app.backup.BackupAgent; |
| import android.app.backup.BackupAnnotations.BackupDestination; |
| import android.app.backup.BackupAnnotations.OperationType; |
| import android.app.compat.CompatChanges; |
| import android.app.sdksandbox.sandboxactivity.ActivityContextInfo; |
| import android.app.sdksandbox.sandboxactivity.SdkSandboxActivityAuthority; |
| import android.app.servertransaction.ActivityLifecycleItem; |
| import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; |
| import android.app.servertransaction.ActivityRelaunchItem; |
| import android.app.servertransaction.ActivityResultItem; |
| import android.app.servertransaction.ClientTransaction; |
| import android.app.servertransaction.ClientTransactionListenerController; |
| import android.app.servertransaction.DestroyActivityItem; |
| import android.app.servertransaction.PauseActivityItem; |
| import android.app.servertransaction.PendingTransactionActions; |
| import android.app.servertransaction.PendingTransactionActions.StopInfo; |
| import android.app.servertransaction.ResumeActivityItem; |
| import android.app.servertransaction.TransactionExecutor; |
| import android.app.servertransaction.TransactionExecutorHelper; |
| import android.bluetooth.BluetoothFrameworkInitializer; |
| import android.companion.virtual.VirtualDeviceManager; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.AttributionSource; |
| import android.content.AutofillOptions; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentCallbacks2; |
| import android.content.ComponentName; |
| import android.content.ContentCaptureOptions; |
| import android.content.ContentProvider; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.IContentProvider; |
| import android.content.IIntentReceiver; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.ComponentInfo; |
| 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.ParceledListSlice; |
| import android.content.pm.PermissionInfo; |
| import android.content.pm.ProviderInfo; |
| import android.content.pm.ProviderInfoList; |
| import android.content.pm.ServiceInfo; |
| import android.content.res.AssetManager; |
| import android.content.res.CompatibilityInfo; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.content.res.ResourcesImpl; |
| import android.content.res.loader.ResourcesLoader; |
| import android.database.sqlite.SQLiteDatabase; |
| import android.database.sqlite.SQLiteDebug; |
| import android.database.sqlite.SQLiteDebug.DbStats; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.graphics.HardwareRenderer; |
| import android.graphics.Typeface; |
| import android.hardware.display.DisplayManagerGlobal; |
| import android.media.MediaFrameworkInitializer; |
| import android.media.MediaFrameworkPlatformInitializer; |
| import android.media.MediaServiceManager; |
| import android.net.ConnectivityManager; |
| import android.net.Proxy; |
| import android.net.TrafficStats; |
| import android.net.Uri; |
| import android.nfc.NfcFrameworkInitializer; |
| import android.nfc.NfcServiceManager; |
| import android.os.AsyncTask; |
| import android.os.Binder; |
| import android.os.BluetoothServiceManager; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.CancellationSignal; |
| import android.os.DdmSyncStageUpdater; |
| import android.os.DdmSyncState.Stage; |
| import android.os.Debug; |
| import android.os.Environment; |
| import android.os.FileUtils; |
| import android.os.GraphicsEnvironment; |
| import android.os.Handler; |
| import android.os.HandlerExecutor; |
| import android.os.IBinder; |
| import android.os.IBinderCallback; |
| import android.os.ICancellationSignal; |
| import android.os.LocaleList; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.MessageQueue; |
| import android.os.Parcel; |
| import android.os.ParcelFileDescriptor; |
| import android.os.PersistableBundle; |
| import android.os.Process; |
| import android.os.ProfilingFrameworkInitializer; |
| import android.os.ProfilingServiceManager; |
| import android.os.RemoteCallback; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.SharedMemory; |
| import android.os.StatsFrameworkInitializer; |
| import android.os.StatsServiceManager; |
| import android.os.StrictMode; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.os.TelephonyServiceManager; |
| import android.os.Trace; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.permission.IPermissionManager; |
| import android.provider.BlockedNumberContract; |
| import android.provider.CalendarContract; |
| import android.provider.CallLog; |
| import android.provider.ContactsContract; |
| import android.provider.DeviceConfigInitializer; |
| import android.provider.DeviceConfigServiceManager; |
| import android.provider.Downloads; |
| import android.provider.FontsContract; |
| import android.provider.Settings; |
| import android.renderscript.RenderScriptCacheDir; |
| import android.se.omapi.SeFrameworkInitializer; |
| import android.se.omapi.SeServiceManager; |
| import android.security.NetworkSecurityPolicy; |
| import android.security.net.config.NetworkSecurityConfigProvider; |
| import android.system.ErrnoException; |
| import android.system.OsConstants; |
| import android.system.StructStat; |
| import android.telephony.TelephonyFrameworkInitializer; |
| import android.util.AndroidRuntimeException; |
| import android.util.ArrayMap; |
| import android.util.DisplayMetrics; |
| import android.util.EventLog; |
| import android.util.Log; |
| import android.util.LogPrinter; |
| import android.util.MergedConfiguration; |
| import android.util.Pair; |
| import android.util.PrintWriterPrinter; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.util.SuperNotCalledException; |
| import android.util.UtilConfig; |
| import android.util.proto.ProtoOutputStream; |
| import android.view.Choreographer; |
| import android.view.Display; |
| import android.view.SurfaceControl; |
| import android.view.ThreadedRenderer; |
| import android.view.View; |
| import android.view.ViewManager; |
| import android.view.ViewRootImpl; |
| import android.view.ViewTreeObserver; |
| import android.view.Window; |
| import android.view.WindowManager; |
| import android.view.WindowManagerGlobal; |
| import android.view.autofill.AutofillId; |
| import android.view.contentcapture.IContentCaptureManager; |
| import android.view.contentcapture.IContentCaptureOptionsCallback; |
| import android.view.translation.TranslationSpec; |
| import android.view.translation.UiTranslationSpec; |
| import android.webkit.WebView; |
| import android.window.ActivityWindowInfo; |
| import android.window.ITaskFragmentOrganizer; |
| import android.window.SizeConfigurationBuckets; |
| import android.window.SplashScreen; |
| import android.window.SplashScreenView; |
| import android.window.TaskFragmentTransaction; |
| import android.window.WindowContextInfo; |
| import android.window.WindowProviderService; |
| import android.window.WindowTokenClientController; |
| |
| import com.android.internal.R; |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.app.IVoiceInteractor; |
| import com.android.internal.content.ReferrerIntent; |
| import com.android.internal.os.BinderCallsStats; |
| import com.android.internal.os.BinderInternal; |
| import com.android.internal.os.RuntimeInit; |
| import com.android.internal.os.SafeZipPathValidatorCallback; |
| import com.android.internal.os.SomeArgs; |
| import com.android.internal.policy.DecorView; |
| import com.android.internal.util.ArrayUtils; |
| import com.android.internal.util.FastPrintWriter; |
| import com.android.internal.util.Preconditions; |
| import com.android.internal.util.function.pooled.PooledLambda; |
| import com.android.org.conscrypt.TrustedCertificateStore; |
| import com.android.server.am.MemInfoDumpProto; |
| |
| import dalvik.annotation.optimization.NeverCompile; |
| import dalvik.system.AppSpecializationHooks; |
| import dalvik.system.CloseGuard; |
| import dalvik.system.VMDebug; |
| import dalvik.system.VMRuntime; |
| import dalvik.system.ZipPathValidator; |
| |
| import libcore.io.ForwardingOs; |
| import libcore.io.IoUtils; |
| import libcore.io.Os; |
| import libcore.net.event.NetworkEventDispatcher; |
| |
| import org.apache.harmony.dalvik.ddmc.DdmVmInternal; |
| |
| import java.io.File; |
| import java.io.FileDescriptor; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.lang.ref.WeakReference; |
| import java.lang.reflect.Method; |
| import java.net.InetAddress; |
| import java.nio.file.DirectoryStream; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.StandardCopyOption; |
| import java.text.DateFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.TimeZone; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.function.Consumer; |
| |
| /** |
| * This manages the execution of the main thread in an |
| * application process, scheduling and executing activities, |
| * broadcasts, and other operations on it as the activity |
| * manager requests. |
| * |
| * {@hide} |
| */ |
| public final class ActivityThread extends ClientTransactionHandler |
| implements ActivityThreadInternal { |
| |
| private final DdmSyncStageUpdater mDdmSyncStageUpdater = new DdmSyncStageUpdater(); |
| |
| /** @hide */ |
| public static final String TAG = "ActivityThread"; |
| static final boolean localLOGV = false; |
| static final boolean DEBUG_MESSAGES = false; |
| /** @hide */ |
| public static final boolean DEBUG_BROADCAST = false; |
| private static final boolean DEBUG_RESULTS = false; |
| private static final boolean DEBUG_BACKUP = false; |
| public static final boolean DEBUG_CONFIGURATION = false; |
| private static final boolean DEBUG_SERVICE = false; |
| public static final boolean DEBUG_MEMORY_TRIM = false; |
| private static final boolean DEBUG_PROVIDER = false; |
| public static final boolean DEBUG_ORDER = false; |
| private static final boolean DEBUG_APP_INFO = false; |
| private static final long MIN_TIME_BETWEEN_GCS = 5*1000; |
| /** |
| * The delay to release the provider when it has no more references. It reduces the number of |
| * transactions for acquiring and releasing provider if the client accesses the provider |
| * frequently in a short time. |
| */ |
| private static final long CONTENT_PROVIDER_RETAIN_TIME = 1000; |
| private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003; |
| |
| /** Type for IActivityManager.serviceDoneExecuting: anonymous operation */ |
| public static final int SERVICE_DONE_EXECUTING_ANON = 0; |
| /** Type for IActivityManager.serviceDoneExecuting: done with an onStart call */ |
| public static final int SERVICE_DONE_EXECUTING_START = 1; |
| /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */ |
| public static final int SERVICE_DONE_EXECUTING_STOP = 2; |
| /** Type for IActivityManager.serviceDoneExecuting: done with an onRebind call */ |
| public static final int SERVICE_DONE_EXECUTING_REBIND = 3; |
| /** Type for IActivityManager.serviceDoneExecuting: done with an onUnbind call */ |
| public static final int SERVICE_DONE_EXECUTING_UNBIND = 4; |
| |
| /** Use foreground GC policy (less pause time) and higher JIT weight. */ |
| private static final int VM_PROCESS_STATE_JANK_PERCEPTIBLE = 0; |
| /** Use background GC policy and default JIT threshold. */ |
| private static final int VM_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1; |
| |
| /** The delay time for retrying to request DirectActions. */ |
| private static final long REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS = 200; |
| /** The max count for retrying to request DirectActions. */ |
| private static final int REQUEST_DIRECT_ACTIONS_RETRY_MAX_COUNT = 7; |
| |
| /** |
| * Denotes an invalid sequence number corresponding to a process state change. |
| */ |
| public static final long INVALID_PROC_STATE_SEQ = -1; |
| |
| /** |
| * Identifier for the sequence no. associated with this process start. It will be provided |
| * as one of the arguments when the process starts. |
| */ |
| public static final String PROC_START_SEQ_IDENT = "seq="; |
| |
| private final Object mNetworkPolicyLock = new Object(); |
| |
| private static final String DEFAULT_FULL_BACKUP_AGENT = "android.app.backup.FullBackupAgent"; |
| |
| private static final long BINDER_CALLBACK_THROTTLE = 10_100L; |
| private long mBinderCallbackLast = -1; |
| |
| /** |
| * Denotes the sequence number of the process state change for which the main thread needs |
| * to block until the network rules are updated for it. |
| * |
| * Value of {@link #INVALID_PROC_STATE_SEQ} indicates there is no need for blocking. |
| */ |
| @GuardedBy("mNetworkPolicyLock") |
| private long mNetworkBlockSeq = INVALID_PROC_STATE_SEQ; |
| |
| @UnsupportedAppUsage |
| private ContextImpl mSystemContext; |
| @GuardedBy("this") |
| private ArrayList<WeakReference<ContextImpl>> mDisplaySystemUiContexts; |
| |
| @UnsupportedAppUsage |
| static volatile IPackageManager sPackageManager; |
| private static volatile IPermissionManager sPermissionManager; |
| |
| @UnsupportedAppUsage |
| final ApplicationThread mAppThread = new ApplicationThread(); |
| @UnsupportedAppUsage |
| final Looper mLooper = Looper.myLooper(); |
| @UnsupportedAppUsage |
| final H mH = new H(); |
| final Executor mExecutor = new HandlerExecutor(mH); |
| /** |
| * Maps from activity token to local record of running activities in this process. |
| * |
| * This variable is readable if the code is running in activity thread or holding {@link |
| * #mResourcesManager}. It's only writable if the code is running in activity thread and holding |
| * {@link #mResourcesManager}. |
| */ |
| @UnsupportedAppUsage |
| final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>(); |
| /** Maps from activity token to the pending override configuration. */ |
| @GuardedBy("mPendingOverrideConfigs") |
| private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>(); |
| |
| /** |
| * A queue of pending ApplicationInfo updates. In case when we get a concurrent update |
| * this queue allows us to only apply the latest object, and it can be applied on demand |
| * instead of waiting for the handler thread to reach the scheduled callback. |
| */ |
| @GuardedBy("mResourcesManager") |
| private final ArrayMap<String, ApplicationInfo> mPendingAppInfoUpdates = new ArrayMap<>(); |
| |
| /** The activities to be truly destroyed (not include relaunch). */ |
| final Map<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed = |
| Collections.synchronizedMap(new ArrayMap<>()); |
| // List of new activities that should be reported when next we idle. |
| final ArrayList<ActivityClientRecord> mNewActivities = new ArrayList<>(); |
| // Number of activities that are currently visible on-screen. |
| @UnsupportedAppUsage |
| int mNumVisibleActivities = 0; |
| private final AtomicInteger mNumLaunchingActivities = new AtomicInteger(); |
| @GuardedBy("mAppThread") |
| private int mLastProcessState = PROCESS_STATE_UNKNOWN; |
| final ArrayList<WeakReference<AssistStructure>> mLastAssistStructures = new ArrayList<>(); |
| |
| @NonNull |
| private final ConfigurationChangedListenerController mConfigurationChangedListenerController = |
| new ConfigurationChangedListenerController(); |
| |
| private int mLastSessionId; |
| // Holds the value of the last reported device ID value from the server for the top activity. |
| int mLastReportedDeviceId = Context.DEVICE_ID_DEFAULT; |
| final ArrayMap<IBinder, CreateServiceData> mServicesData = new ArrayMap<>(); |
| @UnsupportedAppUsage |
| final ArrayMap<IBinder, Service> mServices = new ArrayMap<>(); |
| @UnsupportedAppUsage |
| AppBindData mBoundApplication; |
| Profiler mProfiler; |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553, |
| publicAlternatives = "Use {@code Context#getResources()#getConfiguration()#densityDpi} " |
| + "instead.") |
| int mCurDefaultDisplayDpi; |
| @UnsupportedAppUsage |
| boolean mDensityCompatMode; |
| private CompatibilityInfo mCompatibilityInfo; |
| @UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R, |
| publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.") |
| Configuration mConfiguration; |
| @GuardedBy("this") |
| private boolean mUpdateHttpProxyOnBind = false; |
| @UnsupportedAppUsage |
| Application mInitialApplication; |
| @UnsupportedAppUsage |
| final ArrayList<Application> mAllApplications = new ArrayList<>(); |
| /** |
| * Bookkeeping of instantiated backup agents indexed first by user id, then by package name. |
| * Indexing by user id supports parallel backups across users on system packages as they run in |
| * the same process with the same package name. Indexing by package name supports multiple |
| * distinct applications running in the same process. |
| */ |
| private final SparseArray<ArrayMap<String, BackupAgent>> mBackupAgentsByUser = |
| new SparseArray<>(); |
| /** Reference to singleton {@link ActivityThread} */ |
| @UnsupportedAppUsage |
| private static volatile ActivityThread sCurrentActivityThread; |
| @UnsupportedAppUsage |
| Instrumentation mInstrumentation; |
| String mInstrumentationPackageName = null; |
| @UnsupportedAppUsage |
| String mInstrumentationAppDir = null; |
| String[] mInstrumentationSplitAppDirs = null; |
| String mInstrumentationLibDir = null; |
| @UnsupportedAppUsage |
| String mInstrumentedAppDir = null; |
| String[] mInstrumentedSplitAppDirs = null; |
| String mInstrumentedLibDir = null; |
| boolean mInstrumentingWithoutRestart; |
| boolean mSystemThread = false; |
| boolean mSomeActivitiesChanged = false; |
| |
| // These can be accessed by multiple threads; mResourcesManager is the lock. |
| // XXX For now we keep around information about all packages we have |
| // seen, not removing entries from this map. |
| // NOTE: The activity and window managers need to call in to |
| // ActivityThread to do things like update resource configurations, |
| // which means this lock gets held while the activity and window managers |
| // holds their own lock. Thus you MUST NEVER call back into the activity manager |
| // or window manager or anything that depends on them while holding this lock. |
| // These LoadedApk are only valid for the userId that we're running as. |
| @GuardedBy("mResourcesManager") |
| @UnsupportedAppUsage |
| final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>(); |
| @GuardedBy("mResourcesManager") |
| @UnsupportedAppUsage |
| final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>(); |
| @GuardedBy("mResourcesManager") |
| final ArrayList<ActivityClientRecord> mRelaunchingActivities = new ArrayList<>(); |
| @GuardedBy("mResourcesManager") |
| @UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R, |
| publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.") |
| Configuration mPendingConfiguration = null; |
| // An executor that performs multi-step transactions. |
| private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this); |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| private final ResourcesManager mResourcesManager; |
| |
| // Registry of remote cancellation transports pending a reply with reply handles. |
| @GuardedBy("this") |
| private @Nullable Map<SafeCancellationTransport, CancellationSignal> mRemoteCancellations; |
| |
| private static final class ProviderKey { |
| final String authority; |
| final int userId; |
| |
| @GuardedBy("mLock") |
| ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter |
| final Object mLock; // The lock to be used to get notified when the provider is ready |
| |
| public ProviderKey(String authority, int userId) { |
| this.authority = authority; |
| this.userId = userId; |
| this.mLock = new Object(); |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (o instanceof ProviderKey) { |
| final ProviderKey other = (ProviderKey) o; |
| return Objects.equals(authority, other.authority) && userId == other.userId; |
| } |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| return ((authority != null) ? authority.hashCode() : 0) ^ userId; |
| } |
| } |
| |
| // The lock of mProviderMap protects the following variables. |
| @UnsupportedAppUsage |
| final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap |
| = new ArrayMap<ProviderKey, ProviderClientRecord>(); |
| @UnsupportedAppUsage |
| final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap |
| = new ArrayMap<IBinder, ProviderRefCount>(); |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders |
| = new ArrayMap<IBinder, ProviderClientRecord>(); |
| @UnsupportedAppUsage |
| final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName |
| = new ArrayMap<ComponentName, ProviderClientRecord>(); |
| |
| // Mitigation for b/74523247: Used to serialize calls to AM.getContentProvider(). |
| // Note we never removes items from this map but that's okay because there are only so many |
| // users and so many authorities. |
| @GuardedBy("mGetProviderKeys") |
| final ArrayMap<ProviderKey, ProviderKey> mGetProviderKeys = new ArrayMap<>(); |
| |
| final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners |
| = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>(); |
| |
| private SplashScreen.SplashScreenManagerGlobal mSplashScreenGlobal; |
| |
| final GcIdler mGcIdler = new GcIdler(); |
| final PurgeIdler mPurgeIdler = new PurgeIdler(); |
| |
| boolean mPurgeIdlerScheduled = false; |
| boolean mGcIdlerScheduled = false; |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| static volatile Handler sMainThreadHandler; // set once in main() |
| private long mStartSeq; // Only accesssed from the main thread |
| |
| Bundle mCoreSettings = null; |
| |
| /** |
| * The lock word for the {@link #mCoreSettings}. |
| */ |
| private final Object mCoreSettingsLock = new Object(); |
| |
| private IContentCaptureOptionsCallback.Stub mContentCaptureOptionsCallback = null; |
| |
| /** A client side controller to handle process level configuration changes. */ |
| private ConfigurationController mConfigurationController; |
| |
| /** Activity client record, used for bookkeeping for the real {@link Activity} instance. */ |
| public static final class ActivityClientRecord { |
| @UnsupportedAppUsage |
| public IBinder token; |
| public IBinder assistToken; |
| // A reusable token for other purposes, e.g. content capture, translation. It shouldn't be |
| // used without security checks |
| public IBinder shareableActivityToken; |
| // The token of the TaskFragment that embedded this activity. |
| @Nullable public IBinder mTaskFragmentToken; |
| public IBinder initialCallerInfoAccessToken; |
| int ident; |
| @UnsupportedAppUsage |
| Intent intent; |
| String referrer; |
| IVoiceInteractor voiceInteractor; |
| Bundle state; |
| PersistableBundle persistentState; |
| @UnsupportedAppUsage |
| Activity activity; |
| Window window; |
| Activity parent; |
| String embeddedID; |
| Activity.NonConfigurationInstances lastNonConfigurationInstances; |
| // TODO(lifecycler): Use mLifecycleState instead. |
| @UnsupportedAppUsage |
| boolean paused; |
| @UnsupportedAppUsage |
| boolean stopped; |
| boolean hideForNow; |
| Configuration createdConfig; |
| Configuration overrideConfig; |
| @NonNull |
| private final ActivityWindowInfo mActivityWindowInfo = new ActivityWindowInfo(); |
| @NonNull |
| private final ActivityWindowInfo mLastReportedActivityWindowInfo = new ActivityWindowInfo(); |
| |
| // Used for consolidating configs before sending on to Activity. |
| private final Configuration tmpConfig = new Configuration(); |
| // Callback used for updating activity override config and camera compat control state. |
| ViewRootImpl.ActivityConfigCallback activityConfigCallback; |
| |
| // Indicates whether this activity is currently the topmost resumed one in the system. |
| // This holds the last reported value from server. |
| boolean isTopResumedActivity; |
| // This holds the value last sent to the activity. This is needed, because an update from |
| // server may come at random time, but we always need to report changes between ON_RESUME |
| // and ON_PAUSE to the app. |
| boolean lastReportedTopResumedState; |
| |
| ProfilerInfo profilerInfo; |
| |
| @UnsupportedAppUsage |
| ActivityInfo activityInfo; |
| @UnsupportedAppUsage |
| CompatibilityInfo compatInfo; |
| @UnsupportedAppUsage |
| public LoadedApk packageInfo; |
| |
| List<ResultInfo> pendingResults; |
| List<ReferrerIntent> pendingIntents; |
| |
| boolean startsNotResumed; |
| public final boolean isForward; |
| int pendingConfigChanges; |
| // Whether we are in the process of performing on user leaving. |
| boolean mIsUserLeaving; |
| |
| Window mPendingRemoveWindow; |
| WindowManager mPendingRemoveWindowManager; |
| @UnsupportedAppUsage |
| boolean mPreserveWindow; |
| |
| /** The scene transition info. */ |
| SceneTransitionInfo mSceneTransitionInfo; |
| |
| /** Whether this activiy was launched from a bubble. */ |
| boolean mLaunchedFromBubble; |
| |
| /** |
| * This can be different from the current configuration because a new configuration may not |
| * always update to activity, e.g. windowing mode change without size change. |
| */ |
| int mLastReportedWindowingMode = WINDOWING_MODE_UNDEFINED; |
| |
| @LifecycleState |
| private int mLifecycleState = PRE_ON_CREATE; |
| |
| private SizeConfigurationBuckets mSizeConfigurations; |
| |
| @VisibleForTesting |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| public ActivityClientRecord() { |
| this.isForward = false; |
| init(); |
| } |
| |
| public ActivityClientRecord(IBinder token, Intent intent, int ident, |
| ActivityInfo info, Configuration overrideConfig, |
| String referrer, IVoiceInteractor voiceInteractor, Bundle state, |
| PersistableBundle persistentState, List<ResultInfo> pendingResults, |
| List<ReferrerIntent> pendingNewIntents, SceneTransitionInfo sceneTransitionInfo, |
| boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client, |
| IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble, |
| IBinder taskFragmentToken, IBinder initialCallerInfoAccessToken, |
| ActivityWindowInfo activityWindowInfo) { |
| this.token = token; |
| this.assistToken = assistToken; |
| this.shareableActivityToken = shareableActivityToken; |
| this.ident = ident; |
| this.intent = intent; |
| this.referrer = referrer; |
| this.voiceInteractor = voiceInteractor; |
| this.activityInfo = info; |
| this.state = state; |
| this.persistentState = persistentState; |
| this.pendingResults = pendingResults; |
| this.pendingIntents = pendingNewIntents; |
| this.isForward = isForward; |
| this.profilerInfo = profilerInfo; |
| this.overrideConfig = overrideConfig; |
| this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo); |
| this.initialCallerInfoAccessToken = initialCallerInfoAccessToken; |
| mSceneTransitionInfo = sceneTransitionInfo; |
| mLaunchedFromBubble = launchedFromBubble; |
| mTaskFragmentToken = taskFragmentToken; |
| mActivityWindowInfo.set(activityWindowInfo); |
| init(); |
| } |
| |
| /** Common initializer for all constructors. */ |
| private void init() { |
| parent = null; |
| embeddedID = null; |
| paused = false; |
| stopped = false; |
| hideForNow = false; |
| activityConfigCallback = new ViewRootImpl.ActivityConfigCallback() { |
| |
| @Override |
| public void onConfigurationChanged(@NonNull Configuration overrideConfig, |
| int newDisplayId, @Nullable ActivityWindowInfo activityWindowInfo) { |
| if (activity == null) { |
| throw new IllegalStateException( |
| "Received config update for non-existing activity"); |
| } |
| if (activityWindowInfo == null) { |
| Log.w(TAG, "Received empty ActivityWindowInfo update for r=" + activity); |
| activityWindowInfo = mActivityWindowInfo; |
| } |
| activity.mMainThread.handleActivityConfigurationChanged( |
| ActivityClientRecord.this, overrideConfig, newDisplayId, |
| activityWindowInfo, |
| false /* alwaysReportChange */); |
| } |
| |
| @Override |
| public void requestCompatCameraControl(boolean showControl, |
| boolean transformationApplied, ICompatCameraControlCallback callback) { |
| if (activity == null) { |
| throw new IllegalStateException( |
| "Received camera compat control update for non-existing activity"); |
| } |
| ActivityClient.getInstance().requestCompatCameraControl( |
| activity.getResources(), token, showControl, transformationApplied, |
| callback); |
| } |
| |
| }; |
| } |
| |
| /** Get the current lifecycle state. */ |
| public int getLifecycleState() { |
| return mLifecycleState; |
| } |
| |
| /** Update the current lifecycle state for internal bookkeeping. */ |
| public void setState(@LifecycleState int newLifecycleState) { |
| mLifecycleState = newLifecycleState; |
| switch (mLifecycleState) { |
| case ON_CREATE: |
| paused = true; |
| stopped = true; |
| break; |
| case ON_START: |
| paused = true; |
| stopped = false; |
| break; |
| case ON_RESUME: |
| paused = false; |
| stopped = false; |
| break; |
| case ON_PAUSE: |
| paused = true; |
| stopped = false; |
| break; |
| case ON_STOP: |
| paused = true; |
| stopped = true; |
| break; |
| } |
| } |
| |
| private boolean isPreHoneycomb() { |
| return activity != null && activity.getApplicationInfo().targetSdkVersion |
| < android.os.Build.VERSION_CODES.HONEYCOMB; |
| } |
| |
| private boolean isPreP() { |
| return activity != null && activity.getApplicationInfo().targetSdkVersion |
| < android.os.Build.VERSION_CODES.P; |
| } |
| |
| public boolean isPersistable() { |
| return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS; |
| } |
| |
| public boolean isVisibleFromServer() { |
| return activity != null && activity.mVisibleFromServer; |
| } |
| |
| @NonNull |
| public ActivityWindowInfo getActivityWindowInfo() { |
| return mActivityWindowInfo; |
| } |
| |
| public String toString() { |
| ComponentName componentName = intent != null ? intent.getComponent() : null; |
| return "ActivityRecord{" |
| + Integer.toHexString(System.identityHashCode(this)) |
| + " token=" + token + " " + (componentName == null |
| ? "no component name" : componentName.toShortString()) |
| + "}"; |
| } |
| |
| public String getStateString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("ActivityClientRecord{"); |
| sb.append("paused=").append(paused); |
| sb.append(", stopped=").append(stopped); |
| sb.append(", hideForNow=").append(hideForNow); |
| sb.append(", startsNotResumed=").append(startsNotResumed); |
| sb.append(", isForward=").append(isForward); |
| sb.append(", pendingConfigChanges=").append(pendingConfigChanges); |
| sb.append(", preserveWindow=").append(mPreserveWindow); |
| if (activity != null) { |
| sb.append(", Activity{"); |
| sb.append("resumed=").append(activity.mResumed); |
| sb.append(", stopped=").append(activity.mStopped); |
| sb.append(", finished=").append(activity.isFinishing()); |
| sb.append(", destroyed=").append(activity.isDestroyed()); |
| sb.append(", startedActivity=").append(activity.mStartedActivity); |
| sb.append(", changingConfigurations=").append(activity.mChangingConfigurations); |
| sb.append("}"); |
| } |
| sb.append("}"); |
| return sb.toString(); |
| } |
| } |
| |
| static final class ProviderClientRecord { |
| final String[] mNames; |
| @UnsupportedAppUsage |
| final IContentProvider mProvider; |
| @UnsupportedAppUsage |
| final ContentProvider mLocalProvider; |
| @UnsupportedAppUsage |
| final ContentProviderHolder mHolder; |
| |
| ProviderClientRecord(String[] names, IContentProvider provider, |
| ContentProvider localProvider, ContentProviderHolder holder) { |
| mNames = names; |
| mProvider = provider; |
| mLocalProvider = localProvider; |
| mHolder = holder; |
| } |
| } |
| |
| static final class ReceiverData extends BroadcastReceiver.PendingResult { |
| public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras, |
| boolean ordered, boolean sticky, boolean assumeDelivered, IBinder token, |
| int sendingUser, int sendingUid, String sendingPackage) { |
| super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, |
| assumeDelivered, token, sendingUser, intent.getFlags(), sendingUid, |
| sendingPackage); |
| this.intent = intent; |
| } |
| |
| @UnsupportedAppUsage |
| final Intent intent; |
| @UnsupportedAppUsage |
| ActivityInfo info; |
| @UnsupportedAppUsage |
| CompatibilityInfo compatInfo; |
| public String toString() { |
| return "ReceiverData{intent=" + intent + " packageName=" + |
| info.packageName + " resultCode=" + getResultCode() |
| + " resultData=" + getResultData() + " resultExtras=" |
| + getResultExtras(false) + " sentFromUid=" |
| + getSentFromUid() + " sentFromPackage=" + getSentFromPackage() + "}"; |
| } |
| } |
| |
| static final class CreateBackupAgentData { |
| ApplicationInfo appInfo; |
| int backupMode; |
| int userId; |
| @BackupDestination int backupDestination; |
| public String toString() { |
| return "CreateBackupAgentData{appInfo=" + appInfo |
| + " backupAgent=" + appInfo.backupAgentName |
| + " mode=" + backupMode + " userId=" + userId + "}"; |
| } |
| } |
| |
| static final class CreateServiceData { |
| @UnsupportedAppUsage |
| CreateServiceData() { |
| } |
| @UnsupportedAppUsage |
| IBinder token; |
| @UnsupportedAppUsage |
| ServiceInfo info; |
| @UnsupportedAppUsage |
| CompatibilityInfo compatInfo; |
| @UnsupportedAppUsage |
| Intent intent; |
| public String toString() { |
| return "CreateServiceData{token=" + token + " className=" |
| + info.name + " packageName=" + info.packageName |
| + " intent=" + intent + "}"; |
| } |
| } |
| |
| static final class BindServiceData { |
| @UnsupportedAppUsage |
| IBinder token; |
| @UnsupportedAppUsage |
| Intent intent; |
| boolean rebind; |
| long bindSeq; |
| public String toString() { |
| return "BindServiceData{token=" + token + " intent=" + intent |
| + " bindSeq=" + bindSeq + "}"; |
| } |
| } |
| |
| static final class ServiceArgsData { |
| @UnsupportedAppUsage |
| IBinder token; |
| boolean taskRemoved; |
| int startId; |
| int flags; |
| @UnsupportedAppUsage |
| Intent args; |
| public String toString() { |
| return "ServiceArgsData{token=" + token + " startId=" + startId |
| + " args=" + args + "}"; |
| } |
| } |
| |
| static final class AppBindData { |
| @UnsupportedAppUsage |
| AppBindData() { |
| } |
| @UnsupportedAppUsage |
| LoadedApk info; |
| @UnsupportedAppUsage |
| String processName; |
| @UnsupportedAppUsage |
| ApplicationInfo appInfo; |
| String sdkSandboxClientAppVolumeUuid; |
| String sdkSandboxClientAppPackage; |
| boolean isSdkInSandbox; |
| @UnsupportedAppUsage |
| List<ProviderInfo> providers; |
| ComponentName instrumentationName; |
| @UnsupportedAppUsage |
| Bundle instrumentationArgs; |
| IInstrumentationWatcher instrumentationWatcher; |
| IUiAutomationConnection instrumentationUiAutomationConnection; |
| int debugMode; |
| boolean enableBinderTracking; |
| boolean trackAllocation; |
| @UnsupportedAppUsage |
| boolean restrictedBackupMode; |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| boolean persistent; |
| Configuration config; |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| CompatibilityInfo compatInfo; |
| String buildSerial; |
| |
| /** Initial values for {@link Profiler}. */ |
| ProfilerInfo initProfilerInfo; |
| |
| AutofillOptions autofillOptions; |
| |
| /** |
| * Content capture options for the application - when null, it means ContentCapture is not |
| * enabled for the package. |
| */ |
| @Nullable |
| ContentCaptureOptions contentCaptureOptions; |
| |
| long[] disabledCompatChanges; |
| long[] mLoggableCompatChanges; |
| |
| SharedMemory mSerializedSystemFontMap; |
| |
| long startRequestedElapsedTime; |
| long startRequestedUptime; |
| |
| @Override |
| public String toString() { |
| return "AppBindData{appInfo=" + appInfo + "}"; |
| } |
| } |
| |
| static final class Profiler { |
| String profileFile; |
| ParcelFileDescriptor profileFd; |
| int samplingInterval; |
| boolean autoStopProfiler; |
| boolean streamingOutput; |
| int mClockType; |
| int mProfilerOutputVersion; |
| boolean profiling; |
| boolean handlingProfiling; |
| public void setProfiler(ProfilerInfo profilerInfo) { |
| ParcelFileDescriptor fd = profilerInfo.profileFd; |
| if (profiling) { |
| if (fd != null) { |
| try { |
| fd.close(); |
| } catch (IOException e) { |
| // Ignore |
| } |
| } |
| return; |
| } |
| if (profileFd != null) { |
| try { |
| profileFd.close(); |
| } catch (IOException e) { |
| // Ignore |
| } |
| } |
| profileFile = profilerInfo.profileFile; |
| profileFd = fd; |
| samplingInterval = profilerInfo.samplingInterval; |
| autoStopProfiler = profilerInfo.autoStopProfiler; |
| streamingOutput = profilerInfo.streamingOutput; |
| mClockType = profilerInfo.clockType; |
| mProfilerOutputVersion = profilerInfo.profilerOutputVersion; |
| } |
| public void startProfiling() { |
| if (profileFd == null || profiling) { |
| return; |
| } |
| try { |
| int bufferSize = SystemProperties.getInt("debug.traceview-buffer-size-mb", 8); |
| int flags = 0; |
| flags = mClockType | ProfilerInfo.getFlagsForOutputVersion(mProfilerOutputVersion); |
| VMDebug.startMethodTracing(profileFile, profileFd.getFileDescriptor(), |
| bufferSize * 1024 * 1024, flags, samplingInterval != 0, samplingInterval, |
| streamingOutput); |
| profiling = true; |
| } catch (RuntimeException e) { |
| Slog.w(TAG, "Profiling failed on path " + profileFile, e); |
| try { |
| profileFd.close(); |
| profileFd = null; |
| } catch (IOException e2) { |
| Slog.w(TAG, "Failure closing profile fd", e2); |
| } |
| } |
| } |
| public void stopProfiling() { |
| if (profiling) { |
| profiling = false; |
| Debug.stopMethodTracing(); |
| if (profileFd != null) { |
| try { |
| profileFd.close(); |
| } catch (IOException e) { |
| } |
| } |
| profileFd = null; |
| profileFile = null; |
| } |
| } |
| } |
| |
| static final class DumpComponentInfo { |
| ParcelFileDescriptor fd; |
| IBinder token; |
| String prefix; |
| String[] args; |
| } |
| |
| static final class ContextCleanupInfo { |
| ContextImpl context; |
| String what; |
| String who; |
| } |
| |
| static final class DumpHeapData { |
| // Whether to dump the native or managed heap. |
| public boolean managed; |
| public boolean mallocInfo; |
| public boolean runGc; |
| // compression format to dump bitmaps, null if no bitmaps to be dumped |
| public String dumpBitmaps; |
| String path; |
| ParcelFileDescriptor fd; |
| RemoteCallback finishCallback; |
| } |
| |
| static final class DumpResourcesData { |
| public ParcelFileDescriptor fd; |
| public RemoteCallback finishCallback; |
| } |
| |
| static final class UpdateCompatibilityData { |
| String pkg; |
| CompatibilityInfo info; |
| } |
| |
| static final class RequestAssistContextExtras { |
| IBinder activityToken; |
| IBinder requestToken; |
| int requestType; |
| int sessionId; |
| int flags; |
| } |
| |
| // A list of receivers and an index into the receiver to be processed next. |
| static final class ReceiverList { |
| List<ReceiverInfo> receivers; |
| int index; |
| } |
| |
| private class ApplicationThread extends IApplicationThread.Stub { |
| private static final String DB_CONNECTION_INFO_HEADER = " %8s %8s %14s %5s %5s %5s %s"; |
| private static final String DB_CONNECTION_INFO_FORMAT = " %8s %8s %14s %5d %5d %5d %s"; |
| private static final String DB_POOL_INFO_HEADER = " %13s %13s %13s %s"; |
| private static final String DB_POOL_INFO_FORMAT = " %13d %13d %13d %s"; |
| |
| public final void scheduleReceiver(Intent intent, ActivityInfo info, |
| CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras, |
| boolean ordered, boolean assumeDelivered, int sendingUser, int processState, |
| int sendingUid, String sendingPackage) { |
| updateProcessState(processState, false); |
| ReceiverData r = new ReceiverData(intent, resultCode, data, extras, |
| ordered, false, assumeDelivered, mAppThread.asBinder(), sendingUser, |
| sendingUid, sendingPackage); |
| r.info = info; |
| sendMessage(H.RECEIVER, r); |
| } |
| |
| public final void scheduleReceiverList(List<ReceiverInfo> info) throws RemoteException { |
| for (int i = 0; i < info.size(); i++) { |
| ReceiverInfo r = info.get(i); |
| if (r.registered) { |
| scheduleRegisteredReceiver(r.receiver, r.intent, |
| r.resultCode, r.data, r.extras, r.ordered, r.sticky, |
| r.assumeDelivered, r.sendingUser, r.processState, |
| r.sendingUid, r.sendingPackage); |
| } else { |
| scheduleReceiver(r.intent, r.activityInfo, r.compatInfo, |
| r.resultCode, r.data, r.extras, r.sync, |
| r.assumeDelivered, r.sendingUser, r.processState, |
| r.sendingUid, r.sendingPackage); |
| } |
| } |
| } |
| |
| public final void scheduleCreateBackupAgent(ApplicationInfo app, |
| int backupMode, int userId, @BackupDestination int backupDestination) { |
| CreateBackupAgentData d = new CreateBackupAgentData(); |
| d.appInfo = app; |
| d.backupMode = backupMode; |
| d.userId = userId; |
| d.backupDestination = backupDestination; |
| |
| sendMessage(H.CREATE_BACKUP_AGENT, d); |
| } |
| |
| public final void scheduleDestroyBackupAgent(ApplicationInfo app, int userId) { |
| CreateBackupAgentData d = new CreateBackupAgentData(); |
| d.appInfo = app; |
| d.userId = userId; |
| |
| sendMessage(H.DESTROY_BACKUP_AGENT, d); |
| } |
| |
| public final void scheduleCreateService(IBinder token, |
| ServiceInfo info, CompatibilityInfo compatInfo, int processState) { |
| updateProcessState(processState, false); |
| CreateServiceData s = new CreateServiceData(); |
| s.token = token; |
| s.info = info; |
| |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleCreateService. token=" |
| + token); |
| } |
| sendMessage(H.CREATE_SERVICE, s); |
| } |
| |
| public final void scheduleBindService(IBinder token, Intent intent, |
| boolean rebind, int processState, long bindSeq) { |
| updateProcessState(processState, false); |
| BindServiceData s = new BindServiceData(); |
| s.token = token; |
| s.intent = intent; |
| s.rebind = rebind; |
| s.bindSeq = bindSeq; |
| |
| if (DEBUG_SERVICE) |
| Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid=" |
| + Binder.getCallingUid() + " pid=" + Binder.getCallingPid()); |
| |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleBindService. token=" |
| + token + " bindSeq=" + bindSeq); |
| } |
| sendMessage(H.BIND_SERVICE, s); |
| } |
| |
| public final void scheduleUnbindService(IBinder token, Intent intent) { |
| BindServiceData s = new BindServiceData(); |
| s.token = token; |
| s.intent = intent; |
| s.bindSeq = -1; |
| |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleUnbindService. token=" |
| + token); |
| } |
| sendMessage(H.UNBIND_SERVICE, s); |
| } |
| |
| public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) { |
| List<ServiceStartArgs> list = args.getList(); |
| |
| for (int i = 0; i < list.size(); i++) { |
| ServiceStartArgs ssa = list.get(i); |
| ServiceArgsData s = new ServiceArgsData(); |
| s.token = token; |
| s.taskRemoved = ssa.taskRemoved; |
| s.startId = ssa.startId; |
| s.flags = ssa.flags; |
| s.args = ssa.args; |
| |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleServiceArgs. token=" |
| + token + " startId=" + s.startId); |
| } |
| sendMessage(H.SERVICE_ARGS, s); |
| } |
| } |
| |
| public final void scheduleStopService(IBinder token) { |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleStopService. token=" |
| + token); |
| } |
| sendMessage(H.STOP_SERVICE, token); |
| } |
| |
| @Override |
| public final void scheduleTimeoutService(IBinder token, int startId) { |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, "scheduleTimeoutService. token=" |
| + token); |
| } |
| sendMessage(H.TIMEOUT_SERVICE, token, startId); |
| } |
| |
| @Override |
| public final void schedulePing(RemoteCallback pong) { |
| sendMessage(H.PING, pong); |
| } |
| |
| @Override |
| public final void scheduleTimeoutServiceForType(IBinder token, int startId, |
| @ServiceInfo.ForegroundServiceType int fgsType) { |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, |
| "scheduleTimeoutServiceForType. token=" + token); |
| } |
| sendMessage(H.TIMEOUT_SERVICE_FOR_TYPE, token, startId, fgsType); |
| } |
| |
| @Override |
| public final void bindApplication( |
| String processName, |
| ApplicationInfo appInfo, |
| String sdkSandboxClientAppVolumeUuid, |
| String sdkSandboxClientAppPackage, |
| boolean isSdkInSandbox, |
| ProviderInfoList providerList, |
| ComponentName instrumentationName, |
| ProfilerInfo profilerInfo, |
| Bundle instrumentationArgs, |
| IInstrumentationWatcher instrumentationWatcher, |
| IUiAutomationConnection instrumentationUiConnection, |
| int debugMode, |
| boolean enableBinderTracking, |
| boolean trackAllocation, |
| boolean isRestrictedBackupMode, |
| boolean persistent, |
| Configuration config, |
| CompatibilityInfo compatInfo, |
| Map services, |
| Bundle coreSettings, |
| String buildSerial, |
| AutofillOptions autofillOptions, |
| ContentCaptureOptions contentCaptureOptions, |
| long[] disabledCompatChanges, |
| long[] loggableCompatChanges, |
| SharedMemory serializedSystemFontMap, |
| long startRequestedElapsedTime, |
| long startRequestedUptime) { |
| if (services != null) { |
| if (false) { |
| // Test code to make sure the app could see the passed-in services. |
| for (Object oname : services.keySet()) { |
| if (services.get(oname) == null) { |
| continue; // AM just passed in a null service. |
| } |
| String name = (String) oname; |
| |
| // See b/79378449 about the following exemption. |
| switch (name) { |
| case "package": |
| case Context.WINDOW_SERVICE: |
| continue; |
| } |
| |
| if (ServiceManager.getService(name) == null) { |
| Log.wtf(TAG, "Service " + name + " should be accessible by this app"); |
| } |
| } |
| } |
| |
| // Setup the service cache in the ServiceManager |
| ServiceManager.initServiceCache(services); |
| } |
| |
| setCoreSettings(coreSettings); |
| |
| AppBindData data = new AppBindData(); |
| data.processName = processName; |
| data.appInfo = appInfo; |
| data.sdkSandboxClientAppVolumeUuid = sdkSandboxClientAppVolumeUuid; |
| data.sdkSandboxClientAppPackage = sdkSandboxClientAppPackage; |
| data.isSdkInSandbox = isSdkInSandbox; |
| data.providers = providerList.getList(); |
| data.instrumentationName = instrumentationName; |
| data.instrumentationArgs = instrumentationArgs; |
| data.instrumentationWatcher = instrumentationWatcher; |
| data.instrumentationUiAutomationConnection = instrumentationUiConnection; |
| data.debugMode = debugMode; |
| data.enableBinderTracking = enableBinderTracking; |
| data.trackAllocation = trackAllocation; |
| data.restrictedBackupMode = isRestrictedBackupMode; |
| data.persistent = persistent; |
| data.config = config; |
| data.compatInfo = compatInfo; |
| data.initProfilerInfo = profilerInfo; |
| data.buildSerial = buildSerial; |
| data.autofillOptions = autofillOptions; |
| data.contentCaptureOptions = contentCaptureOptions; |
| data.disabledCompatChanges = disabledCompatChanges; |
| data.mLoggableCompatChanges = loggableCompatChanges; |
| data.mSerializedSystemFontMap = serializedSystemFontMap; |
| data.startRequestedElapsedTime = startRequestedElapsedTime; |
| data.startRequestedUptime = startRequestedUptime; |
| updateCompatOverrideScale(compatInfo); |
| CompatibilityInfo.applyOverrideScaleIfNeeded(config); |
| sendMessage(H.BIND_APPLICATION, data); |
| } |
| |
| private void updateCompatOverrideScale(CompatibilityInfo info) { |
| if (info.hasOverrideScaling()) { |
| CompatibilityInfo.setOverrideInvertedScale(info.applicationInvertedScale, |
| info.applicationDensityInvertedScale); |
| } else { |
| CompatibilityInfo.setOverrideInvertedScale(/* invertScale */ 1f, |
| /* densityInvertScale */1f); |
| } |
| } |
| |
| public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = entryPoint; |
| args.arg2 = entryPointArgs; |
| sendMessage(H.RUN_ISOLATED_ENTRY_POINT, args); |
| } |
| |
| public final void scheduleExit() { |
| sendMessage(H.EXIT_APPLICATION, null); |
| } |
| |
| public final void scheduleSuicide() { |
| sendMessage(H.SUICIDE, null); |
| } |
| |
| public void scheduleApplicationInfoChanged(ApplicationInfo ai) { |
| synchronized (mResourcesManager) { |
| var oldAi = mPendingAppInfoUpdates.put(ai.packageName, ai); |
| if (oldAi != null && oldAi.createTimestamp > ai.createTimestamp) { |
| Slog.w(TAG, "Skipping application info changed for obsolete AI with TS " |
| + ai.createTimestamp + " < already pending TS " |
| + oldAi.createTimestamp); |
| mPendingAppInfoUpdates.put(ai.packageName, oldAi); |
| return; |
| } |
| } |
| mResourcesManager.appendPendingAppInfoUpdate(new String[]{ai.sourceDir}, ai); |
| mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai.packageName); |
| sendMessage(H.APPLICATION_INFO_CHANGED, ai.packageName); |
| } |
| |
| public void updateTimeZone() { |
| TimeZone.setDefault(null); |
| } |
| |
| public void clearDnsCache() { |
| // a non-standard API to get this to libcore |
| InetAddress.clearDnsCache(); |
| // Allow libcore to perform the necessary actions as it sees fit upon a network |
| // configuration change. |
| NetworkEventDispatcher.getInstance().dispatchNetworkConfigurationChange(); |
| } |
| |
| public void updateHttpProxy() { |
| final Application app; |
| synchronized (ActivityThread.this) { |
| app = getApplication(); |
| if (null == app) { |
| // The app is not bound yet. Make a note to update the HTTP proxy when the |
| // app is bound. |
| mUpdateHttpProxyOnBind = true; |
| return; |
| } |
| } |
| // App is present, update the proxy inline. |
| ActivityThread.updateHttpProxy(app); |
| } |
| |
| public void processInBackground() { |
| mH.removeMessages(H.GC_WHEN_IDLE); |
| mH.sendMessage(mH.obtainMessage(H.GC_WHEN_IDLE)); |
| } |
| |
| public void dumpService(ParcelFileDescriptor pfd, IBinder servicetoken, String[] args) { |
| DumpComponentInfo data = new DumpComponentInfo(); |
| try { |
| data.fd = pfd.dup(); |
| data.token = servicetoken; |
| data.args = args; |
| sendMessage(H.DUMP_SERVICE, data, 0, 0, true /*async*/); |
| } catch (IOException e) { |
| Slog.w(TAG, "dumpService failed", e); |
| } finally { |
| IoUtils.closeQuietly(pfd); |
| } |
| } |
| |
| // This function exists to make sure all receiver dispatching is |
| // correctly ordered, since these are one-way calls and the binder driver |
| // applies transaction ordering per object for such calls. |
| public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, |
| int resultCode, String dataStr, Bundle extras, boolean ordered, |
| boolean sticky, boolean assumeDelivered, int sendingUser, int processState, |
| int sendingUid, String sendingPackage) |
| throws RemoteException { |
| updateProcessState(processState, false); |
| |
| // We can't modify IIntentReceiver due to UnsupportedAppUsage, so |
| // try our best to shortcut to known subclasses, and alert if |
| // registered using a custom IIntentReceiver that isn't able to |
| // report an expected delivery event |
| if (receiver instanceof LoadedApk.ReceiverDispatcher.InnerReceiver) { |
| ((LoadedApk.ReceiverDispatcher.InnerReceiver) receiver).performReceive(intent, |
| resultCode, dataStr, extras, ordered, sticky, assumeDelivered, sendingUser, |
| sendingUid, sendingPackage); |
| } else { |
| if (!assumeDelivered) { |
| Log.wtf(TAG, "scheduleRegisteredReceiver() called for " + receiver |
| + " and " + intent + " without mechanism to finish delivery"); |
| } |
| if (sendingUid != Process.INVALID_UID || sendingPackage != null) { |
| Log.wtf(TAG, |
| "scheduleRegisteredReceiver() called for " + receiver + " and " + intent |
| + " from " + sendingPackage + " (UID: " + sendingUid |
| + ") without mechanism to propagate the sender's identity"); |
| } |
| receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky, |
| sendingUser); |
| } |
| } |
| |
| @Override |
| public void scheduleLowMemory() { |
| sendMessage(H.LOW_MEMORY, null); |
| } |
| |
| @Override |
| public void profilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) { |
| sendMessage(H.PROFILER_CONTROL, profilerInfo, start ? 1 : 0, profileType); |
| } |
| |
| @Override |
| public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String dumpBitmaps, |
| String path, ParcelFileDescriptor fd, RemoteCallback finishCallback) { |
| DumpHeapData dhd = new DumpHeapData(); |
| dhd.managed = managed; |
| dhd.mallocInfo = mallocInfo; |
| dhd.dumpBitmaps = dumpBitmaps; |
| dhd.runGc = runGc; |
| dhd.path = path; |
| try { |
| // Since we're going to dump the heap asynchronously, dup the file descriptor before |
| // it's closed on returning from the IPC call. |
| dhd.fd = fd.dup(); |
| } catch (IOException e) { |
| Slog.e(TAG, "Failed to duplicate heap dump file descriptor", e); |
| return; |
| } finally { |
| IoUtils.closeQuietly(fd); |
| } |
| dhd.finishCallback = finishCallback; |
| sendMessage(H.DUMP_HEAP, dhd, 0, 0, true /*async*/); |
| } |
| |
| public void attachAgent(String agent) { |
| sendMessage(H.ATTACH_AGENT, agent); |
| } |
| |
| public void attachStartupAgents(String dataDir) { |
| sendMessage(H.ATTACH_STARTUP_AGENTS, dataDir); |
| } |
| |
| public void setSchedulingGroup(int group) { |
| // Note: do this immediately, since going into the foreground |
| // should happen regardless of what pending work we have to do |
| // and the activity manager will wait for us to report back that |
| // we are done before sending us to the background. |
| try { |
| Process.setProcessGroup(Process.myPid(), group); |
| } catch (Exception e) { |
| Slog.w(TAG, "Failed setting process group to " + group, e); |
| } |
| } |
| |
| public void dispatchPackageBroadcast(int cmd, String[] packages) { |
| sendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd); |
| } |
| |
| @Override |
| public void scheduleCrash(String msg, int typeId, @Nullable Bundle extras) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = msg; |
| args.arg2 = extras; |
| sendMessage(H.SCHEDULE_CRASH, args, typeId); |
| } |
| |
| @Override |
| public void dumpResources(ParcelFileDescriptor fd, RemoteCallback callback) { |
| DumpResourcesData data = new DumpResourcesData(); |
| try { |
| data.fd = fd.dup(); |
| data.finishCallback = callback; |
| sendMessage(H.DUMP_RESOURCES, data, 0, 0, false /*async*/); |
| } catch (IOException e) { |
| Slog.w(TAG, "dumpResources failed", e); |
| } finally { |
| IoUtils.closeQuietly(fd); |
| } |
| } |
| |
| public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken, |
| String prefix, String[] args) { |
| DumpComponentInfo data = new DumpComponentInfo(); |
| try { |
| data.fd = pfd.dup(); |
| data.token = activitytoken; |
| data.prefix = prefix; |
| data.args = args; |
| sendMessage(H.DUMP_ACTIVITY, data, 0, 0, true /*async*/); |
| } catch (IOException e) { |
| Slog.w(TAG, "dumpActivity failed", e); |
| } finally { |
| IoUtils.closeQuietly(pfd); |
| } |
| } |
| |
| public void dumpProvider(ParcelFileDescriptor pfd, IBinder providertoken, |
| String[] args) { |
| DumpComponentInfo data = new DumpComponentInfo(); |
| try { |
| data.fd = pfd.dup(); |
| data.token = providertoken; |
| data.args = args; |
| sendMessage(H.DUMP_PROVIDER, data, 0, 0, true /*async*/); |
| } catch (IOException e) { |
| Slog.w(TAG, "dumpProvider failed", e); |
| } finally { |
| IoUtils.closeQuietly(pfd); |
| } |
| } |
| |
| @NeverCompile |
| @Override |
| public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin, |
| boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, |
| boolean dumpUnreachable, boolean dumpAllocatorStats, String[] args) { |
| FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor()); |
| PrintWriter pw = new FastPrintWriter(fout); |
| try { |
| dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, |
| dumpUnreachable, dumpAllocatorStats); |
| } finally { |
| pw.flush(); |
| IoUtils.closeQuietly(pfd); |
| } |
| } |
| |
| @NeverCompile |
| private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, |
| boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, |
| boolean dumpUnreachable, boolean dumpAllocatorStats) { |
| long nativeMax = Debug.getNativeHeapSize() / 1024; |
| long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; |
| long nativeFree = Debug.getNativeHeapFreeSize() / 1024; |
| |
| Runtime runtime = Runtime.getRuntime(); |
| runtime.gc(); // Do GC since countInstancesOfClass counts unreachable objects. |
| long dalvikMax = runtime.totalMemory() / 1024; |
| long dalvikFree = runtime.freeMemory() / 1024; |
| long dalvikAllocated = dalvikMax - dalvikFree; |
| |
| Class[] classesToCount = new Class[] { |
| ContextImpl.class, |
| Activity.class, |
| WebView.class, |
| View.class, |
| ViewRootImpl.class |
| }; |
| long[] instanceCounts = VMDebug.countInstancesOfClasses(classesToCount, true); |
| long appContextInstanceCount = instanceCounts[0]; |
| long activityInstanceCount = instanceCounts[1]; |
| long webviewInstanceCount = instanceCounts[2]; |
| long viewInstanceCount = instanceCounts[3]; |
| long viewRootInstanceCount = instanceCounts[4]; |
| |
| int globalAssetCount = AssetManager.getGlobalAssetCount(); |
| int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount(); |
| int binderLocalObjectCount = Debug.getBinderLocalObjectCount(); |
| int binderProxyObjectCount = Debug.getBinderProxyObjectCount(); |
| int binderDeathObjectCount = Debug.getBinderDeathObjectCount(); |
| long parcelSize = Parcel.getGlobalAllocSize(); |
| long parcelCount = Parcel.getGlobalAllocCount(); |
| SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); |
| |
| dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, |
| Process.myPid(), |
| (mBoundApplication != null) ? mBoundApplication.processName : "unknown", |
| nativeMax, nativeAllocated, nativeFree, |
| dalvikMax, dalvikAllocated, dalvikFree); |
| |
| if (checkin) { |
| // NOTE: if you change anything significant below, also consider changing |
| // ACTIVITY_THREAD_CHECKIN_VERSION. |
| |
| // Object counts |
| pw.print(viewInstanceCount); pw.print(','); |
| pw.print(viewRootInstanceCount); pw.print(','); |
| pw.print(appContextInstanceCount); pw.print(','); |
| pw.print(activityInstanceCount); pw.print(','); |
| |
| pw.print(globalAssetCount); pw.print(','); |
| pw.print(globalAssetManagerCount); pw.print(','); |
| pw.print(binderLocalObjectCount); pw.print(','); |
| pw.print(binderProxyObjectCount); pw.print(','); |
| |
| pw.print(binderDeathObjectCount); pw.print(','); |
| |
| // SQL |
| pw.print(stats.memoryUsed / 1024); pw.print(','); |
| pw.print(stats.memoryUsed / 1024); pw.print(','); |
| pw.print(stats.pageCacheOverflow / 1024); pw.print(','); |
| pw.print(stats.largestMemAlloc / 1024); |
| for (int i = 0; i < stats.dbStats.size(); i++) { |
| DbStats dbStats = stats.dbStats.get(i); |
| pw.print(','); pw.print(dbStats.dbName); |
| pw.print(','); pw.print(dbStats.pageSize); |
| pw.print(','); pw.print(dbStats.dbSize); |
| pw.print(','); pw.print(dbStats.lookaside); |
| pw.print(','); pw.print(dbStats.cacheHits); |
| pw.print(','); pw.print(dbStats.cacheMisses); |
| pw.print(','); pw.print(dbStats.cacheSize); |
| } |
| pw.println(); |
| |
| return; |
| } |
| |
| pw.println(" "); |
| pw.println(" Objects"); |
| printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRootImpl:", |
| viewRootInstanceCount); |
| |
| printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount, |
| "Activities:", activityInstanceCount); |
| |
| printRow(pw, TWO_COUNT_COLUMNS, "Assets:", globalAssetCount, |
| "AssetManagers:", globalAssetManagerCount); |
| |
| printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", binderLocalObjectCount, |
| "Proxy Binders:", binderProxyObjectCount); |
| printRow(pw, TWO_COUNT_COLUMNS, "Parcel memory:", parcelSize/1024, |
| "Parcel count:", parcelCount); |
| printRow(pw, TWO_COUNT_COLUMNS, "Death Recipients:", binderDeathObjectCount, |
| "WebViews:", webviewInstanceCount); |
| |
| // SQLite mem info |
| pw.println(" "); |
| pw.println(" SQL"); |
| printRow(pw, ONE_COUNT_COLUMN, "MEMORY_USED:", stats.memoryUsed / 1024); |
| printRow(pw, TWO_COUNT_COLUMNS, "PAGECACHE_OVERFLOW:", |
| stats.pageCacheOverflow / 1024, "MALLOC_SIZE:", stats.largestMemAlloc / 1024); |
| pw.println(" "); |
| int N = stats.dbStats.size(); |
| if (N > 0) { |
| pw.println(" DATABASES"); |
| printRow(pw, DB_CONNECTION_INFO_HEADER, "pgsz", "dbsz", "Lookaside(b)", |
| "cache hits", "cache misses", "cache size", "Dbname"); |
| pw.println("PER CONNECTION STATS"); |
| for (int i = 0; i < N; i++) { |
| DbStats dbStats = stats.dbStats.get(i); |
| if (dbStats.arePoolStats) { |
| // these will be printed after |
| continue; |
| } |
| printRow(pw, DB_CONNECTION_INFO_FORMAT, |
| (dbStats.pageSize > 0) ? String.valueOf(dbStats.pageSize) : " ", |
| (dbStats.dbSize > 0) ? String.valueOf(dbStats.dbSize) : " ", |
| (dbStats.lookaside > 0) ? String.valueOf(dbStats.lookaside) : " ", |
| dbStats.cacheHits, dbStats.cacheMisses, dbStats.cacheSize, |
| dbStats.dbName); |
| } |
| // Print stats accumulated through all the connections that have existed in the |
| // pool since it was opened. |
| pw.println("POOL STATS"); |
| printRow(pw, DB_POOL_INFO_HEADER, "cache hits", "cache misses", "cache size", |
| "Dbname"); |
| for (int i = 0; i < N; i++) { |
| DbStats dbStats = stats.dbStats.get(i); |
| if (!dbStats.arePoolStats) { |
| continue; |
| } |
| printRow(pw, DB_POOL_INFO_FORMAT, dbStats.cacheHits, dbStats.cacheMisses, |
| dbStats.cacheSize, dbStats.dbName); |
| } |
| } |
| |
| // Asset details. |
| String assetAlloc = AssetManager.getAssetAllocations(); |
| if (assetAlloc != null) { |
| pw.println(" "); |
| pw.println(" Asset Allocations"); |
| pw.print(assetAlloc); |
| } |
| |
| // Unreachable native memory |
| if (dumpUnreachable) { |
| boolean showContents = ((mBoundApplication != null) |
| && ((mBoundApplication.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0)) |
| || android.os.Build.IS_DEBUGGABLE; |
| pw.println(" "); |
| pw.println(" Unreachable memory"); |
| pw.print(Debug.getUnreachableMemory(100, showContents)); |
| } |
| if (dumpAllocatorStats) { |
| Debug.logAllocatorStats(); |
| } |
| } |
| |
| @NeverCompile |
| @Override |
| public void dumpMemInfoProto(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, |
| boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, |
| boolean dumpUnreachable, String[] args) { |
| ProtoOutputStream proto = new ProtoOutputStream(pfd.getFileDescriptor()); |
| try { |
| dumpMemInfo(proto, mem, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable); |
| } finally { |
| proto.flush(); |
| IoUtils.closeQuietly(pfd); |
| } |
| } |
| |
| @NeverCompile |
| private void dumpMemInfo(ProtoOutputStream proto, Debug.MemoryInfo memInfo, |
| boolean dumpFullInfo, boolean dumpDalvik, |
| boolean dumpSummaryOnly, boolean dumpUnreachable) { |
| long nativeMax = Debug.getNativeHeapSize() / 1024; |
| long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; |
| long nativeFree = Debug.getNativeHeapFreeSize() / 1024; |
| |
| Runtime runtime = Runtime.getRuntime(); |
| runtime.gc(); // Do GC since countInstancesOfClass counts unreachable objects. |
| long dalvikMax = runtime.totalMemory() / 1024; |
| long dalvikFree = runtime.freeMemory() / 1024; |
| long dalvikAllocated = dalvikMax - dalvikFree; |
| |
| Class[] classesToCount = new Class[] { |
| ContextImpl.class, |
| Activity.class, |
| WebView.class, |
| View.class, |
| ViewRootImpl.class |
| }; |
| long[] instanceCounts = VMDebug.countInstancesOfClasses(classesToCount, true); |
| long appContextInstanceCount = instanceCounts[0]; |
| long activityInstanceCount = instanceCounts[1]; |
| long webviewInstanceCount = instanceCounts[2]; |
| long viewInstanceCount = instanceCounts[3]; |
| long viewRootInstanceCount = instanceCounts[4]; |
| |
| int globalAssetCount = AssetManager.getGlobalAssetCount(); |
| int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount(); |
| int binderLocalObjectCount = Debug.getBinderLocalObjectCount(); |
| int binderProxyObjectCount = Debug.getBinderProxyObjectCount(); |
| int binderDeathObjectCount = Debug.getBinderDeathObjectCount(); |
| long parcelSize = Parcel.getGlobalAllocSize(); |
| long parcelCount = Parcel.getGlobalAllocCount(); |
| SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); |
| |
| final long mToken = proto.start(MemInfoDumpProto.AppData.PROCESS_MEMORY); |
| proto.write(MemInfoDumpProto.ProcessMemory.PID, Process.myPid()); |
| proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, |
| (mBoundApplication != null) ? mBoundApplication.processName : "unknown"); |
| dumpMemInfoTable(proto, memInfo, dumpDalvik, dumpSummaryOnly, |
| nativeMax, nativeAllocated, nativeFree, |
| dalvikMax, dalvikAllocated, dalvikFree); |
| proto.end(mToken); |
| |
| final long oToken = proto.start(MemInfoDumpProto.AppData.OBJECTS); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_INSTANCE_COUNT, |
| viewInstanceCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.VIEW_ROOT_INSTANCE_COUNT, |
| viewRootInstanceCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.APP_CONTEXT_INSTANCE_COUNT, |
| appContextInstanceCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.ACTIVITY_INSTANCE_COUNT, |
| activityInstanceCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_COUNT, |
| globalAssetCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.GLOBAL_ASSET_MANAGER_COUNT, |
| globalAssetManagerCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.LOCAL_BINDER_OBJECT_COUNT, |
| binderLocalObjectCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.PROXY_BINDER_OBJECT_COUNT, |
| binderProxyObjectCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_MEMORY_KB, |
| parcelSize / 1024); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.PARCEL_COUNT, parcelCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.BINDER_OBJECT_DEATH_COUNT, |
| binderDeathObjectCount); |
| proto.write(MemInfoDumpProto.AppData.ObjectStats.WEBVIEW_INSTANCE_COUNT, |
| webviewInstanceCount); |
| proto.end(oToken); |
| |
| // SQLite mem info |
| final long sToken = proto.start(MemInfoDumpProto.AppData.SQL); |
| proto.write(MemInfoDumpProto.AppData.SqlStats.MEMORY_USED_KB, |
| stats.memoryUsed / 1024); |
| proto.write(MemInfoDumpProto.AppData.SqlStats.PAGECACHE_OVERFLOW_KB, |
| stats.pageCacheOverflow / 1024); |
| proto.write(MemInfoDumpProto.AppData.SqlStats.MALLOC_SIZE_KB, |
| stats.largestMemAlloc / 1024); |
| int n = stats.dbStats.size(); |
| for (int i = 0; i < n; i++) { |
| DbStats dbStats = stats.dbStats.get(i); |
| |
| final long dToken = proto.start(MemInfoDumpProto.AppData.SqlStats.DATABASES); |
| proto.write(MemInfoDumpProto.AppData.SqlStats.Database.NAME, dbStats.dbName); |
| proto.write(MemInfoDumpProto.AppData.SqlStats.Database.PAGE_SIZE, dbStats.pageSize); |
| proto.write(MemInfoDumpProto.AppData.SqlStats.Database.DB_SIZE, dbStats.dbSize); |
| proto.write(MemInfoDumpProto.AppData.SqlStats.Database.LOOKASIDE_B, |
| dbStats.lookaside); |
| proto.write( |
| MemInfoDumpProto.AppData.SqlStats.Database.CACHE_HITS, dbStats.cacheHits); |
| proto.write(MemInfoDumpProto.AppData.SqlStats.Database.CACHE_MISSES, |
| dbStats.cacheMisses); |
| proto.write( |
| MemInfoDumpProto.AppData.SqlStats.Database.CACHE_SIZE, dbStats.cacheSize); |
| proto.end(dToken); |
| } |
| proto.end(sToken); |
| |
| // Asset details. |
| String assetAlloc = AssetManager.getAssetAllocations(); |
| if (assetAlloc != null) { |
| proto.write(MemInfoDumpProto.AppData.ASSET_ALLOCATIONS, assetAlloc); |
| } |
| |
| // Unreachable native memory |
| if (dumpUnreachable) { |
| int flags = mBoundApplication == null ? 0 : mBoundApplication.appInfo.flags; |
| boolean showContents = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 |
| || android.os.Build.IS_DEBUGGABLE; |
| proto.write(MemInfoDumpProto.AppData.UNREACHABLE_MEMORY, |
| Debug.getUnreachableMemory(100, showContents)); |
| } |
| } |
| |
| @Override |
| public void dumpGfxInfo(ParcelFileDescriptor pfd, String[] args) { |
| DumpComponentInfo data = new DumpComponentInfo(); |
| try { |
| data.fd = pfd.dup(); |
| data.token = null; |
| data.args = args; |
| sendMessage(H.DUMP_GFXINFO, data, 0, 0, true /*async*/); |
| } catch (IOException e) { |
| Slog.w(TAG, "dumpGfxInfo failed", e); |
| } finally { |
| IoUtils.closeQuietly(pfd); |
| } |
| } |
| |
| @Override |
| public void dumpCacheInfo(ParcelFileDescriptor pfd, String[] args) { |
| PropertyInvalidatedCache.dumpCacheInfo(pfd, args); |
| IoUtils.closeQuietly(pfd); |
| } |
| |
| private File getDatabasesDir(Context context) { |
| // There's no simple way to get the databases/ path, so do it this way. |
| return context.getDatabasePath("a").getParentFile(); |
| } |
| |
| private void dumpDatabaseInfo(ParcelFileDescriptor pfd, String[] args, boolean isSystem) { |
| PrintWriter pw = new FastPrintWriter( |
| new FileOutputStream(pfd.getFileDescriptor())); |
| PrintWriterPrinter printer = new PrintWriterPrinter(pw); |
| SQLiteDebug.dump(printer, args, isSystem); |
| pw.flush(); |
| } |
| |
| @Override |
| public void dumpDbInfo(final ParcelFileDescriptor pfd, final String[] args) { |
| if (mSystemThread) { |
| // Ensure this invocation is asynchronous to prevent writer waiting if buffer cannot |
| // be consumed. But it must duplicate the file descriptor first, since caller might |
| // be closing it. |
| final ParcelFileDescriptor dup; |
| try { |
| dup = pfd.dup(); |
| } catch (IOException e) { |
| Log.w(TAG, "Could not dup FD " + pfd.getFileDescriptor().getInt$()); |
| return; |
| } finally { |
| IoUtils.closeQuietly(pfd); |
| } |
| |
| AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| dumpDatabaseInfo(dup, args, true); |
| } finally { |
| IoUtils.closeQuietly(dup); |
| } |
| } |
| }); |
| } else { |
| dumpDatabaseInfo(pfd, args, false); |
| IoUtils.closeQuietly(pfd); |
| } |
| } |
| |
| @Override |
| public void unstableProviderDied(IBinder provider) { |
| sendMessage(H.UNSTABLE_PROVIDER_DIED, provider); |
| } |
| |
| @Override |
| public void requestAssistContextExtras(IBinder activityToken, IBinder requestToken, |
| int requestType, int sessionId, int flags) { |
| RequestAssistContextExtras cmd = new RequestAssistContextExtras(); |
| cmd.activityToken = activityToken; |
| cmd.requestToken = requestToken; |
| cmd.requestType = requestType; |
| cmd.sessionId = sessionId; |
| cmd.flags = flags; |
| sendMessage(H.REQUEST_ASSIST_CONTEXT_EXTRAS, cmd); |
| } |
| |
| public void setCoreSettings(Bundle coreSettings) { |
| sendMessage(H.SET_CORE_SETTINGS, coreSettings); |
| } |
| |
| public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) { |
| UpdateCompatibilityData ucd = new UpdateCompatibilityData(); |
| ucd.pkg = pkg; |
| ucd.info = info; |
| updateCompatOverrideScale(info); |
| sendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd); |
| } |
| |
| public void scheduleTrimMemory(int level) { |
| final Runnable r = PooledLambda.obtainRunnable(ActivityThread::handleTrimMemory, |
| ActivityThread.this, level).recycleOnUse(); |
| // Schedule trimming memory after drawing the frame to minimize jank-risk. |
| Choreographer choreographer = Choreographer.getMainThreadInstance(); |
| if (choreographer != null) { |
| choreographer.postCallback(Choreographer.CALLBACK_COMMIT, r, null); |
| } else { |
| mH.post(r); |
| } |
| } |
| |
| public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) { |
| sendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0); |
| } |
| |
| public void scheduleOnNewSceneTransitionInfo(IBinder token, SceneTransitionInfo info) { |
| sendMessage(H.ON_NEW_SCENE_TRANSITION_INFO, |
| new Pair<IBinder, SceneTransitionInfo>(token, info)); |
| } |
| |
| public void setProcessState(int state) { |
| updateProcessState(state, true); |
| } |
| |
| /** |
| * Updates {@link #mNetworkBlockSeq}. This is used by ActivityManagerService to inform |
| * the main thread that it needs to wait for the network rules to get updated before |
| * launching an activity. |
| */ |
| @Override |
| public void setNetworkBlockSeq(long procStateSeq) { |
| synchronized (mNetworkPolicyLock) { |
| mNetworkBlockSeq = procStateSeq; |
| } |
| } |
| |
| @Override |
| public void scheduleInstallProvider(ProviderInfo provider) { |
| sendMessage(H.INSTALL_PROVIDER, provider); |
| } |
| |
| @Override |
| public final void updateTimePrefs(int timeFormatPreference) { |
| final Boolean timeFormatPreferenceBool; |
| // For convenience we are using the Intent extra values. |
| if (timeFormatPreference == Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR) { |
| timeFormatPreferenceBool = Boolean.FALSE; |
| } else if (timeFormatPreference == Intent.EXTRA_TIME_PREF_VALUE_USE_24_HOUR) { |
| timeFormatPreferenceBool = Boolean.TRUE; |
| } else { |
| // timeFormatPreference == Intent.EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT |
| // (or unknown). |
| timeFormatPreferenceBool = null; |
| } |
| DateFormat.set24HourTimePref(timeFormatPreferenceBool); |
| } |
| |
| @Override |
| public void scheduleEnterAnimationComplete(IBinder token) { |
| sendMessage(H.ENTER_ANIMATION_COMPLETE, token); |
| } |
| |
| @Override |
| public void notifyCleartextNetwork(byte[] firstPacket) { |
| if (StrictMode.vmCleartextNetworkEnabled()) { |
| StrictMode.onCleartextNetworkDetected(firstPacket); |
| } |
| } |
| |
| @Override |
| public void startBinderTracking() { |
| sendMessage(H.START_BINDER_TRACKING, null); |
| } |
| |
| @Override |
| public void stopBinderTrackingAndDump(ParcelFileDescriptor pfd) { |
| try { |
| sendMessage(H.STOP_BINDER_TRACKING_AND_DUMP, pfd.dup()); |
| } catch (IOException e) { |
| } finally { |
| IoUtils.closeQuietly(pfd); |
| } |
| } |
| |
| @Override |
| public void scheduleLocalVoiceInteractionStarted(IBinder token, |
| IVoiceInteractor voiceInteractor) throws RemoteException { |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = token; |
| args.arg2 = voiceInteractor; |
| sendMessage(H.LOCAL_VOICE_INTERACTION_STARTED, args); |
| } |
| |
| @Override |
| public void handleTrustStorageUpdate() { |
| NetworkSecurityPolicy.getInstance().handleTrustStorageUpdate(); |
| } |
| |
| @Override |
| public void scheduleTransaction(ClientTransaction transaction) throws RemoteException { |
| ActivityThread.this.scheduleTransaction(transaction); |
| } |
| |
| @Override |
| public void scheduleTaskFragmentTransaction(@NonNull ITaskFragmentOrganizer organizer, |
| @NonNull TaskFragmentTransaction transaction) throws RemoteException { |
| // TODO(b/260873529): ITaskFragmentOrganizer can be cleanup to be a IBinder token |
| // after flag removal. |
| organizer.onTransactionReady(transaction); |
| } |
| |
| @Override |
| public void requestDirectActions(@NonNull IBinder activityToken, |
| @NonNull IVoiceInteractor interactor, @Nullable RemoteCallback cancellationCallback, |
| @NonNull RemoteCallback callback) { |
| final CancellationSignal cancellationSignal = new CancellationSignal(); |
| if (cancellationCallback != null) { |
| final ICancellationSignal transport = createSafeCancellationTransport( |
| cancellationSignal); |
| final Bundle cancellationResult = new Bundle(); |
| cancellationResult.putBinder(VoiceInteractor.KEY_CANCELLATION_SIGNAL, |
| transport.asBinder()); |
| cancellationCallback.sendResult(cancellationResult); |
| } |
| mH.sendMessage(PooledLambda.obtainMessage(ActivityThread::handleRequestDirectActions, |
| ActivityThread.this, activityToken, interactor, cancellationSignal, callback, |
| REQUEST_DIRECT_ACTIONS_RETRY_MAX_COUNT)); |
| } |
| |
| @Override |
| public void performDirectAction(@NonNull IBinder activityToken, @NonNull String actionId, |
| @Nullable Bundle arguments, @Nullable RemoteCallback cancellationCallback, |
| @NonNull RemoteCallback resultCallback) { |
| final CancellationSignal cancellationSignal = new CancellationSignal(); |
| if (cancellationCallback != null) { |
| final ICancellationSignal transport = createSafeCancellationTransport( |
| cancellationSignal); |
| final Bundle cancellationResult = new Bundle(); |
| cancellationResult.putBinder(VoiceInteractor.KEY_CANCELLATION_SIGNAL, |
| transport.asBinder()); |
| cancellationCallback.sendResult(cancellationResult); |
| } |
| mH.sendMessage(PooledLambda.obtainMessage(ActivityThread::handlePerformDirectAction, |
| ActivityThread.this, activityToken, actionId, arguments, |
| cancellationSignal, resultCallback)); |
| } |
| |
| @Override |
| public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder, |
| @NonNull String authorities, int userId, boolean published) { |
| final String auths[] = authorities.split(";"); |
| for (String auth: auths) { |
| final ProviderKey key = getGetProviderKey(auth, userId); |
| synchronized (key.mLock) { |
| key.mHolder = holder; |
| key.mLock.notifyAll(); |
| } |
| } |
| } |
| |
| @Override |
| public void instrumentWithoutRestart(ComponentName instrumentationName, |
| Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, |
| IUiAutomationConnection instrumentationUiConnection, ApplicationInfo targetInfo) { |
| AppBindData data = new AppBindData(); |
| data.instrumentationName = instrumentationName; |
| data.instrumentationArgs = instrumentationArgs; |
| data.instrumentationWatcher = instrumentationWatcher; |
| data.instrumentationUiAutomationConnection = instrumentationUiConnection; |
| data.appInfo = targetInfo; |
| sendMessage(H.INSTRUMENT_WITHOUT_RESTART, data); |
| } |
| |
| @Override |
| public void updateUiTranslationState(IBinder activityToken, int state, |
| TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds, |
| UiTranslationSpec uiTranslationSpec) { |
| SomeArgs args = SomeArgs.obtain(); |
| args.arg1 = activityToken; |
| args.arg2 = state; |
| args.arg3 = sourceSpec; |
| args.arg4 = targetSpec; |
| args.arg5 = viewIds; |
| args.arg6 = uiTranslationSpec; |
| sendMessage(H.UPDATE_UI_TRANSLATION_STATE, args); |
| } |
| } |
| |
| private @NonNull SafeCancellationTransport createSafeCancellationTransport( |
| @NonNull CancellationSignal cancellationSignal) { |
| synchronized (ActivityThread.this) { |
| if (mRemoteCancellations == null) { |
| mRemoteCancellations = new ArrayMap<>(); |
| } |
| final SafeCancellationTransport transport = new SafeCancellationTransport( |
| this, cancellationSignal); |
| mRemoteCancellations.put(transport, cancellationSignal); |
| return transport; |
| } |
| } |
| |
| private @NonNull CancellationSignal removeSafeCancellationTransport( |
| @NonNull SafeCancellationTransport transport) { |
| synchronized (ActivityThread.this) { |
| final CancellationSignal cancellation = mRemoteCancellations.remove(transport); |
| if (mRemoteCancellations.isEmpty()) { |
| mRemoteCancellations = null; |
| } |
| return cancellation; |
| } |
| } |
| |
| private static final class SafeCancellationTransport extends ICancellationSignal.Stub { |
| private final @NonNull WeakReference<ActivityThread> mWeakActivityThread; |
| |
| SafeCancellationTransport(@NonNull ActivityThread activityThread, |
| @NonNull CancellationSignal cancellation) { |
| mWeakActivityThread = new WeakReference<>(activityThread); |
| } |
| |
| @Override |
| public void cancel() { |
| final ActivityThread activityThread = mWeakActivityThread.get(); |
| if (activityThread != null) { |
| final CancellationSignal cancellation = activityThread |
| .removeSafeCancellationTransport(this); |
| if (cancellation != null) { |
| cancellation.cancel(); |
| } |
| } |
| } |
| } |
| |
| private void throwRemoteServiceException(String message, int typeId, @Nullable Bundle extras) { |
| // Use a switch to ensure all the type IDs are unique. |
| switch (typeId) { |
| case ForegroundServiceDidNotStartInTimeException.TYPE_ID: |
| throw generateForegroundServiceDidNotStartInTimeException(message, extras); |
| |
| case ForegroundServiceDidNotStopInTimeException.TYPE_ID: |
| throw generateForegroundServiceDidNotStopInTimeException(message, extras); |
| |
| case CannotPostForegroundServiceNotificationException.TYPE_ID: |
| throw new CannotPostForegroundServiceNotificationException(message); |
| |
| case BadForegroundServiceNotificationException.TYPE_ID: |
| throw new BadForegroundServiceNotificationException(message); |
| |
| case BadUserInitiatedJobNotificationException.TYPE_ID: |
| throw new BadUserInitiatedJobNotificationException(message); |
| |
| case MissingRequestPasswordComplexityPermissionException.TYPE_ID: |
| throw new MissingRequestPasswordComplexityPermissionException(message); |
| |
| case CrashedByAdbException.TYPE_ID: |
| throw new CrashedByAdbException(message); |
| |
| default: |
| throw new RemoteServiceException(message |
| + " (with unwknown typeId:" + typeId + ")"); |
| } |
| } |
| |
| private ForegroundServiceDidNotStartInTimeException |
| generateForegroundServiceDidNotStartInTimeException(String message, Bundle extras) { |
| final String serviceClassName = |
| ForegroundServiceDidNotStartInTimeException.getServiceClassNameFromExtras(extras); |
| final Exception inner = (serviceClassName == null) ? null |
| : Service.getStartForegroundServiceStackTrace(serviceClassName); |
| throw new ForegroundServiceDidNotStartInTimeException(message, inner); |
| } |
| |
| private ForegroundServiceDidNotStopInTimeException |
| generateForegroundServiceDidNotStopInTimeException(String message, Bundle extras) { |
| final String serviceClassName = |
| ForegroundServiceDidNotStopInTimeException.getServiceClassNameFromExtras(extras); |
| final Exception inner = (serviceClassName == null) ? null |
| : Service.getStartForegroundServiceStackTrace(serviceClassName); |
| throw new ForegroundServiceDidNotStopInTimeException(message, inner); |
| } |
| |
| class H extends Handler { |
| public static final int BIND_APPLICATION = 110; |
| @UnsupportedAppUsage |
| public static final int EXIT_APPLICATION = 111; |
| @UnsupportedAppUsage |
| public static final int RECEIVER = 113; |
| @UnsupportedAppUsage |
| public static final int CREATE_SERVICE = 114; |
| @UnsupportedAppUsage |
| public static final int SERVICE_ARGS = 115; |
| @UnsupportedAppUsage |
| public static final int STOP_SERVICE = 116; |
| |
| public static final int CONFIGURATION_CHANGED = 118; |
| public static final int CLEAN_UP_CONTEXT = 119; |
| @UnsupportedAppUsage |
| public static final int GC_WHEN_IDLE = 120; |
| @UnsupportedAppUsage |
| public static final int BIND_SERVICE = 121; |
| @UnsupportedAppUsage |
| public static final int UNBIND_SERVICE = 122; |
| public static final int DUMP_SERVICE = 123; |
| public static final int LOW_MEMORY = 124; |
| public static final int PROFILER_CONTROL = 127; |
| public static final int CREATE_BACKUP_AGENT = 128; |
| public static final int DESTROY_BACKUP_AGENT = 129; |
| public static final int SUICIDE = 130; |
| @UnsupportedAppUsage |
| public static final int REMOVE_PROVIDER = 131; |
| public static final int DISPATCH_PACKAGE_BROADCAST = 133; |
| @UnsupportedAppUsage |
| public static final int SCHEDULE_CRASH = 134; |
| public static final int DUMP_HEAP = 135; |
| public static final int DUMP_ACTIVITY = 136; |
| public static final int SLEEPING = 137; |
| public static final int SET_CORE_SETTINGS = 138; |
| public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139; |
| @UnsupportedAppUsage |
| public static final int DUMP_PROVIDER = 141; |
| public static final int UNSTABLE_PROVIDER_DIED = 142; |
| public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143; |
| public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144; |
| @UnsupportedAppUsage |
| public static final int INSTALL_PROVIDER = 145; |
| public static final int ON_NEW_SCENE_TRANSITION_INFO = 146; |
| @UnsupportedAppUsage |
| public static final int ENTER_ANIMATION_COMPLETE = 149; |
| public static final int START_BINDER_TRACKING = 150; |
| public static final int STOP_BINDER_TRACKING_AND_DUMP = 151; |
| public static final int LOCAL_VOICE_INTERACTION_STARTED = 154; |
| public static final int ATTACH_AGENT = 155; |
| public static final int APPLICATION_INFO_CHANGED = 156; |
| public static final int RUN_ISOLATED_ENTRY_POINT = 158; |
| public static final int EXECUTE_TRANSACTION = 159; |
| public static final int RELAUNCH_ACTIVITY = 160; |
| public static final int PURGE_RESOURCES = 161; |
| public static final int ATTACH_STARTUP_AGENTS = 162; |
| public static final int UPDATE_UI_TRANSLATION_STATE = 163; |
| public static final int SET_CONTENT_CAPTURE_OPTIONS_CALLBACK = 164; |
| public static final int DUMP_GFXINFO = 165; |
| public static final int DUMP_RESOURCES = 166; |
| public static final int TIMEOUT_SERVICE = 167; |
| public static final int PING = 168; |
| |
| public static final int INSTRUMENT_WITHOUT_RESTART = 170; |
| public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171; |
| |
| public static final int TIMEOUT_SERVICE_FOR_TYPE = 172; |
| |
| String codeToString(int code) { |
| if (DEBUG_MESSAGES) { |
| switch (code) { |
| case BIND_APPLICATION: return "BIND_APPLICATION"; |
| case EXIT_APPLICATION: return "EXIT_APPLICATION"; |
| case RECEIVER: return "RECEIVER"; |
| case CREATE_SERVICE: return "CREATE_SERVICE"; |
| case SERVICE_ARGS: return "SERVICE_ARGS"; |
| case STOP_SERVICE: return "STOP_SERVICE"; |
| case CONFIGURATION_CHANGED: return "CONFIGURATION_CHANGED"; |
| case CLEAN_UP_CONTEXT: return "CLEAN_UP_CONTEXT"; |
| case GC_WHEN_IDLE: return "GC_WHEN_IDLE"; |
| case BIND_SERVICE: return "BIND_SERVICE"; |
| case UNBIND_SERVICE: return "UNBIND_SERVICE"; |
| case DUMP_SERVICE: return "DUMP_SERVICE"; |
| case LOW_MEMORY: return "LOW_MEMORY"; |
| case PROFILER_CONTROL: return "PROFILER_CONTROL"; |
| case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT"; |
| case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT"; |
| case SUICIDE: return "SUICIDE"; |
| case REMOVE_PROVIDER: return "REMOVE_PROVIDER"; |
| case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST"; |
| case SCHEDULE_CRASH: return "SCHEDULE_CRASH"; |
| case DUMP_HEAP: return "DUMP_HEAP"; |
| case DUMP_ACTIVITY: return "DUMP_ACTIVITY"; |
| case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS"; |
| case UPDATE_PACKAGE_COMPATIBILITY_INFO: |
| return "UPDATE_PACKAGE_COMPATIBILITY_INFO"; |
| case DUMP_PROVIDER: return "DUMP_PROVIDER"; |
| case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED"; |
| case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS"; |
| case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE"; |
| case INSTALL_PROVIDER: return "INSTALL_PROVIDER"; |
| case ON_NEW_SCENE_TRANSITION_INFO: return "ON_NEW_SCENE_TRANSITION_INFO"; |
| case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE"; |
| case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED"; |
| case ATTACH_AGENT: return "ATTACH_AGENT"; |
| case APPLICATION_INFO_CHANGED: return "APPLICATION_INFO_CHANGED"; |
| case RUN_ISOLATED_ENTRY_POINT: return "RUN_ISOLATED_ENTRY_POINT"; |
| case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION"; |
| case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY"; |
| case PURGE_RESOURCES: return "PURGE_RESOURCES"; |
| case ATTACH_STARTUP_AGENTS: return "ATTACH_STARTUP_AGENTS"; |
| case UPDATE_UI_TRANSLATION_STATE: return "UPDATE_UI_TRANSLATION_STATE"; |
| case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK: |
| return "SET_CONTENT_CAPTURE_OPTIONS_CALLBACK"; |
| case DUMP_GFXINFO: return "DUMP GFXINFO"; |
| case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART"; |
| case FINISH_INSTRUMENTATION_WITHOUT_RESTART: |
| return "FINISH_INSTRUMENTATION_WITHOUT_RESTART"; |
| case DUMP_RESOURCES: return "DUMP_RESOURCES"; |
| case TIMEOUT_SERVICE: return "TIMEOUT_SERVICE"; |
| case PING: return "PING"; |
| case TIMEOUT_SERVICE_FOR_TYPE: return "TIMEOUT_SERVICE_FOR_TYPE"; |
| } |
| } |
| return Integer.toString(code); |
| } |
| public void handleMessage(Message msg) { |
| if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); |
| switch (msg.what) { |
| case BIND_APPLICATION: |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); |
| AppBindData data = (AppBindData)msg.obj; |
| handleBindApplication(data); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case EXIT_APPLICATION: |
| if (mInitialApplication != null) { |
| mInitialApplication.onTerminate(); |
| } |
| Looper.myLooper().quit(); |
| break; |
| case RECEIVER: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| ReceiverData rec = (ReceiverData) msg.obj; |
| if (rec.intent != null) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, |
| "broadcastReceiveComp: " + rec.intent.getAction()); |
| } else { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, |
| "broadcastReceiveComp"); |
| } |
| } |
| handleReceiver((ReceiverData)msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case CREATE_SERVICE: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, |
| ("serviceCreate: " + String.valueOf(msg.obj))); |
| } |
| handleCreateService((CreateServiceData)msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case BIND_SERVICE: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind: " |
| + String.valueOf(msg.obj)); |
| } |
| handleBindService((BindServiceData)msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case UNBIND_SERVICE: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind: " |
| + String.valueOf(msg.obj)); |
| } |
| handleUnbindService((BindServiceData)msg.obj); |
| schedulePurgeIdler(); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case SERVICE_ARGS: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, |
| ("serviceStart: " + String.valueOf(msg.obj))); |
| } |
| handleServiceArgs((ServiceArgsData)msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case STOP_SERVICE: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop: " |
| + String.valueOf(msg.obj)); |
| } |
| handleStopService((IBinder)msg.obj); |
| schedulePurgeIdler(); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case TIMEOUT_SERVICE: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout: " |
| + String.valueOf(msg.obj)); |
| } |
| handleTimeoutService((IBinder) msg.obj, msg.arg1); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case PING: |
| ((RemoteCallback) msg.obj).sendResult(null); |
| break; |
| case TIMEOUT_SERVICE_FOR_TYPE: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, |
| "serviceTimeoutForType: " + msg.obj); |
| } |
| handleTimeoutServiceForType((IBinder) msg.obj, msg.arg1, msg.arg2); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case CONFIGURATION_CHANGED: |
| mConfigurationController.handleConfigurationChanged((Configuration) msg.obj); |
| break; |
| case CLEAN_UP_CONTEXT: |
| ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj; |
| cci.context.performFinalCleanup(cci.who, cci.what); |
| break; |
| case GC_WHEN_IDLE: |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "gcWhenIdle"); |
| try { |
| scheduleGcIdler(); |
| } finally { |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| } |
| break; |
| case DUMP_SERVICE: |
| handleDumpService((DumpComponentInfo)msg.obj); |
| break; |
| case DUMP_GFXINFO: |
| handleDumpGfxInfo((DumpComponentInfo) msg.obj); |
| break; |
| case LOW_MEMORY: |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "lowMemory"); |
| handleLowMemory(); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case PROFILER_CONTROL: |
| handleProfilerControl(msg.arg1 != 0, (ProfilerInfo)msg.obj, msg.arg2); |
| break; |
| case CREATE_BACKUP_AGENT: |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backupCreateAgent"); |
| handleCreateBackupAgent((CreateBackupAgentData)msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case DESTROY_BACKUP_AGENT: |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backupDestroyAgent"); |
| handleDestroyBackupAgent((CreateBackupAgentData)msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case SUICIDE: |
| Process.killProcess(Process.myPid()); |
| break; |
| case REMOVE_PROVIDER: |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerRemove"); |
| completeRemoveProvider((ProviderRefCount)msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case DISPATCH_PACKAGE_BROADCAST: |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastPackage"); |
| handleDispatchPackageBroadcast(msg.arg1, (String[])msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case SCHEDULE_CRASH: { |
| SomeArgs args = (SomeArgs) msg.obj; |
| String message = (String) args.arg1; |
| Bundle extras = (Bundle) args.arg2; |
| args.recycle(); |
| throwRemoteServiceException(message, msg.arg1, extras); |
| break; |
| } |
| case DUMP_HEAP: |
| handleDumpHeap((DumpHeapData) msg.obj); |
| break; |
| case DUMP_RESOURCES: |
| handleDumpResources((DumpResourcesData) msg.obj); |
| break; |
| case DUMP_ACTIVITY: |
| handleDumpActivity((DumpComponentInfo)msg.obj); |
| break; |
| case DUMP_PROVIDER: |
| handleDumpProvider((DumpComponentInfo)msg.obj); |
| break; |
| case SET_CORE_SETTINGS: |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setCoreSettings"); |
| handleSetCoreSettings((Bundle) msg.obj); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| break; |
| case UPDATE_PACKAGE_COMPATIBILITY_INFO: |
| handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj); |
| break; |
| case UNSTABLE_PROVIDER_DIED: |
| handleUnstableProviderDied((IBinder)msg.obj, false); |
| break; |
| case REQUEST_ASSIST_CONTEXT_EXTRAS: |
| handleRequestAssistContextExtras((RequestAssistContextExtras)msg.obj); |
| break; |
| case TRANSLUCENT_CONVERSION_COMPLETE: |
| handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1); |
| break; |
| case INSTALL_PROVIDER: |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "providerInstall: " |
| + String.valueOf(msg.obj)); |
| } |
| try { |
| handleInstallProvider((ProviderInfo) msg.obj); |
| } finally { |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| } |
| break; |
| case ON_NEW_SCENE_TRANSITION_INFO: |
| Pair<IBinder, SceneTransitionInfo> pair = |
| (Pair<IBinder, SceneTransitionInfo>) msg.obj; |
| onNewSceneTransitionInfo(pair.first, pair.second); |
| break; |
| case ENTER_ANIMATION_COMPLETE: |
| handleEnterAnimationComplete((IBinder) msg.obj); |
| break; |
| case START_BINDER_TRACKING: |
| handleStartBinderTracking(); |
| break; |
| case STOP_BINDER_TRACKING_AND_DUMP: |
| handleStopBinderTrackingAndDump((ParcelFileDescriptor) msg.obj); |
| break; |
| case LOCAL_VOICE_INTERACTION_STARTED: |
| handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1, |
| (IVoiceInteractor) ((SomeArgs) msg.obj).arg2); |
| break; |
| case ATTACH_AGENT: { |
| Application app = getApplication(); |
| handleAttachAgent((String) msg.obj, app != null ? app.mLoadedApk : null); |
| break; |
| } |
| case APPLICATION_INFO_CHANGED: |
| applyPendingApplicationInfoChanges((String) msg.obj); |
| break; |
| case RUN_ISOLATED_ENTRY_POINT: |
| handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1, |
| (String[]) ((SomeArgs) msg.obj).arg2); |
| break; |
| case EXECUTE_TRANSACTION: |
| final ClientTransaction transaction = (ClientTransaction) msg.obj; |
| final ClientTransactionListenerController controller = |
| ClientTransactionListenerController.getInstance(); |
| controller.onClientTransactionStarted(); |
| try { |
| mTransactionExecutor.execute(transaction); |
| } finally { |
| controller.onClientTransactionFinished(); |
| } |
| if (isSystem()) { |
| // Client transactions inside system process are recycled on the client side |
| // instead of ClientLifecycleManager to avoid being cleared before this |
| // message is handled. |
| transaction.recycle(); |
| } |
| // TODO(lifecycler): Recycle locally scheduled transactions. |
| break; |
| case RELAUNCH_ACTIVITY: |
| handleRelaunchActivityLocally((IBinder) msg.obj); |
| break; |
| case PURGE_RESOURCES: |
| schedulePurgeIdler(); |
| break; |
| case ATTACH_STARTUP_AGENTS: |
| handleAttachStartupAgents((String) msg.obj); |
| break; |
| case UPDATE_UI_TRANSLATION_STATE: |
| final SomeArgs args = (SomeArgs) msg.obj; |
| updateUiTranslationState((IBinder) args.arg1, (int) args.arg2, |
| (TranslationSpec) args.arg3, (TranslationSpec) args.arg4, |
| (List<AutofillId>) args.arg5, (UiTranslationSpec) args.arg6); |
| break; |
| case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK: |
| handleSetContentCaptureOptionsCallback((String) msg.obj); |
| break; |
| case INSTRUMENT_WITHOUT_RESTART: |
| handleInstrumentWithoutRestart((AppBindData) msg.obj); |
| break; |
| case FINISH_INSTRUMENTATION_WITHOUT_RESTART: |
| handleFinishInstrumentationWithoutRestart(); |
| break; |
| } |
| Object obj = msg.obj; |
| if (obj instanceof SomeArgs) { |
| ((SomeArgs) obj).recycle(); |
| } |
| if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); |
| } |
| } |
| |
| private class Idler implements MessageQueue.IdleHandler { |
| @Override |
| public final boolean queueIdle() { |
| boolean stopProfiling = false; |
| if (mBoundApplication != null && mProfiler.profileFd != null |
| && mProfiler.autoStopProfiler) { |
| stopProfiling = true; |
| } |
| final ActivityClient ac = ActivityClient.getInstance(); |
| while (mNewActivities.size() > 0) { |
| final ActivityClientRecord a = mNewActivities.remove(0); |
| if (localLOGV) { |
| Slog.v(TAG, "Reporting idle of " + a + " finished=" |
| + (a.activity != null && a.activity.mFinished)); |
| } |
| if (a.activity != null && !a.activity.mFinished) { |
| ac.activityIdle(a.token, a.createdConfig, stopProfiling); |
| a.createdConfig = null; |
| } |
| } |
| if (stopProfiling) { |
| mProfiler.stopProfiling(); |
| } |
| return false; |
| } |
| } |
| |
| final class GcIdler implements MessageQueue.IdleHandler { |
| @Override |
| public final boolean queueIdle() { |
| doGcIfNeeded(); |
| purgePendingResources(); |
| return false; |
| } |
| } |
| |
| final class PurgeIdler implements MessageQueue.IdleHandler { |
| @Override |
| public boolean queueIdle() { |
| purgePendingResources(); |
| return false; |
| } |
| } |
| |
| @UnsupportedAppUsage |
| public static ActivityThread currentActivityThread() { |
| return sCurrentActivityThread; |
| } |
| |
| public static boolean isSystem() { |
| return (sCurrentActivityThread != null) ? sCurrentActivityThread.mSystemThread : false; |
| } |
| |
| public static String currentOpPackageName() { |
| ActivityThread am = currentActivityThread(); |
| return (am != null && am.getApplication() != null) |
| ? am.getApplication().getOpPackageName() : null; |
| } |
| |
| public static AttributionSource currentAttributionSource() { |
| ActivityThread am = currentActivityThread(); |
| return (am != null && am.getApplication() != null) |
| ? am.getApplication().getAttributionSource() : null; |
| } |
| |
| @UnsupportedAppUsage |
| public static String currentPackageName() { |
| ActivityThread am = currentActivityThread(); |
| return (am != null && am.mBoundApplication != null) |
| ? am.mBoundApplication.appInfo.packageName : null; |
| } |
| |
| @UnsupportedAppUsage |
| public static String currentProcessName() { |
| ActivityThread am = currentActivityThread(); |
| return (am != null && am.mBoundApplication != null) |
| ? am.mBoundApplication.processName : null; |
| } |
| |
| @UnsupportedAppUsage |
| public static Application currentApplication() { |
| ActivityThread am = currentActivityThread(); |
| return am != null ? am.mInitialApplication : null; |
| } |
| |
| @UnsupportedAppUsage |
| public static IPackageManager getPackageManager() { |
| if (sPackageManager != null) { |
| return sPackageManager; |
| } |
| final IBinder b = ServiceManager.getService("package"); |
| sPackageManager = IPackageManager.Stub.asInterface(b); |
| return sPackageManager; |
| } |
| |
| /** Returns the permission manager */ |
| public static IPermissionManager getPermissionManager() { |
| if (sPermissionManager != null) { |
| return sPermissionManager; |
| } |
| final IBinder b = ServiceManager.getService("permissionmgr"); |
| sPermissionManager = IPermissionManager.Stub.asInterface(b); |
| return sPermissionManager; |
| } |
| |
| /** |
| * Creates the top level resources for the given package. Will return an existing |
| * Resources if one has already been created. |
| */ |
| Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] legacyOverlayDirs, |
| String[] overlayPaths, String[] libDirs, LoadedApk pkgInfo, |
| Configuration overrideConfig) { |
| return mResourcesManager.getResources(null, resDir, splitResDirs, legacyOverlayDirs, |
| overlayPaths, libDirs, null, overrideConfig, pkgInfo.getCompatibilityInfo(), |
| pkgInfo.getClassLoader(), null); |
| } |
| |
| @UnsupportedAppUsage |
| public Handler getHandler() { |
| return mH; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, |
| int flags) { |
| return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); |
| } |
| |
| public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, |
| int flags, int userId) { |
| final boolean differentUser = (UserHandle.myUserId() != userId); |
| ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached( |
| packageName, |
| PackageManager.GET_SHARED_LIBRARY_FILES |
| | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, |
| (userId < 0) ? UserHandle.myUserId() : userId); |
| synchronized (mResourcesManager) { |
| WeakReference<LoadedApk> ref; |
| if (differentUser) { |
| // Caching not supported across users |
| ref = null; |
| } else if ((flags & Context.CONTEXT_INCLUDE_CODE) != 0) { |
| ref = mPackages.get(packageName); |
| } else { |
| ref = mResourcePackages.get(packageName); |
| } |
| |
| LoadedApk packageInfo = ref != null ? ref.get() : null; |
| if (ai != null && packageInfo != null) { |
| if (!isLoadedApkResourceDirsUpToDate(packageInfo, ai)) { |
| List<String> oldPaths = new ArrayList<>(); |
| LoadedApk.makePaths(this, ai, oldPaths); |
| packageInfo.updateApplicationInfo(ai, oldPaths); |
| } |
| |
| if (packageInfo.isSecurityViolation() |
| && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) { |
| throw new SecurityException( |
| "Requesting code from " + packageName |
| + " to be run in process " |
| + mBoundApplication.processName |
| + "/" + mBoundApplication.appInfo.uid); |
| } |
| return packageInfo; |
| } |
| } |
| |
| if (ai != null) { |
| return getPackageInfo(ai, compatInfo, flags); |
| } |
| |
| return null; |
| } |
| |
| @UnsupportedAppUsage(trackingBug = 171933273) |
| public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, |
| int flags) { |
| boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0; |
| boolean securityViolation = includeCode && ai.uid != 0 |
| && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null |
| ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid) |
| : true); |
| boolean registerPackage = includeCode && (flags&Context.CONTEXT_REGISTER_PACKAGE) != 0; |
| if ((flags&(Context.CONTEXT_INCLUDE_CODE |
| |Context.CONTEXT_IGNORE_SECURITY)) |
| == Context.CONTEXT_INCLUDE_CODE) { |
| if (securityViolation) { |
| String msg = "Requesting code from " + ai.packageName |
| + " (with uid " + ai.uid + ")"; |
| if (mBoundApplication != null) { |
| msg = msg + " to be run in process " |
| + mBoundApplication.processName + " (with uid " |
| + mBoundApplication.appInfo.uid + ")"; |
| } |
| throw new SecurityException(msg); |
| } |
| } |
| return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode, |
| registerPackage); |
| } |
| |
| @UnsupportedAppUsage |
| public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, |
| CompatibilityInfo compatInfo) { |
| return getPackageInfo(ai, compatInfo, null, false, true, false); |
| } |
| |
| @Override |
| public LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) { |
| return getPackageInfo(ai, mCompatibilityInfo, null /* baseLoader */, |
| false /* securityViolation */, true /* includeCode */, false /* registerPackage */); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) { |
| synchronized (mResourcesManager) { |
| WeakReference<LoadedApk> ref; |
| if (includeCode) { |
| ref = mPackages.get(packageName); |
| } else { |
| ref = mResourcePackages.get(packageName); |
| } |
| return ref != null ? ref.get() : null; |
| } |
| } |
| |
| private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, |
| ClassLoader baseLoader, boolean securityViolation, boolean includeCode, |
| boolean registerPackage) { |
| return getPackageInfo(aInfo, compatInfo, baseLoader, securityViolation, includeCode, |
| registerPackage, Process.isSdkSandbox()); |
| } |
| |
| private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, |
| ClassLoader baseLoader, boolean securityViolation, boolean includeCode, |
| boolean registerPackage, boolean isSdkSandbox) { |
| final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid)); |
| synchronized (mResourcesManager) { |
| WeakReference<LoadedApk> ref; |
| if (differentUser || isSdkSandbox) { |
| // Caching not supported across users and for sdk sandboxes |
| ref = null; |
| } else if (includeCode) { |
| ref = mPackages.get(aInfo.packageName); |
| } else { |
| ref = mResourcePackages.get(aInfo.packageName); |
| } |
| |
| LoadedApk packageInfo = ref != null ? ref.get() : null; |
| |
| if (packageInfo != null) { |
| if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) { |
| if (packageInfo.getApplicationInfo().createTimestamp > aInfo.createTimestamp) { |
| // The cached loaded apk is newer than the one passed in, we should not |
| // update the cached version |
| Slog.w(TAG, "getPackageInfo() called with an older ApplicationInfo " |
| + "than the cached version for package " + aInfo.packageName); |
| } else { |
| Slog.v(TAG, "getPackageInfo() caused update to cached ApplicationInfo " |
| + "for package " + aInfo.packageName); |
| List<String> oldPaths = new ArrayList<>(); |
| LoadedApk.makePaths(this, aInfo, oldPaths); |
| packageInfo.updateApplicationInfo(aInfo, oldPaths); |
| } |
| } |
| |
| return packageInfo; |
| } |
| |
| if (localLOGV) { |
| Slog.v(TAG, (includeCode ? "Loading code package " |
| : "Loading resource-only package ") + aInfo.packageName |
| + " (in " + (mBoundApplication != null |
| ? mBoundApplication.processName : null) |
| + ")"); |
| } |
| |
| packageInfo = |
| new LoadedApk(this, aInfo, compatInfo, baseLoader, |
| securityViolation, includeCode |
| && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); |
| |
| if (mSystemThread && "android".equals(aInfo.packageName)) { |
| packageInfo.installSystemApplicationInfo(aInfo, |
| getSystemContext().mPackageInfo.getClassLoader()); |
| } |
| |
| if (differentUser || isSdkSandbox) { |
| // Caching not supported across users and for sdk sandboxes |
| } else if (includeCode) { |
| mPackages.put(aInfo.packageName, |
| new WeakReference<LoadedApk>(packageInfo)); |
| } else { |
| mResourcePackages.put(aInfo.packageName, |
| new WeakReference<LoadedApk>(packageInfo)); |
| } |
| |
| return packageInfo; |
| } |
| } |
| |
| private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk, |
| ApplicationInfo appInfo) { |
| Resources packageResources = loadedApk.mResources; |
| boolean resourceDirsUpToDate = Arrays.equals( |
| ArrayUtils.defeatNullable(appInfo.resourceDirs), |
| ArrayUtils.defeatNullable(loadedApk.getOverlayDirs())); |
| boolean overlayPathsUpToDate = Arrays.equals( |
| ArrayUtils.defeatNullable(appInfo.overlayPaths), |
| ArrayUtils.defeatNullable(loadedApk.getOverlayPaths())); |
| |
| return (packageResources == null || packageResources.getAssets().isUpToDate()) |
| && resourceDirsUpToDate && overlayPathsUpToDate; |
| } |
| |
| @UnsupportedAppUsage |
| ActivityThread() { |
| mResourcesManager = ResourcesManager.getInstance(); |
| } |
| |
| @UnsupportedAppUsage |
| public ApplicationThread getApplicationThread() |
| { |
| return mAppThread; |
| } |
| |
| @UnsupportedAppUsage |
| public Instrumentation getInstrumentation() |
| { |
| return mInstrumentation; |
| } |
| |
| public boolean isProfiling() { |
| return mProfiler != null && mProfiler.profileFile != null |
| && mProfiler.profileFd == null; |
| } |
| |
| public String getProfileFilePath() { |
| return mProfiler.profileFile; |
| } |
| |
| @UnsupportedAppUsage |
| public Looper getLooper() { |
| return mLooper; |
| } |
| |
| public Executor getExecutor() { |
| return mExecutor; |
| } |
| |
| @Override |
| @UnsupportedAppUsage |
| public Application getApplication() { |
| return mInitialApplication; |
| } |
| |
| @UnsupportedAppUsage |
| public String getProcessName() { |
| return mBoundApplication.processName; |
| } |
| |
| @Override |
| @UnsupportedAppUsage |
| public ContextImpl getSystemContext() { |
| synchronized (this) { |
| if (mSystemContext == null) { |
| mSystemContext = ContextImpl.createSystemContext(this); |
| } |
| return mSystemContext; |
| } |
| } |
| |
| @NonNull |
| public ContextImpl getSystemUiContext() { |
| return getSystemUiContext(DEFAULT_DISPLAY); |
| } |
| |
| /** |
| * Gets the context instance base on system resources & display information which used for UI. |
| * @param displayId The ID of the display where the UI is shown. |
| * @see ContextImpl#createSystemUiContext(ContextImpl, int) |
| */ |
| @NonNull |
| public ContextImpl getSystemUiContext(int displayId) { |
| synchronized (this) { |
| if (mDisplaySystemUiContexts == null) { |
| mDisplaySystemUiContexts = new ArrayList<>(); |
| } |
| |
| mDisplaySystemUiContexts.removeIf(contextRef -> contextRef.refersTo(null)); |
| |
| ContextImpl context = getSystemUiContextNoCreateLocked(displayId); |
| if (context != null) { |
| return context; |
| } |
| |
| context = ContextImpl.createSystemUiContext(getSystemContext(), displayId); |
| mDisplaySystemUiContexts.add(new WeakReference<>(context)); |
| return context; |
| } |
| } |
| |
| @Nullable |
| @Override |
| public ContextImpl getSystemUiContextNoCreate() { |
| synchronized (this) { |
| if (mDisplaySystemUiContexts == null) { |
| return null; |
| } |
| return getSystemUiContextNoCreateLocked(DEFAULT_DISPLAY); |
| } |
| } |
| |
| @GuardedBy("this") |
| @Nullable |
| private ContextImpl getSystemUiContextNoCreateLocked(int displayId) { |
| for (int i = 0; i < mDisplaySystemUiContexts.size(); i++) { |
| ContextImpl context = mDisplaySystemUiContexts.get(i).get(); |
| if (context != null && context.getDisplayId() == displayId) { |
| return context; |
| } |
| } |
| return null; |
| } |
| |
| void onSystemUiContextCleanup(ContextImpl context) { |
| synchronized (this) { |
| if (mDisplaySystemUiContexts == null) return; |
| mDisplaySystemUiContexts.removeIf( |
| contextRef -> contextRef.refersTo(null) || contextRef.refersTo(context)); |
| } |
| } |
| |
| public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { |
| synchronized (this) { |
| getSystemContext().installSystemApplicationInfo(info, classLoader); |
| getSystemUiContext().installSystemApplicationInfo(info, classLoader); |
| |
| // give ourselves a default profiler |
| mProfiler = new Profiler(); |
| } |
| } |
| |
| @UnsupportedAppUsage |
| void scheduleGcIdler() { |
| if (!mGcIdlerScheduled) { |
| mGcIdlerScheduled = true; |
| Looper.myQueue().addIdleHandler(mGcIdler); |
| } |
| mH.removeMessages(H.GC_WHEN_IDLE); |
| } |
| |
| void unscheduleGcIdler() { |
| if (mGcIdlerScheduled) { |
| mGcIdlerScheduled = false; |
| Looper.myQueue().removeIdleHandler(mGcIdler); |
| } |
| mH.removeMessages(H.GC_WHEN_IDLE); |
| } |
| |
| void schedulePurgeIdler() { |
| if (!mPurgeIdlerScheduled) { |
| mPurgeIdlerScheduled = true; |
| Looper.myQueue().addIdleHandler(mPurgeIdler); |
| } |
| mH.removeMessages(H.PURGE_RESOURCES); |
| } |
| |
| void unschedulePurgeIdler() { |
| if (mPurgeIdlerScheduled) { |
| mPurgeIdlerScheduled = false; |
| Looper.myQueue().removeIdleHandler(mPurgeIdler); |
| } |
| mH.removeMessages(H.PURGE_RESOURCES); |
| } |
| |
| void doGcIfNeeded() { |
| doGcIfNeeded("bg"); |
| } |
| |
| void doGcIfNeeded(String reason) { |
| mGcIdlerScheduled = false; |
| final long now = SystemClock.uptimeMillis(); |
| //Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime() |
| // + "m now=" + now); |
| if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) { |
| //Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!"); |
| BinderInternal.forceGc(reason); |
| } |
| } |
| |
| private static final String HEAP_FULL_COLUMN = |
| "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s"; |
| private static final String HEAP_COLUMN = |
| "%13s %8s %8s %8s %8s %8s %8s %8s %8s"; |
| private static final String ONE_COUNT_COLUMN = "%21s %8d"; |
| private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; |
| private static final String THREE_COUNT_COLUMNS = "%21s %8d %21s %8d %21s %8d"; |
| private static final String TWO_COUNT_COLUMN_HEADER = "%21s %8s %21s %8s"; |
| private static final String ONE_ALT_COUNT_COLUMN = "%21s %8s %21s %8d"; |
| |
| // Formatting for checkin service - update version if row format changes |
| private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 4; |
| |
| static void printRow(PrintWriter pw, String format, Object...objs) { |
| pw.println(String.format(Locale.US, format, objs)); |
| } |
| |
| @NeverCompile |
| public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, |
| boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, |
| int pid, String processName, |
| long nativeMax, long nativeAllocated, long nativeFree, |
| long dalvikMax, long dalvikAllocated, long dalvikFree) { |
| |
| // For checkin, we print one long comma-separated list of values |
| if (checkin) { |
| // NOTE: if you change anything significant below, also consider changing |
| // ACTIVITY_THREAD_CHECKIN_VERSION. |
| |
| // Header |
| pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(','); |
| pw.print(pid); pw.print(','); |
| pw.print(processName); pw.print(','); |
| |
| // Heap info - max |
| pw.print(nativeMax); pw.print(','); |
| pw.print(dalvikMax); pw.print(','); |
| pw.print("N/A,"); |
| pw.print(nativeMax + dalvikMax); pw.print(','); |
| |
| // Heap info - allocated |
| pw.print(nativeAllocated); pw.print(','); |
| pw.print(dalvikAllocated); pw.print(','); |
| pw.print("N/A,"); |
| pw.print(nativeAllocated + dalvikAllocated); pw.print(','); |
| |
| // Heap info - free |
| pw.print(nativeFree); pw.print(','); |
| pw.print(dalvikFree); pw.print(','); |
| pw.print("N/A,"); |
| pw.print(nativeFree + dalvikFree); pw.print(','); |
| |
| // Heap info - proportional set size |
| pw.print(memInfo.nativePss); pw.print(','); |
| pw.print(memInfo.dalvikPss); pw.print(','); |
| pw.print(memInfo.otherPss); pw.print(','); |
| pw.print(memInfo.getTotalPss()); pw.print(','); |
| |
| // Heap info - swappable set size |
| pw.print(memInfo.nativeSwappablePss); pw.print(','); |
| pw.print(memInfo.dalvikSwappablePss); pw.print(','); |
| pw.print(memInfo.otherSwappablePss); pw.print(','); |
| pw.print(memInfo.getTotalSwappablePss()); pw.print(','); |
| |
| // Heap info - shared dirty |
| pw.print(memInfo.nativeSharedDirty); pw.print(','); |
| pw.print(memInfo.dalvikSharedDirty); pw.print(','); |
| pw.print(memInfo.otherSharedDirty); pw.print(','); |
| pw.print(memInfo.getTotalSharedDirty()); pw.print(','); |
| |
| // Heap info - shared clean |
| pw.print(memInfo.nativeSharedClean); pw.print(','); |
| pw.print(memInfo.dalvikSharedClean); pw.print(','); |
| pw.print(memInfo.otherSharedClean); pw.print(','); |
| pw.print(memInfo.getTotalSharedClean()); pw.print(','); |
| |
| // Heap info - private Dirty |
| pw.print(memInfo.nativePrivateDirty); pw.print(','); |
| pw.print(memInfo.dalvikPrivateDirty); pw.print(','); |
| pw.print(memInfo.otherPrivateDirty); pw.print(','); |
| pw.print(memInfo.getTotalPrivateDirty()); pw.print(','); |
| |
| // Heap info - private Clean |
| pw.print(memInfo.nativePrivateClean); pw.print(','); |
| pw.print(memInfo.dalvikPrivateClean); pw.print(','); |
| pw.print(memInfo.otherPrivateClean); pw.print(','); |
| pw.print(memInfo.getTotalPrivateClean()); pw.print(','); |
| |
| // Heap info - swapped out |
| pw.print(memInfo.nativeSwappedOut); pw.print(','); |
| pw.print(memInfo.dalvikSwappedOut); pw.print(','); |
| pw.print(memInfo.otherSwappedOut); pw.print(','); |
| pw.print(memInfo.getTotalSwappedOut()); pw.print(','); |
| |
| // Heap info - swapped out pss |
| if (memInfo.hasSwappedOutPss) { |
| pw.print(memInfo.nativeSwappedOutPss); pw.print(','); |
| pw.print(memInfo.dalvikSwappedOutPss); pw.print(','); |
| pw.print(memInfo.otherSwappedOutPss); pw.print(','); |
| pw.print(memInfo.getTotalSwappedOutPss()); pw.print(','); |
| } else { |
| pw.print("N/A,"); |
| pw.print("N/A,"); |
| pw.print("N/A,"); |
| pw.print("N/A,"); |
| } |
| |
| // Heap info - other areas |
| for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { |
| pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(','); |
| pw.print(memInfo.getOtherPss(i)); pw.print(','); |
| pw.print(memInfo.getOtherSwappablePss(i)); pw.print(','); |
| pw.print(memInfo.getOtherSharedDirty(i)); pw.print(','); |
| pw.print(memInfo.getOtherSharedClean(i)); pw.print(','); |
| pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(','); |
| pw.print(memInfo.getOtherPrivateClean(i)); pw.print(','); |
| pw.print(memInfo.getOtherSwappedOut(i)); pw.print(','); |
| if (memInfo.hasSwappedOutPss) { |
| pw.print(memInfo.getOtherSwappedOutPss(i)); pw.print(','); |
| } else { |
| pw.print("N/A,"); |
| } |
| } |
| return; |
| } |
| |
| if (!dumpSummaryOnly) { |
| if (dumpFullInfo) { |
| printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", |
| "Shared", "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap", |
| "Rss", "Heap", "Heap", "Heap"); |
| printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", |
| "Clean", "Clean", "Dirty", "Total", |
| "Size", "Alloc", "Free"); |
| printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", |
| "------", "------", "------", "------", "------", "------", "------"); |
| printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, |
| memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, |
| memInfo.nativePrivateDirty, memInfo.nativeSharedClean, |
| memInfo.nativePrivateClean, memInfo.hasSwappedOutPss ? |
| memInfo.nativeSwappedOutPss : memInfo.nativeSwappedOut, |
| memInfo.nativeRss, nativeMax, nativeAllocated, nativeFree); |
| printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, |
| memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, |
| memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, |
| memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss ? |
| memInfo.dalvikSwappedOutPss : memInfo.dalvikSwappedOut, |
| memInfo.dalvikRss, dalvikMax, dalvikAllocated, dalvikFree); |
| } else { |
| printRow(pw, HEAP_COLUMN, "", "Pss", "Private", |
| "Private", memInfo.hasSwappedOutPss ? "SwapPss" : "Swap", |
| "Rss", "Heap", "Heap", "Heap"); |
| printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", |
| "Clean", "Dirty", "Total", "Size", "Alloc", "Free"); |
| printRow(pw, HEAP_COLUMN, "", "------", "------", "------", |
| "------", "------", "------", "------", "------", "------"); |
| printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, |
| memInfo.nativePrivateDirty, |
| memInfo.nativePrivateClean, |
| memInfo.hasSwappedOutPss ? memInfo.nativeSwappedOutPss : |
| memInfo.nativeSwappedOut, memInfo.nativeRss, |
| nativeMax, nativeAllocated, nativeFree); |
| printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, |
| memInfo.dalvikPrivateDirty, |
| memInfo.dalvikPrivateClean, |
| memInfo.hasSwappedOutPss ? memInfo.dalvikSwappedOutPss : |
| memInfo.dalvikSwappedOut, memInfo.dalvikRss, |
| dalvikMax, dalvikAllocated, dalvikFree); |
| } |
| |
| int otherPss = memInfo.otherPss; |
| int otherSwappablePss = memInfo.otherSwappablePss; |
| int otherSharedDirty = memInfo.otherSharedDirty; |
| int otherPrivateDirty = memInfo.otherPrivateDirty; |
| int otherSharedClean = memInfo.otherSharedClean; |
| int otherPrivateClean = memInfo.otherPrivateClean; |
| int otherSwappedOut = memInfo.otherSwappedOut; |
| int otherSwappedOutPss = memInfo.otherSwappedOutPss; |
| int otherRss = memInfo.otherRss; |
| |
| for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { |
| final int myPss = memInfo.getOtherPss(i); |
| final int mySwappablePss = memInfo.getOtherSwappablePss(i); |
| final int mySharedDirty = memInfo.getOtherSharedDirty(i); |
| final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); |
| final int mySharedClean = memInfo.getOtherSharedClean(i); |
| final int myPrivateClean = memInfo.getOtherPrivateClean(i); |
| final int mySwappedOut = memInfo.getOtherSwappedOut(i); |
| final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i); |
| final int myRss = memInfo.getOtherRss(i); |
| if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 |
| || mySharedClean != 0 || myPrivateClean != 0 || myRss != 0 |
| || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) { |
| if (dumpFullInfo) { |
| printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), |
| myPss, mySwappablePss, mySharedDirty, myPrivateDirty, |
| mySharedClean, myPrivateClean, |
| memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut, |
| myRss, "", "", ""); |
| } else { |
| printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), |
| myPss, myPrivateDirty, |
| myPrivateClean, |
| memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut, |
| myRss, "", "", ""); |
| } |
| otherPss -= myPss; |
| otherSwappablePss -= mySwappablePss; |
| otherSharedDirty -= mySharedDirty; |
| otherPrivateDirty -= myPrivateDirty; |
| otherSharedClean -= mySharedClean; |
| otherPrivateClean -= myPrivateClean; |
| otherSwappedOut -= mySwappedOut; |
| otherSwappedOutPss -= mySwappedOutPss; |
| otherRss -= myRss; |
| } |
| } |
| |
| if (dumpFullInfo) { |
| printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, |
| otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, |
| memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut, |
| otherRss, "", "", ""); |
| printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), |
| memInfo.getTotalSwappablePss(), |
| memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), |
| memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), |
| memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() : |
| memInfo.getTotalSwappedOut(), memInfo.getTotalRss(), |
| nativeMax+dalvikMax, nativeAllocated+dalvikAllocated, |
| nativeFree+dalvikFree); |
| } else { |
| printRow(pw, HEAP_COLUMN, "Unknown", otherPss, |
| otherPrivateDirty, otherPrivateClean, |
| memInfo.hasSwappedOutPss ? otherSwappedOutPss : otherSwappedOut, |
| otherRss, "", "", ""); |
| printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), |
| memInfo.getTotalPrivateDirty(), |
| memInfo.getTotalPrivateClean(), |
| memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() : |
| memInfo.getTotalSwappedOut(), memInfo.getTotalRss(), |
| nativeMax+dalvikMax, |
| nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); |
| } |
| |
| if (dumpDalvik) { |
| pw.println(" "); |
| pw.println(" Dalvik Details"); |
| |
| for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; |
| i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { |
| final int myPss = memInfo.getOtherPss(i); |
| final int mySwappablePss = memInfo.getOtherSwappablePss(i); |
| final int mySharedDirty = memInfo.getOtherSharedDirty(i); |
| final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); |
| final int mySharedClean = memInfo.getOtherSharedClean(i); |
| final int myPrivateClean = memInfo.getOtherPrivateClean(i); |
| final int mySwappedOut = memInfo.getOtherSwappedOut(i); |
| final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i); |
| final int myRss = memInfo.getOtherRss(i); |
| if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 |
| || mySharedClean != 0 || myPrivateClean != 0 |
| || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) { |
| if (dumpFullInfo) { |
| printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), |
| myPss, mySwappablePss, mySharedDirty, myPrivateDirty, |
| mySharedClean, myPrivateClean, |
| memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut, |
| myRss, "", "", ""); |
| } else { |
| printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), |
| myPss, myPrivateDirty, |
| myPrivateClean, |
| memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut, |
| myRss, "", "", ""); |
| } |
| } |
| } |
| } |
| } |
| |
| pw.println(" "); |
| pw.println(" App Summary"); |
| printRow(pw, TWO_COUNT_COLUMN_HEADER, "", "Pss(KB)", "", "Rss(KB)"); |
| printRow(pw, TWO_COUNT_COLUMN_HEADER, "", "------", "", "------"); |
| printRow(pw, TWO_COUNT_COLUMNS, |
| "Java Heap:", memInfo.getSummaryJavaHeap(), "", memInfo.getSummaryJavaHeapRss()); |
| printRow(pw, TWO_COUNT_COLUMNS, |
| "Native Heap:", memInfo.getSummaryNativeHeap(), "", |
| memInfo.getSummaryNativeHeapRss()); |
| printRow(pw, TWO_COUNT_COLUMNS, |
| "Code:", memInfo.getSummaryCode(), "", memInfo.getSummaryCodeRss()); |
| printRow(pw, TWO_COUNT_COLUMNS, |
| "Stack:", memInfo.getSummaryStack(), "", memInfo.getSummaryStackRss()); |
| printRow(pw, TWO_COUNT_COLUMNS, |
| "Graphics:", memInfo.getSummaryGraphics(), "", memInfo.getSummaryGraphicsRss()); |
| printRow(pw, ONE_COUNT_COLUMN, |
| "Private Other:", memInfo.getSummaryPrivateOther()); |
| printRow(pw, ONE_COUNT_COLUMN, |
| "System:", memInfo.getSummarySystem()); |
| printRow(pw, ONE_ALT_COUNT_COLUMN, |
| "Unknown:", "", "", memInfo.getSummaryUnknownRss()); |
| pw.println(" "); |
| if (memInfo.hasSwappedOutPss) { |
| printRow(pw, THREE_COUNT_COLUMNS, |
| "TOTAL PSS:", memInfo.getSummaryTotalPss(), |
| "TOTAL RSS:", memInfo.getTotalRss(), |
| "TOTAL SWAP PSS:", memInfo.getSummaryTotalSwapPss()); |
| } else { |
| printRow(pw, THREE_COUNT_COLUMNS, |
| "TOTAL PSS:", memInfo.getSummaryTotalPss(), |
| "TOTAL RSS:", memInfo.getTotalRss(), |
| "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap()); |
| } |
| } |
| |
| /** |
| * Dump heap info to proto. |
| * |
| * @param hasSwappedOutPss determines whether to use dirtySwap or dirtySwapPss |
| */ |
| private static void dumpMemoryInfo(ProtoOutputStream proto, long fieldId, String name, |
| int pss, int cleanPss, int sharedDirty, int privateDirty, |
| int sharedClean, int privateClean, |
| boolean hasSwappedOutPss, int dirtySwap, int dirtySwapPss, int rss) { |
| final long token = proto.start(fieldId); |
| |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.NAME, name); |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.TOTAL_PSS_KB, pss); |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.CLEAN_PSS_KB, cleanPss); |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_DIRTY_KB, sharedDirty); |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_DIRTY_KB, privateDirty); |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.SHARED_CLEAN_KB, sharedClean); |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.PRIVATE_CLEAN_KB, privateClean); |
| if (hasSwappedOutPss) { |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_PSS_KB, dirtySwapPss); |
| } else { |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.DIRTY_SWAP_KB, dirtySwap); |
| } |
| proto.write(MemInfoDumpProto.ProcessMemory.MemoryInfo.TOTAL_RSS_KB, rss); |
| |
| proto.end(token); |
| } |
| |
| /** |
| * Dump mem info data to proto. |
| */ |
| @NeverCompile |
| public static void dumpMemInfoTable(ProtoOutputStream proto, Debug.MemoryInfo memInfo, |
| boolean dumpDalvik, boolean dumpSummaryOnly, |
| long nativeMax, long nativeAllocated, long nativeFree, |
| long dalvikMax, long dalvikAllocated, long dalvikFree) { |
| |
| if (!dumpSummaryOnly) { |
| final long nhToken = proto.start(MemInfoDumpProto.ProcessMemory.NATIVE_HEAP); |
| dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Native Heap", |
| memInfo.nativePss, memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, |
| memInfo.nativePrivateDirty, memInfo.nativeSharedClean, |
| memInfo.nativePrivateClean, memInfo.hasSwappedOutPss, |
| memInfo.nativeSwappedOut, memInfo.nativeSwappedOutPss, |
| memInfo.nativeRss); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, nativeMax); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, nativeAllocated); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, nativeFree); |
| proto.end(nhToken); |
| |
| final long dvToken = proto.start(MemInfoDumpProto.ProcessMemory.DALVIK_HEAP); |
| dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "Dalvik Heap", |
| memInfo.dalvikPss, memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, |
| memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, |
| memInfo.dalvikPrivateClean, memInfo.hasSwappedOutPss, |
| memInfo.dalvikSwappedOut, memInfo.dalvikSwappedOutPss, |
| memInfo.dalvikRss); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, dalvikMax); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, dalvikAllocated); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, dalvikFree); |
| proto.end(dvToken); |
| |
| int otherPss = memInfo.otherPss; |
| int otherSwappablePss = memInfo.otherSwappablePss; |
| int otherSharedDirty = memInfo.otherSharedDirty; |
| int otherPrivateDirty = memInfo.otherPrivateDirty; |
| int otherSharedClean = memInfo.otherSharedClean; |
| int otherPrivateClean = memInfo.otherPrivateClean; |
| int otherSwappedOut = memInfo.otherSwappedOut; |
| int otherSwappedOutPss = memInfo.otherSwappedOutPss; |
| int otherRss = memInfo.otherRss; |
| |
| for (int i = 0; i < Debug.MemoryInfo.NUM_OTHER_STATS; i++) { |
| final int myPss = memInfo.getOtherPss(i); |
| final int mySwappablePss = memInfo.getOtherSwappablePss(i); |
| final int mySharedDirty = memInfo.getOtherSharedDirty(i); |
| final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); |
| final int mySharedClean = memInfo.getOtherSharedClean(i); |
| final int myPrivateClean = memInfo.getOtherPrivateClean(i); |
| final int mySwappedOut = memInfo.getOtherSwappedOut(i); |
| final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i); |
| final int myRss = memInfo.getOtherRss(i); |
| if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 |
| || mySharedClean != 0 || myPrivateClean != 0 || myRss != 0 |
| || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) { |
| dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.OTHER_HEAPS, |
| Debug.MemoryInfo.getOtherLabel(i), |
| myPss, mySwappablePss, mySharedDirty, myPrivateDirty, |
| mySharedClean, myPrivateClean, |
| memInfo.hasSwappedOutPss, mySwappedOut, mySwappedOutPss, myRss); |
| |
| otherPss -= myPss; |
| otherSwappablePss -= mySwappablePss; |
| otherSharedDirty -= mySharedDirty; |
| otherPrivateDirty -= myPrivateDirty; |
| otherSharedClean -= mySharedClean; |
| otherPrivateClean -= myPrivateClean; |
| otherSwappedOut -= mySwappedOut; |
| otherSwappedOutPss -= mySwappedOutPss; |
| otherRss -= myRss; |
| } |
| } |
| |
| dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.UNKNOWN_HEAP, "Unknown", |
| otherPss, otherSwappablePss, |
| otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, |
| memInfo.hasSwappedOutPss, otherSwappedOut, otherSwappedOutPss, otherRss); |
| final long tToken = proto.start(MemInfoDumpProto.ProcessMemory.TOTAL_HEAP); |
| dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.HeapInfo.MEM_INFO, "TOTAL", |
| memInfo.getTotalPss(), memInfo.getTotalSwappablePss(), |
| memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), |
| memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), |
| memInfo.hasSwappedOutPss, memInfo.getTotalSwappedOut(), |
| memInfo.getTotalSwappedOutPss(), memInfo.getTotalRss()); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_SIZE_KB, |
| nativeMax + dalvikMax); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_ALLOC_KB, |
| nativeAllocated + dalvikAllocated); |
| proto.write(MemInfoDumpProto.ProcessMemory.HeapInfo.HEAP_FREE_KB, |
| nativeFree + dalvikFree); |
| proto.end(tToken); |
| |
| if (dumpDalvik) { |
| for (int i = Debug.MemoryInfo.NUM_OTHER_STATS; |
| i < Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; |
| i++) { |
| final int myPss = memInfo.getOtherPss(i); |
| final int mySwappablePss = memInfo.getOtherSwappablePss(i); |
| final int mySharedDirty = memInfo.getOtherSharedDirty(i); |
| final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); |
| final int mySharedClean = memInfo.getOtherSharedClean(i); |
| final int myPrivateClean = memInfo.getOtherPrivateClean(i); |
| final int mySwappedOut = memInfo.getOtherSwappedOut(i); |
| final int mySwappedOutPss = memInfo.getOtherSwappedOutPss(i); |
| final int myRss = memInfo.getOtherRss(i); |
| if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 |
| || mySharedClean != 0 || myPrivateClean != 0 |
| || (memInfo.hasSwappedOutPss ? mySwappedOutPss : mySwappedOut) != 0) { |
| dumpMemoryInfo(proto, MemInfoDumpProto.ProcessMemory.DALVIK_DETAILS, |
| Debug.MemoryInfo.getOtherLabel(i), |
| myPss, mySwappablePss, mySharedDirty, myPrivateDirty, |
| mySharedClean, myPrivateClean, |
| memInfo.hasSwappedOutPss, mySwappedOut, mySwappedOutPss, myRss); |
| } |
| } |
| } |
| } |
| |
| final long asToken = proto.start(MemInfoDumpProto.ProcessMemory.APP_SUMMARY); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.JAVA_HEAP_PSS_KB, |
| memInfo.getSummaryJavaHeap()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.NATIVE_HEAP_PSS_KB, |
| memInfo.getSummaryNativeHeap()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.CODE_PSS_KB, |
| memInfo.getSummaryCode()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.STACK_PSS_KB, |
| memInfo.getSummaryStack()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.GRAPHICS_PSS_KB, |
| memInfo.getSummaryGraphics()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.PRIVATE_OTHER_PSS_KB, |
| memInfo.getSummaryPrivateOther()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.SYSTEM_PSS_KB, |
| memInfo.getSummarySystem()); |
| if (memInfo.hasSwappedOutPss) { |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS, |
| memInfo.getSummaryTotalSwapPss()); |
| } else { |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.TOTAL_SWAP_PSS, |
| memInfo.getSummaryTotalSwap()); |
| } |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.JAVA_HEAP_RSS_KB, |
| memInfo.getSummaryJavaHeapRss()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.NATIVE_HEAP_RSS_KB, |
| memInfo.getSummaryNativeHeapRss()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.CODE_RSS_KB, |
| memInfo.getSummaryCodeRss()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.STACK_RSS_KB, |
| memInfo.getSummaryStackRss()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.GRAPHICS_RSS_KB, |
| memInfo.getSummaryGraphicsRss()); |
| proto.write(MemInfoDumpProto.ProcessMemory.AppSummary.UNKNOWN_RSS_KB, |
| memInfo.getSummaryUnknownRss()); |
| |
| proto.end(asToken); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void registerOnActivityPausedListener(Activity activity, |
| OnActivityPausedListener listener) { |
| synchronized (mOnPauseListeners) { |
| ArrayList<OnActivityPausedListener> list = |
| mOnPauseListeners.computeIfAbsent(activity, k -> new ArrayList<>()); |
| list.add(listener); |
| } |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| public void unregisterOnActivityPausedListener(Activity activity, |
| OnActivityPausedListener listener) { |
| synchronized (mOnPauseListeners) { |
| ArrayList<OnActivityPausedListener> list = mOnPauseListeners.get(activity); |
| if (list != null) { |
| list.remove(listener); |
| } |
| } |
| } |
| |
| public final ActivityInfo resolveActivityInfo(Intent intent) { |
| ActivityInfo aInfo = intent.resolveActivityInfo( |
| mInitialApplication.getPackageManager(), PackageManager.GET_SHARED_LIBRARY_FILES); |
| if (aInfo == null) { |
| // Throw an exception. |
| Instrumentation.checkStartActivityResult( |
| ActivityManager.START_CLASS_NOT_FOUND, intent); |
| } |
| return aInfo; |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| public final Activity startActivityNow(Activity parent, String id, |
| Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state, |
| Activity.NonConfigurationInstances lastNonConfigurationInstances, IBinder assistToken, |
| IBinder shareableActivityToken) { |
| ActivityClientRecord r = new ActivityClientRecord(); |
| r.token = token; |
| r.assistToken = assistToken; |
| r.shareableActivityToken = shareableActivityToken; |
| r.ident = 0; |
| r.intent = intent; |
| r.state = state; |
| r.parent = parent; |
| r.embeddedID = id; |
| r.activityInfo = activityInfo; |
| r.lastNonConfigurationInstances = lastNonConfigurationInstances; |
| if (localLOGV) { |
| ComponentName compname = intent.getComponent(); |
| String name; |
| if (compname != null) { |
| name = compname.toShortString(); |
| } else { |
| name = "(Intent " + intent + ").getComponent() returned null"; |
| } |
| Slog.v(TAG, "Performing launch: action=" + intent.getAction() |
| + ", comp=" + name |
| + ", token=" + token); |
| } |
| // TODO(lifecycler): Can't switch to use #handleLaunchActivity() because it will try to |
| // call #reportSizeConfigurations(), but the server might not know anything about the |
| // activity if it was launched from LocalAcvitivyManager. |
| return performLaunchActivity(r, null /* customIntent */); |
| } |
| |
| @UnsupportedAppUsage |
| public final Activity getActivity(IBinder token) { |
| final ActivityClientRecord activityRecord = mActivities.get(token); |
| return activityRecord != null ? activityRecord.activity : null; |
| } |
| |
| @Override |
| public ActivityClientRecord getActivityClient(IBinder token) { |
| return mActivities.get(token); |
| } |
| |
| @VisibleForTesting(visibility = PACKAGE) |
| public Configuration getConfiguration() { |
| return mConfigurationController.getConfiguration(); |
| } |
| |
| /** |
| * @hide |
| */ |
| public void addConfigurationChangedListener(Executor executor, |
| Consumer<IBinder> consumer) { |
| mConfigurationChangedListenerController.addListener(executor, consumer); |
| } |
| |
| /** |
| * @hide |
| */ |
| public void removeConfigurationChangedListener(Consumer<IBinder> consumer) { |
| mConfigurationChangedListenerController.removeListener(consumer); |
| } |
| |
| @Override |
| public void updatePendingConfiguration(Configuration config) { |
| final Configuration updatedConfig = |
| mConfigurationController.updatePendingConfiguration(config); |
| // This is only done to maintain @UnsupportedAppUsage and should be removed someday. |
| if (updatedConfig != null) { |
| mPendingConfiguration = updatedConfig; |
| } |
| } |
| |
| @Override |
| public void updateProcessState(int processState, boolean fromIpc) { |
| synchronized (mAppThread) { |
| if (mLastProcessState == processState) { |
| return; |
| } |
| // Do not issue a transitional GC if we are transitioning between 2 cached states. |
| // Only update if the state flips between cached and uncached or vice versa |
| if (ActivityManager.isProcStateCached(mLastProcessState) |
| != ActivityManager.isProcStateCached(processState)) { |
| updateVmProcessState(processState); |
| } |
| mLastProcessState = processState; |
| if (localLOGV) { |
| Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState |
| + (fromIpc ? " (from ipc" : "")); |
| } |
| } |
| } |
| |
| /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */ |
| // Currently ART VM only uses state updates for Transitional GC, and thus |
| // this function initiates a Transitional GC for transitions into Cached apps states. |
| private void updateVmProcessState(int processState) { |
| // Only a transition into Cached state should result in a Transitional GC request |
| // to the ART runtime. Update VM state to JANK_IMPERCEPTIBLE in that case. |
| // Note that there are 4 possible cached states currently, all of which are |
| // JANK_IMPERCEPTIBLE from GC point of view. |
| final int state = ActivityManager.isProcStateCached(processState) |
| ? VM_PROCESS_STATE_JANK_IMPERCEPTIBLE |
| : VM_PROCESS_STATE_JANK_PERCEPTIBLE; |
| VMRuntime.getRuntime().updateProcessState(state); |
| } |
| |
| @Override |
| public void countLaunchingActivities(int num) { |
| mNumLaunchingActivities.getAndAdd(num); |
| } |
| |
| @UnsupportedAppUsage |
| public void sendActivityResult( |
| IBinder activityToken, String id, int requestCode, |
| int resultCode, Intent data) { |
| if (DEBUG_RESULTS) Slog.v(TAG, "sendActivityResult: id=" + id |
| + " req=" + requestCode + " res=" + resultCode + " data=" + data); |
| final ArrayList<ResultInfo> list = new ArrayList<>(); |
| list.add(new ResultInfo(id, requestCode, resultCode, data, activityToken)); |
| final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread); |
| final ActivityResultItem activityResultItem = ActivityResultItem.obtain( |
| activityToken, list); |
| clientTransaction.addTransactionItem(activityResultItem); |
| try { |
| mAppThread.scheduleTransaction(clientTransaction); |
| } catch (RemoteException e) { |
| // Local scheduling |
| } |
| } |
| |
| @Override |
| TransactionExecutor getTransactionExecutor() { |
| return mTransactionExecutor; |
| } |
| |
| void sendMessage(int what, Object obj) { |
| sendMessage(what, obj, 0, 0, false); |
| } |
| |
| private void sendMessage(int what, Object obj, int arg1) { |
| sendMessage(what, obj, arg1, 0, false); |
| } |
| |
| private void sendMessage(int what, Object obj, int arg1, int arg2) { |
| sendMessage(what, obj, arg1, arg2, false); |
| } |
| |
| private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { |
| if (DEBUG_MESSAGES) { |
| Slog.v(TAG, |
| "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj); |
| } |
| Message msg = Message.obtain(); |
| msg.what = what; |
| msg.obj = obj; |
| msg.arg1 = arg1; |
| msg.arg2 = arg2; |
| if (async) { |
| msg.setAsynchronous(true); |
| } |
| mH.sendMessage(msg); |
| } |
| |
| final void scheduleContextCleanup(ContextImpl context, String who, |
| String what) { |
| ContextCleanupInfo cci = new ContextCleanupInfo(); |
| cci.context = context; |
| cci.who = who; |
| cci.what = what; |
| sendMessage(H.CLEAN_UP_CONTEXT, cci); |
| } |
| |
| /** Core implementation of activity launch. */ |
| private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { |
| ActivityInfo aInfo = r.activityInfo; |
| |
| if (getInstrumentation() != null |
| && getInstrumentation().getContext() != null |
| && getInstrumentation().getContext().getApplicationInfo() != null |
| && getInstrumentation().isSdkSandboxAllowedToStartActivities()) { |
| // Activities launched from CTS-in-sandbox tests use a customized ApplicationInfo. See |
| // also {@link SdkSandboxManagerLocal#getSdkSandboxApplicationInfoForInstrumentation}. |
| r.packageInfo = |
| getPackageInfo( |
| getInstrumentation().getContext().getApplicationInfo(), |
| mCompatibilityInfo, |
| Context.CONTEXT_INCLUDE_CODE); |
| } else if (r.packageInfo == null) { |
| r.packageInfo = getPackageInfo(aInfo.applicationInfo, mCompatibilityInfo, |
| Context.CONTEXT_INCLUDE_CODE); |
| } |
| |
| ComponentName component = r.intent.getComponent(); |
| if (component == null) { |
| component = r.intent.resolveActivity( |
| mInitialApplication.getPackageManager()); |
| r.intent.setComponent(component); |
| } |
| |
| if (r.activityInfo.targetActivity != null) { |
| component = new ComponentName(r.activityInfo.packageName, |
| r.activityInfo.targetActivity); |
| } |
| |
| boolean isSandboxActivityContext = |
| sandboxActivitySdkBasedContext() |
| && SdkSandboxActivityAuthority.isSdkSandboxActivityIntent( |
| mSystemContext, r.intent); |
| boolean isSandboxedSdkContextUsed = false; |
| ContextImpl activityBaseContext; |
| if (isSandboxActivityContext) { |
| activityBaseContext = createBaseContextForSandboxActivity(r); |
| if (activityBaseContext == null) { |
| // Failed to retrieve the SDK based sandbox activity context, falling back to the |
| // app based context. |
| activityBaseContext = createBaseContextForActivity(r); |
| } else { |
| isSandboxedSdkContextUsed = true; |
| } |
| } else { |
| activityBaseContext = createBaseContextForActivity(r); |
| } |
| Activity activity = null; |
| try { |
| java.lang.ClassLoader cl; |
| if (isSandboxedSdkContextUsed) { |
| // In case of sandbox activity, the context refers to the an SDK with no visibility |
| // on the SandboxedActivity java class, the App context should be used instead. |
| cl = activityBaseContext.getApplicationContext().getClassLoader(); |
| } else { |
| cl = activityBaseContext.getClassLoader(); |
| } |
| activity = mInstrumentation.newActivity( |
| cl, component.getClassName(), r.intent); |
| StrictMode.incrementExpectedActivityCount(activity.getClass()); |
| r.intent.setExtrasClassLoader(cl); |
| r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo), |
| activityBaseContext.getAttributionSource()); |
| if (r.state != null) { |
| r.state.setClassLoader(cl); |
| } |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(activity, e)) { |
| throw new RuntimeException( |
| "Unable to instantiate activity " + component |
| + ": " + e.toString(), e); |
| } |
| } |
| |
| try { |
| Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation); |
| |
| if (localLOGV) Slog.v(TAG, "Performing launch of " + r); |
| if (localLOGV) Slog.v( |
| TAG, r + ": app=" + app |
| + ", appName=" + app.getPackageName() |
| + ", pkg=" + r.packageInfo.getPackageName() |
| + ", comp=" + r.intent.getComponent().toShortString() |
| + ", dir=" + r.packageInfo.getAppDir()); |
| |
| // updatePendingActivityConfiguration() reads from mActivities to update |
| // ActivityClientRecord which runs in a different thread. Protect modifications to |
| // mActivities to avoid race. |
| synchronized (mResourcesManager) { |
| mActivities.put(r.token, r); |
| } |
| |
| if (activity != null) { |
| CharSequence title = |
| r.activityInfo.loadLabel(activityBaseContext.getPackageManager()); |
| Configuration config = |
| new Configuration(mConfigurationController.getCompatConfiguration()); |
| if (r.overrideConfig != null) { |
| config.updateFrom(r.overrideConfig); |
| } |
| if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " |
| + r.activityInfo.name + " with config " + config); |
| Window window = null; |
| if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { |
| window = r.mPendingRemoveWindow; |
| r.mPendingRemoveWindow = null; |
| r.mPendingRemoveWindowManager = null; |
| } |
| |
| // Activity resources must be initialized with the same loaders as the |
| // application context. |
| activityBaseContext.getResources().addLoaders( |
| app.getResources().getLoaders().toArray(new ResourcesLoader[0])); |
| |
| activityBaseContext.setOuterContext(activity); |
| activity.attach(activityBaseContext, this, getInstrumentation(), r.token, |
| r.ident, app, r.intent, r.activityInfo, title, r.parent, |
| r.embeddedID, r.lastNonConfigurationInstances, config, |
| r.referrer, r.voiceInteractor, window, r.activityConfigCallback, |
| r.assistToken, r.shareableActivityToken, r.initialCallerInfoAccessToken); |
| |
| if (customIntent != null) { |
| activity.mIntent = customIntent; |
| } |
| r.lastNonConfigurationInstances = null; |
| checkAndBlockForNetworkAccess(); |
| activity.mStartedActivity = false; |
| int theme = r.activityInfo.getThemeResource(); |
| if (theme != 0) { |
| activity.setTheme(theme); |
| } |
| |
| if (r.mSceneTransitionInfo != null) { |
| activity.mSceneTransitionInfo = r.mSceneTransitionInfo; |
| r.mSceneTransitionInfo = null; |
| } |
| activity.mLaunchedFromBubble = r.mLaunchedFromBubble; |
| activity.mCalled = false; |
| // Assigning the activity to the record before calling onCreate() allows |
| // ActivityThread#getActivity() lookup for the callbacks triggered from |
| // ActivityLifecycleCallbacks#onActivityCreated() or |
| // ActivityLifecycleCallback#onActivityPostCreated(). |
| r.activity = activity; |
| if (r.isPersistable()) { |
| mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); |
| } else { |
| mInstrumentation.callActivityOnCreate(activity, r.state); |
| } |
| if (!activity.mCalled) { |
| throw new SuperNotCalledException( |
| "Activity " + r.intent.getComponent().toShortString() + |
| " did not call through to super.onCreate()"); |
| } |
| r.mLastReportedWindowingMode = config.windowConfiguration.getWindowingMode(); |
| } |
| r.setState(ON_CREATE); |
| |
| } catch (SuperNotCalledException e) { |
| throw e; |
| |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(activity, e)) { |
| throw new RuntimeException( |
| "Unable to start activity " + component |
| + ": " + e.toString(), e); |
| } |
| } |
| |
| return activity; |
| } |
| |
| @Override |
| public void handleStartActivity(ActivityClientRecord r, |
| PendingTransactionActions pendingActions, SceneTransitionInfo sceneTransitionInfo) { |
| final Activity activity = r.activity; |
| if (!r.stopped) { |
| throw new IllegalStateException("Can't start activity that is not stopped."); |
| } |
| if (r.activity.mFinished) { |
| // TODO(lifecycler): How can this happen? |
| return; |
| } |
| |
| unscheduleGcIdler(); |
| if (sceneTransitionInfo != null) { |
| activity.mSceneTransitionInfo = sceneTransitionInfo; |
| } |
| |
| // Start |
| activity.performStart("handleStartActivity"); |
| r.setState(ON_START); |
| |
| if (pendingActions == null) { |
| // No more work to do. |
| return; |
| } |
| |
| // Restore instance state |
| if (pendingActions.shouldRestoreInstanceState()) { |
| if (r.isPersistable()) { |
| if (r.state != null || r.persistentState != null) { |
| mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, |
| r.persistentState); |
| } |
| } else if (r.state != null) { |
| mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); |
| } |
| } |
| |
| // Call postOnCreate() |
| if (pendingActions.shouldCallOnPostCreate()) { |
| activity.mCalled = false; |
| Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "onPostCreate"); |
| if (r.isPersistable()) { |
| mInstrumentation.callActivityOnPostCreate(activity, r.state, |
| r.persistentState); |
| } else { |
| mInstrumentation.callActivityOnPostCreate(activity, r.state); |
| } |
| Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); |
| if (!activity.mCalled) { |
| throw new SuperNotCalledException( |
| "Activity " + r.intent.getComponent().toShortString() |
| + " did not call through to super.onPostCreate()"); |
| } |
| } |
| |
| updateVisibility(r, true /* show */); |
| mSomeActivitiesChanged = true; |
| } |
| |
| /** |
| * Checks if {@link #mNetworkBlockSeq} is {@link #INVALID_PROC_STATE_SEQ} and if so, returns |
| * immediately. Otherwise, makes a blocking call to ActivityManagerService to wait for the |
| * network rules to get updated. |
| */ |
| private void checkAndBlockForNetworkAccess() { |
| synchronized (mNetworkPolicyLock) { |
| if (mNetworkBlockSeq != INVALID_PROC_STATE_SEQ) { |
| try { |
| ActivityManager.getService().waitForNetworkStateUpdate(mNetworkBlockSeq); |
| mNetworkBlockSeq = INVALID_PROC_STATE_SEQ; |
| } catch (RemoteException ignored) {} |
| if (Flags.clearDnsCacheOnNetworkRulesUpdate()) { |
| // InetAddress will cache UnknownHostException failures. If the rules got |
| // updated and the app has network access now, we need to clear the negative |
| // cache to ensure valid dns queries can work immediately. |
| // TODO: b/329133769 - Clear only the negative cache once it is available. |
| InetAddress.clearDnsCache(); |
| } |
| } |
| } |
| } |
| |
| private ContextImpl createBaseContextForActivity(ActivityClientRecord r) { |
| final int displayId = ActivityClient.getInstance().getDisplayId(r.token); |
| ContextImpl appContext = ContextImpl.createActivityContext( |
| this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig); |
| |
| final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance(); |
| // For debugging purposes, if the activity's package name contains the value of |
| // the "debug.use-second-display" system property as a substring, then show |
| // its content on a secondary display if there is one. |
| String pkgName = SystemProperties.get("debug.second-display.pkg"); |
| if (pkgName != null && !pkgName.isEmpty() |
| && r.packageInfo.mPackageName.contains(pkgName)) { |
| for (int id : dm.getDisplayIds()) { |
| if (id != DEFAULT_DISPLAY) { |
| Display display = |
| dm.getCompatibleDisplay(id, appContext.getResources()); |
| appContext = (ContextImpl) appContext.createDisplayContext(display); |
| break; |
| } |
| } |
| } |
| return appContext; |
| } |
| |
| /** |
| * Creates the base context for the sandbox activity based on its corresponding SDK {@link |
| * ApplicationInfo} and flags. |
| */ |
| @Nullable |
| private ContextImpl createBaseContextForSandboxActivity(@NonNull ActivityClientRecord r) { |
| SdkSandboxActivityAuthority sdkSandboxActivityAuthority = |
| SdkSandboxActivityAuthority.getInstance(); |
| |
| ActivityContextInfo contextInfo; |
| try { |
| contextInfo = sdkSandboxActivityAuthority.getActivityContextInfo(r.intent); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Passed intent does not match an expected sandbox activity", e); |
| return null; |
| } catch (IllegalStateException e) { |
| Log.e(TAG, "SDK customized context flag is disabled", e); |
| return null; |
| } catch (Exception e) { // generic catch to unexpected exceptions |
| Log.e(TAG, "Failed to create context for sandbox activity", e); |
| return null; |
| } |
| |
| final int displayId = ActivityClient.getInstance().getDisplayId(r.token); |
| final LoadedApk sdkApk = getPackageInfo( |
| contextInfo.getSdkApplicationInfo(), |
| r.packageInfo.getCompatibilityInfo(), |
| contextInfo.getContextFlags()); |
| |
| final ContextImpl activityContext = ContextImpl.createActivityContext( |
| this, sdkApk, r.activityInfo, r.token, displayId, r.overrideConfig); |
| |
| // Set sandbox app's context as the application context for sdk context |
| activityContext.mPackageInfo.makeApplicationInner( |
| /*forceDefaultAppClass=*/false, mInstrumentation); |
| |
| return activityContext; |
| } |
| |
| /** |
| * Extended implementation of activity launch. Used when server requests a launch or relaunch. |
| */ |
| @Override |
| public Activity handleLaunchActivity(ActivityClientRecord r, |
| PendingTransactionActions pendingActions, int deviceId, Intent customIntent) { |
| // If we are getting ready to gc after going to the background, well |
| // we are back active so skip it. |
| unscheduleGcIdler(); |
| mSomeActivitiesChanged = true; |
| |
| if (r.profilerInfo != null) { |
| mProfiler.setProfiler(r.profilerInfo); |
| mProfiler.startProfiling(); |
| } |
| |
| // Make sure we are running with the most recent config and resource paths. |
| applyPendingApplicationInfoChanges(r.activityInfo.packageName); |
| mConfigurationController.handleConfigurationChanged(null, null); |
| updateDeviceIdForNonUIContexts(deviceId); |
| |
| if (localLOGV) Slog.v( |
| TAG, "Handling launch of " + r); |
| |
| // Initialize before creating the activity |
| if (ThreadedRenderer.sRendererEnabled |
| && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { |
| HardwareRenderer.preload(); |
| } |
| WindowManagerGlobal.initialize(); |
| |
| // Hint the GraphicsEnvironment that an activity is launching on the process. |
| GraphicsEnvironment.hintActivityLaunch(); |
| |
| final Activity a = performLaunchActivity(r, customIntent); |
| |
| if (a != null) { |
| r.createdConfig = new Configuration(mConfigurationController.getConfiguration()); |
| reportSizeConfigurations(r); |
| if (!r.activity.mFinished && pendingActions != null) { |
| pendingActions.setOldState(r.state); |
| pendingActions.setRestoreInstanceState(true); |
| pendingActions.setCallOnPostCreate(true); |
| } |
| |
| // Trigger ActivityWindowInfo callback if first launch or change from relaunch. |
| handleActivityWindowInfoChanged(r); |
| } else { |
| // If there was an error, for any reason, tell the activity manager to stop us. |
| ActivityClient.getInstance().finishActivity(r.token, Activity.RESULT_CANCELED, |
| null /* resultData */, Activity.DONT_FINISH_TASK_WITH_ACTIVITY); |
| } |
| |
| return a; |
| } |
| |
| private void reportSizeConfigurations(ActivityClientRecord r) { |
| if (mActivitiesToBeDestroyed.containsKey(r.token)) { |
| // Size configurations of a destroyed activity is meaningless. |
| return; |
| } |
| Configuration[] configurations = r.activity.getResources().getSizeConfigurations(); |
| if (configurations == null) { |
| return; |
| } |
| r.mSizeConfigurations = new SizeConfigurationBuckets(configurations); |
| ActivityClient.getInstance().reportSizeConfigurations(r.token, r.mSizeConfigurations); |
| } |
| |
| private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) { |
| final int N = intents.size(); |
| for (int i=0; i<N; i++) { |
| ReferrerIntent intent = intents.get(i); |
| intent.setExtrasClassLoader(r.activity.getClassLoader()); |
| intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo), |
| r.activity.getAttributionSource()); |
| r.activity.mFragments.noteStateNotSaved(); |
| if (android.security.Flags.contentUriPermissionApis()) { |
| ComponentCaller caller = new ComponentCaller(r.token, intent.mCallerToken); |
| mInstrumentation.callActivityOnNewIntent(r.activity, intent, caller); |
| } else { |
| mInstrumentation.callActivityOnNewIntent(r.activity, intent); |
| } |
| } |
| } |
| |
| @Override |
| public void handleNewIntent(ActivityClientRecord r, List<ReferrerIntent> intents) { |
| checkAndBlockForNetworkAccess(); |
| deliverNewIntents(r, intents); |
| } |
| |
| public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) { |
| // Filling for autofill has a few differences: |
| // - it does not need an AssistContent |
| // - it does not call onProvideAssistData() |
| // - it needs an IAutoFillCallback |
| boolean forAutofill = cmd.requestType == ActivityManager.ASSIST_CONTEXT_AUTOFILL; |
| // When only the AssistContent is requested, omit the AsssistStructure |
| boolean requestedOnlyContent = cmd.requestType == ActivityManager.ASSIST_CONTEXT_CONTENT; |
| |
| // TODO: decide if lastSessionId logic applies to autofill sessions |
| if (mLastSessionId != cmd.sessionId) { |
| // Clear the existing structures |
| mLastSessionId = cmd.sessionId; |
| for (int i = mLastAssistStructures.size() - 1; i >= 0; i--) { |
| AssistStructure structure = mLastAssistStructures.get(i).get(); |
| if (structure != null) { |
| structure.clearSendChannel(); |
| } |
| mLastAssistStructures.remove(i); |
| } |
| } |
| |
| Bundle data = new Bundle(); |
| AssistStructure structure = null; |
| AssistContent content = forAutofill ? null : new AssistContent(); |
| final long startTime = SystemClock.uptimeMillis(); |
| ActivityClientRecord r = mActivities.get(cmd.activityToken); |
| Uri referrer = null; |
| if (r != null) { |
| if (!forAutofill) { |
| r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data); |
| r.activity.onProvideAssistData(data); |
| referrer = r.activity.onProvideReferrer(); |
| } |
| if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL || forAutofill |
| || requestedOnlyContent) { |
| if (!requestedOnlyContent) { |
| structure = new AssistStructure(r.activity, forAutofill, cmd.flags); |
| } |
| Intent activityIntent = r.activity.getIntent(); |
| boolean notSecure = r.window == null || |
| (r.window.getAttributes().flags |
| & WindowManager.LayoutParams.FLAG_SECURE) == 0; |
| if (activityIntent != null && notSecure) { |
| if (!forAutofill) { |
| Intent intent = new Intent(activityIntent); |
| intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
| | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); |
| content.setDefaultIntent(intent); |
| } |
| } else { |
| if (!forAutofill) { |
| content.setDefaultIntent(new Intent()); |
| } |
| } |
| if (!forAutofill) { |
| r.activity.onProvideAssistContent(content); |
| } |
| } |
| } |
| |
| if (!requestedOnlyContent) { |
| if (structure == null) { |
| structure = new AssistStructure(); |
| } |
| |
| // TODO: decide if lastSessionId logic applies to autofill sessions |
| |
| structure.setAcquisitionStartTime(startTime); |
| structure.setAcquisitionEndTime(SystemClock.uptimeMillis()); |
| |
| mLastAssistStructures.add(new WeakReference<>(structure)); |
| } |
| |
| IActivityTaskManager mgr = ActivityTaskManager.getService(); |
| try { |
| mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content, referrer); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** Fetches the user actions for the corresponding activity */ |
| private void handleRequestDirectActions(@NonNull IBinder activityToken, |
| @NonNull IVoiceInteractor interactor, @NonNull CancellationSignal cancellationSignal, |
| @NonNull RemoteCallback callback, int retryCount) { |
| final ActivityClientRecord r = mActivities.get(activityToken); |
| if (r == null) { |
| Log.w(TAG, "requestDirectActions(): no activity for " + activityToken); |
| callback.sendResult(null); |
| return; |
| } |
| final int lifecycleState = r.getLifecycleState(); |
| if (lifecycleState < ON_START) { |
| // TODO(b/234173463): requestDirectActions callback should indicate errors |
| if (retryCount > 0) { |
| mH.sendMessageDelayed( |
| PooledLambda.obtainMessage(ActivityThread::handleRequestDirectActions, |
| ActivityThread.this, activityToken, interactor, cancellationSignal, |
| callback, retryCount - 1), REQUEST_DIRECT_ACTIONS_RETRY_TIME_MS); |
| return; |
| } |
| Log.w(TAG, "requestDirectActions(" + r + "): wrong lifecycle: " + lifecycleState); |
| callback.sendResult(null); |
| return; |
| } |
| if (lifecycleState >= ON_STOP) { |
| Log.w(TAG, "requestDirectActions(" + r + "): wrong lifecycle: " + lifecycleState); |
| callback.sendResult(null); |
| return; |
| } |
| if (r.activity.mVoiceInteractor == null |
| || r.activity.mVoiceInteractor.mInteractor.asBinder() |
| != interactor.asBinder()) { |
| if (r.activity.mVoiceInteractor != null) { |
| r.activity.mVoiceInteractor.destroy(); |
| } |
| r.activity.mVoiceInteractor = new VoiceInteractor(interactor, r.activity, |
| r.activity, Looper.myLooper()); |
| } |
| r.activity.onGetDirectActions(cancellationSignal, (actions) -> { |
| Objects.requireNonNull(actions); |
| Preconditions.checkCollectionElementsNotNull(actions, "actions"); |
| if (!actions.isEmpty()) { |
| final int actionCount = actions.size(); |
| for (int i = 0; i < actionCount; i++) { |
| final DirectAction action = actions.get(i); |
| action.setSource(r.activity.getTaskId(), r.activity.getAssistToken()); |
| } |
| final Bundle result = new Bundle(); |
| result.putParcelable(DirectAction.KEY_ACTIONS_LIST, |
| new ParceledListSlice<>(actions)); |
| callback.sendResult(result); |
| } else { |
| callback.sendResult(null); |
| } |
| }); |
| } |
| |
| /** Performs an actions in the corresponding activity */ |
| private void handlePerformDirectAction(@NonNull IBinder activityToken, |
| @NonNull String actionId, @Nullable Bundle arguments, |
| @NonNull CancellationSignal cancellationSignal, |
| @NonNull RemoteCallback resultCallback) { |
| final ActivityClientRecord r = mActivities.get(activityToken); |
| if (r != null) { |
| final int lifecycleState = r.getLifecycleState(); |
| if (lifecycleState < ON_START || lifecycleState >= ON_STOP) { |
| resultCallback.sendResult(null); |
| return; |
| } |
| final Bundle nonNullArguments = (arguments != null) ? arguments : Bundle.EMPTY; |
| r.activity.onPerformDirectAction(actionId, nonNullArguments, cancellationSignal, |
| resultCallback::sendResult); |
| } else { |
| resultCallback.sendResult(null); |
| } |
| } |
| |
| public void handleTranslucentConversionComplete(IBinder token, boolean drawComplete) { |
| ActivityClientRecord r = mActivities.get(token); |
| if (r != null) { |
| r.activity.onTranslucentConversionComplete(drawComplete); |
| } |
| } |
| |
| public void onNewSceneTransitionInfo(IBinder token, SceneTransitionInfo info) { |
| ActivityClientRecord r = mActivities.get(token); |
| if (r != null) { |
| r.activity.onNewSceneTransitionInfo(info); |
| } |
| } |
| |
| public void handleInstallProvider(ProviderInfo info) { |
| final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| try { |
| installContentProviders(mInitialApplication, Arrays.asList(info)); |
| } finally { |
| StrictMode.setThreadPolicy(oldPolicy); |
| } |
| } |
| |
| private void handleEnterAnimationComplete(IBinder token) { |
| ActivityClientRecord r = mActivities.get(token); |
| if (r != null) { |
| r.activity.dispatchEnterAnimationComplete(); |
| } |
| } |
| |
| private void handleStartBinderTracking() { |
| Binder.enableStackTracking(); |
| } |
| |
| private void handleStopBinderTrackingAndDump(ParcelFileDescriptor fd) { |
| try { |
| Binder.disableStackTracking(); |
| Binder.getTransactionTracker().writeTracesToFile(fd); |
| } finally { |
| IoUtils.closeQuietly(fd); |
| Binder.getTransactionTracker().clearTraces(); |
| } |
| } |
| |
| @Override |
| public void handlePictureInPictureRequested(ActivityClientRecord r) { |
| final boolean receivedByApp = r.activity.onPictureInPictureRequested(); |
| if (!receivedByApp) { |
| // Previous recommendation was for apps to enter picture-in-picture in |
| // onUserLeavingHint() for cases such as the app being put into the background. For |
| // backwards compatibility with apps that are not using the newer |
| // onPictureInPictureRequested() callback, we schedule the life cycle events needed to |
| // trigger onUserLeavingHint(), then we return the activity to its previous state. |
| schedulePauseWithUserLeaveHintAndReturnToCurrentState(r); |
| } |
| } |
| |
| @Override |
| public void handlePictureInPictureStateChanged(@NonNull ActivityClientRecord r, |
| PictureInPictureUiState pipState) { |
| r.activity.onPictureInPictureUiStateChanged(pipState); |
| } |
| |
| /** |
| * Register a splash screen manager to this process. |
| */ |
| public void registerSplashScreenManager( |
| @NonNull SplashScreen.SplashScreenManagerGlobal manager) { |
| synchronized (this) { |
| mSplashScreenGlobal = manager; |
| } |
| } |
| |
| @Override |
| public boolean isHandleSplashScreenExit(@NonNull IBinder token) { |
| synchronized (this) { |
| return mSplashScreenGlobal != null && mSplashScreenGlobal.containsExitListener(token); |
| } |
| } |
| |
| @Override |
| public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r, |
| @Nullable SplashScreenView.SplashScreenViewParcelable parcelable, |
| @NonNull SurfaceControl startingWindowLeash) { |
| final DecorView decorView = (DecorView) r.window.peekDecorView(); |
| if (parcelable != null && decorView != null) { |
| createSplashScreen(r, decorView, parcelable, startingWindowLeash); |
| } else { |
| // shouldn't happen! |
| Slog.e(TAG, "handleAttachSplashScreenView failed, unable to attach"); |
| } |
| } |
| |
| private void createSplashScreen(ActivityClientRecord r, DecorView decorView, |
| SplashScreenView.SplashScreenViewParcelable parcelable, |
| @NonNull SurfaceControl startingWindowLeash) { |
| final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity); |
| final SplashScreenView view = builder.createFromParcel(parcelable).build(); |
| view.attachHostWindow(r.window); |
| decorView.addView(view); |
| view.requestLayout(); |
| |
| view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { |
| private boolean mHandled = false; |
| @Override |
| public boolean onPreDraw() { |
| if (mHandled) { |
| return true; |
| } |
| mHandled = true; |
| // Transfer the splash screen view from shell to client. |
| // Call syncTransferSplashscreenViewTransaction at the first onPreDraw, so we can |
| // ensure the client view is ready to show, and can use applyTransactionOnDraw to |
| // make all transitions happen at the same frame. |
| syncTransferSplashscreenViewTransaction( |
| view, r.token, decorView, startingWindowLeash); |
| view.post(() -> view.getViewTreeObserver().removeOnPreDrawListener(this)); |
| return true; |
| } |
| }); |
| } |
| |
| private void reportSplashscreenViewShown(IBinder token, SplashScreenView view) { |
| ActivityClient.getInstance().reportSplashScreenAttached(token); |
| synchronized (this) { |
| if (mSplashScreenGlobal != null) { |
| mSplashScreenGlobal.handOverSplashScreenView(token, view); |
| } |
| } |
| } |
| |
| private void syncTransferSplashscreenViewTransaction(SplashScreenView view, IBinder token, |
| View decorView, @NonNull SurfaceControl startingWindowLeash) { |
| // Ensure splash screen view is shown before remove the splash screen window. |
| // Once the copied splash screen view is onDrawn on decor view, use applyTransactionOnDraw |
| // to ensure the transfer of surface view and hide starting window are happen at the same |
| // frame. |
| final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); |
| transaction.hide(startingWindowLeash); |
| |
| decorView.getViewRootImpl().applyTransactionOnDraw(transaction); |
| view.syncTransferSurfaceOnDraw(); |
| // Tell server we can remove the starting window |
| decorView.postOnAnimation(() -> reportSplashscreenViewShown(token, view)); |
| } |
| |
| /** |
| * Cycle activity through onPause and onUserLeaveHint so that PIP is entered if supported, then |
| * return to its previous state. This allows activities that rely on onUserLeaveHint instead of |
| * onPictureInPictureRequested to enter picture-in-picture. |
| */ |
| private void schedulePauseWithUserLeaveHintAndReturnToCurrentState(ActivityClientRecord r) { |
| final int prevState = r.getLifecycleState(); |
| if (prevState != ON_RESUME && prevState != ON_PAUSE) { |
| return; |
| } |
| |
| switch (prevState) { |
| case ON_RESUME: |
| // Schedule a PAUSE then return to RESUME. |
| schedulePauseWithUserLeavingHint(r); |
| scheduleResume(r); |
| break; |
| case ON_PAUSE: |
| // Schedule a RESUME then return to PAUSE. |
| scheduleResume(r); |
| schedulePauseWithUserLeavingHint(r); |
| break; |
| } |
| } |
| |
| private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) { |
| final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); |
| final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token, |
| r.activity.isFinishing(), /* userLeaving */ true, |
| /* dontReport */ false, /* autoEnteringPip */ false); |
| transaction.addTransactionItem(pauseActivityItem); |
| executeTransaction(transaction); |
| } |
| |
| private void scheduleResume(ActivityClientRecord r) { |
| final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); |
| final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token, |
| /* isForward */ false, /* shouldSendCompatFakeFocus */ false); |
| transaction.addTransactionItem(resumeActivityItem); |
| executeTransaction(transaction); |
| } |
| |
| private void handleLocalVoiceInteractionStarted(IBinder token, IVoiceInteractor interactor) { |
| final ActivityClientRecord r = mActivities.get(token); |
| if (r != null) { |
| r.voiceInteractor = interactor; |
| r.activity.setVoiceInteractor(interactor); |
| if (interactor == null) { |
| r.activity.onLocalVoiceInteractionStopped(); |
| } else { |
| r.activity.onLocalVoiceInteractionStarted(); |
| } |
| } |
| } |
| |
| private static boolean attemptAttachAgent(String agent, ClassLoader classLoader) { |
| try { |
| VMDebug.attachAgent(agent, classLoader); |
| return true; |
| } catch (IOException e) { |
| Slog.e(TAG, "Attaching agent with " + classLoader + " failed: " + agent); |
| return false; |
| } |
| } |
| |
| static void handleAttachAgent(String agent, LoadedApk loadedApk) { |
| ClassLoader classLoader = loadedApk != null ? loadedApk.getClassLoader() : null; |
| if (attemptAttachAgent(agent, classLoader)) { |
| return; |
| } |
| if (classLoader != null) { |
| attemptAttachAgent(agent, null); |
| } |
| } |
| |
| static void handleAttachStartupAgents(String dataDir) { |
| try { |
| Path codeCache = ContextImpl.getCodeCacheDirBeforeBind(new File(dataDir)).toPath(); |
| if (!Files.exists(codeCache)) { |
| return; |
| } |
| Path startupPath = codeCache.resolve("startup_agents"); |
| if (Files.exists(startupPath)) { |
| try (DirectoryStream<Path> startupFiles = Files.newDirectoryStream(startupPath)) { |
| for (Path p : startupFiles) { |
| handleAttachAgent( |
| p.toAbsolutePath().toString() |
| + "=" |
| + dataDir, |
| null); |
| } |
| } |
| } |
| } catch (Exception e) { |
| // Ignored. |
| } |
| } |
| |
| private void updateUiTranslationState(IBinder activityToken, int state, |
| TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds, |
| UiTranslationSpec uiTranslationSpec) { |
| final ActivityClientRecord r = mActivities.get(activityToken); |
| if (r == null) { |
| Log.w(TAG, "updateUiTranslationState(): no activity for " + activityToken); |
| return; |
| } |
| r.activity.updateUiTranslationState( |
| state, sourceSpec, targetSpec, viewIds, uiTranslationSpec); |
| } |
| |
| private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>(); |
| |
| /** |
| * Return the Intent that's currently being handled by a |
| * BroadcastReceiver on this thread, or null if none. |
| * @hide |
| */ |
| public static Intent getIntentBeingBroadcast() { |
| return sCurrentBroadcastIntent.get(); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) |
| private void handleReceiver(ReceiverData data) { |
| // If we are getting ready to gc after going to the background, well |
| // we are back active so skip it. |
| unscheduleGcIdler(); |
| |
| String component = data.intent.getComponent().getClassName(); |
| |
| final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo); |
| |
| IActivityManager mgr = ActivityManager.getService(); |
| |
| Application app; |
| BroadcastReceiver receiver; |
| ContextImpl context; |
| try { |
| app = packageInfo.makeApplicationInner(false, mInstrumentation); |
| context = (ContextImpl) app.getBaseContext(); |
| if (data.info.splitName != null) { |
| context = (ContextImpl) context.createContextForSplit(data.info.splitName); |
| } |
| if (data.info.attributionTags != null && data.info.attributionTags.length > 0) { |
| final String attributionTag = data.info.attributionTags[0]; |
| context = (ContextImpl) context.createAttributionContext(attributionTag); |
| } |
| java.lang.ClassLoader cl = context.getClassLoader(); |
| data.intent.setExtrasClassLoader(cl); |
| data.intent.prepareToEnterProcess( |
| isProtectedComponent(data.info) || isProtectedBroadcast(data.intent), |
| context.getAttributionSource()); |
| data.setExtrasClassLoader(cl); |
| receiver = packageInfo.getAppFactory() |
| .instantiateReceiver(cl, data.info.name, data.intent); |
| } catch (Exception e) { |
| if (DEBUG_BROADCAST) Slog.i(TAG, |
| "Finishing failed broadcast to " + data.intent.getComponent()); |
| data.sendFinished(mgr); |
| throw new RuntimeException( |
| "Unable to instantiate receiver " + component |
| + ": " + e.toString(), e); |
| } |
| |
| try { |
| if (localLOGV) Slog.v( |
| TAG, "Performing receive of " + data.intent |
| + ": app=" + app |
| + ", appName=" + app.getPackageName() |
| + ", pkg=" + packageInfo.getPackageName() |
| + ", comp=" + data.intent.getComponent().toShortString() |
| + ", dir=" + packageInfo.getAppDir()); |
| |
| sCurrentBroadcastIntent.set(data.intent); |
| receiver.setPendingResult(data); |
| receiver.onReceive(context.getReceiverRestrictedContext(), |
| data.intent); |
| } catch (Exception e) { |
| if (DEBUG_BROADCAST) Slog.i(TAG, |
| "Finishing failed broadcast to " + data.intent.getComponent()); |
| data.sendFinished(mgr); |
| if (!mInstrumentation.onException(receiver, e)) { |
| throw new RuntimeException( |
| "Unable to start receiver " + component |
| + ": " + e.toString(), e); |
| } |
| } finally { |
| sCurrentBroadcastIntent.set(null); |
| } |
| |
| if (receiver.getPendingResult() != null) { |
| data.finish(); |
| } |
| } |
| |
| // Instantiate a BackupAgent and tell it that it's alive |
| private void handleCreateBackupAgent(CreateBackupAgentData data) { |
| if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data); |
| |
| // Validity check the requested target package's uid against ours |
| try { |
| PackageInfo requestedPackage = getPackageManager().getPackageInfo( |
| data.appInfo.packageName, 0, UserHandle.myUserId()); |
| if (requestedPackage.applicationInfo.uid != Process.myUid()) { |
| Slog.w(TAG, "Asked to instantiate non-matching package " |
| + data.appInfo.packageName); |
| return; |
| } |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| |
| // no longer idle; we have backup work to do |
| unscheduleGcIdler(); |
| |
| // instantiate the BackupAgent class named in the manifest |
| final LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo); |
| String packageName = packageInfo.mPackageName; |
| if (packageName == null) { |
| Slog.d(TAG, "Asked to create backup agent for nonexistent package"); |
| return; |
| } |
| |
| String classname = getBackupAgentName(data); |
| |
| try { |
| IBinder binder = null; |
| ArrayMap<String, BackupAgent> backupAgents = getBackupAgentsForUser(data.userId); |
| BackupAgent agent = backupAgents.get(packageName); |
| if (agent != null) { |
| // reusing the existing instance |
| if (DEBUG_BACKUP) { |
| Slog.v(TAG, "Reusing existing agent instance"); |
| } |
| binder = agent.onBind(); |
| } else { |
| try { |
| if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname); |
| |
| java.lang.ClassLoader cl = packageInfo.getClassLoader(); |
| agent = (BackupAgent) cl.loadClass(classname).newInstance(); |
| |
| // set up the agent's context |
| ContextImpl context = ContextImpl.createAppContext(this, packageInfo); |
| context.setOuterContext(agent); |
| agent.attach(context); |
| |
| agent.onCreate(UserHandle.of(data.userId), data.backupDestination, |
| getOperationTypeFromBackupMode(data.backupMode)); |
| binder = agent.onBind(); |
| backupAgents.put(packageName, agent); |
| } catch (Exception e) { |
| // If this is during restore, fail silently; otherwise go |
| // ahead and let the user see the crash. |
| Slog.e(TAG, "Agent threw during creation: " + e); |
| if (data.backupMode != ApplicationThreadConstants.BACKUP_MODE_RESTORE |
| && data.backupMode != |
| ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL) { |
| throw e; |
| } |
| // falling through with 'binder' still null |
| } |
| } |
| |
| // tell the OS that we're live now |
| try { |
| ActivityManager.getService().backupAgentCreated(packageName, binder, data.userId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } catch (Exception e) { |
| throw new RuntimeException("Unable to create BackupAgent " |
| + classname + ": " + e.toString(), e); |
| } |
| } |
| |
| @OperationType |
| private static int getOperationTypeFromBackupMode(int backupMode) { |
| switch (backupMode) { |
| case ApplicationThreadConstants.BACKUP_MODE_RESTORE: |
| case ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL: |
| return OperationType.RESTORE; |
| case ApplicationThreadConstants.BACKUP_MODE_FULL: |
| case ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL: |
| return OperationType.BACKUP; |
| default: |
| Slog.w(TAG, "Invalid backup mode when initialising BackupAgent: " |
| + backupMode); |
| return OperationType.UNKNOWN; |
| } |
| } |
| |
| private String getBackupAgentName(CreateBackupAgentData data) { |
| String agentName = data.appInfo.backupAgentName; |
| // full backup operation but no app-supplied agent? use the default implementation |
| if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL |
| || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) { |
| agentName = DEFAULT_FULL_BACKUP_AGENT; |
| } |
| return agentName; |
| } |
| |
| // Tear down a BackupAgent |
| private void handleDestroyBackupAgent(CreateBackupAgentData data) { |
| if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data); |
| |
| final LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo); |
| String packageName = packageInfo.mPackageName; |
| ArrayMap<String, BackupAgent> backupAgents = getBackupAgentsForUser(data.userId); |
| BackupAgent agent = backupAgents.get(packageName); |
| if (agent != null) { |
| try { |
| agent.onDestroy(); |
| } catch (Exception e) { |
| Slog.w(TAG, "Exception thrown in onDestroy by backup agent of " + data.appInfo); |
| e.printStackTrace(); |
| } |
| backupAgents.remove(packageName); |
| } else { |
| Slog.w(TAG, "Attempt to destroy unknown backup agent " + data); |
| } |
| } |
| |
| private ArrayMap<String, BackupAgent> getBackupAgentsForUser(int userId) { |
| ArrayMap<String, BackupAgent> backupAgents = mBackupAgentsByUser.get(userId); |
| if (backupAgents == null) { |
| backupAgents = new ArrayMap<>(); |
| mBackupAgentsByUser.put(userId, backupAgents); |
| } |
| return backupAgents; |
| } |
| |
| @UnsupportedAppUsage |
| private void handleCreateService(CreateServiceData data) { |
| // If we are getting ready to gc after going to the background, well |
| // we are back active so skip it. |
| unscheduleGcIdler(); |
| |
| final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo); |
| Service service = null; |
| try { |
| if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); |
| |
| Application app = packageInfo.makeApplicationInner(false, mInstrumentation); |
| |
| final java.lang.ClassLoader cl; |
| if (data.info.splitName != null) { |
| cl = packageInfo.getSplitClassLoader(data.info.splitName); |
| } else { |
| cl = packageInfo.getClassLoader(); |
| } |
| service = packageInfo.getAppFactory() |
| .instantiateService(cl, data.info.name, data.intent); |
| ContextImpl context = ContextImpl.getImpl(service |
| .createServiceBaseContext(this, packageInfo)); |
| if (data.info.splitName != null) { |
| context = (ContextImpl) context.createContextForSplit(data.info.splitName); |
| } |
| if (data.info.attributionTags != null && data.info.attributionTags.length > 0) { |
| final String attributionTag = data.info.attributionTags[0]; |
| context = (ContextImpl) context.createAttributionContext(attributionTag); |
| } |
| // Service resources must be initialized with the same loaders as the application |
| // context. |
| context.getResources().addLoaders( |
| app.getResources().getLoaders().toArray(new ResourcesLoader[0])); |
| |
| context.setOuterContext(service); |
| service.attach(context, this, data.info.name, data.token, app, |
| ActivityManager.getService()); |
| if (!service.isUiContext()) { // WindowProviderService is a UI Context. |
| if (mLastReportedDeviceId == Context.DEVICE_ID_DEFAULT) { |
| service.updateDeviceId(mLastReportedDeviceId); |
| } else { |
| VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class); |
| if (vdm != null && vdm.isValidVirtualDeviceId(mLastReportedDeviceId)) { |
| service.updateDeviceId(mLastReportedDeviceId); |
| } |
| } |
| } |
| service.onCreate(); |
| mServicesData.put(data.token, data); |
| mServices.put(data.token, service); |
| try { |
| ActivityManager.getService().serviceDoneExecuting( |
| data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0, null); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(service, e)) { |
| throw new RuntimeException( |
| "Unable to create service " + data.info.name |
| + ": " + e.toString(), e); |
| } |
| } |
| } |
| |
| private void handleBindService(BindServiceData data) { |
| CreateServiceData createData = mServicesData.get(data.token); |
| Service s = mServices.get(data.token); |
| if (DEBUG_SERVICE) |
| Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind); |
| if (s != null) { |
| try { |
| data.intent.setExtrasClassLoader(s.getClassLoader()); |
| data.intent.prepareToEnterProcess(isProtectedComponent(createData.info), |
| s.getAttributionSource()); |
| try { |
| if (!data.rebind) { |
| IBinder binder = s.onBind(data.intent); |
| ActivityManager.getService().publishService( |
| data.token, data.intent, binder); |
| } else { |
| s.onRebind(data.intent); |
| ActivityManager.getService().serviceDoneExecuting( |
| data.token, SERVICE_DONE_EXECUTING_REBIND, 0, 0, data.intent); |
| } |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(s, e)) { |
| throw new RuntimeException( |
| "Unable to bind to service " + s |
| + " with " + data.intent + ": " + e.toString(), e); |
| } |
| } |
| } |
| } |
| |
| private void handleUnbindService(BindServiceData data) { |
| CreateServiceData createData = mServicesData.get(data.token); |
| Service s = mServices.get(data.token); |
| if (s != null) { |
| try { |
| data.intent.setExtrasClassLoader(s.getClassLoader()); |
| data.intent.prepareToEnterProcess(isProtectedComponent(createData.info), |
| s.getAttributionSource()); |
| boolean doRebind = s.onUnbind(data.intent); |
| try { |
| if (doRebind) { |
| ActivityManager.getService().unbindFinished( |
| data.token, data.intent, doRebind); |
| } else { |
| ActivityManager.getService().serviceDoneExecuting( |
| data.token, SERVICE_DONE_EXECUTING_UNBIND, 0, 0, data.intent); |
| } |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(s, e)) { |
| throw new RuntimeException( |
| "Unable to unbind to service " + s |
| + " with " + data.intent + ": " + e.toString(), e); |
| } |
| } |
| } |
| } |
| |
| private void handleDumpGfxInfo(DumpComponentInfo info) { |
| final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| try { |
| ThreadedRenderer.handleDumpGfxInfo(info.fd.getFileDescriptor(), info.args); |
| } catch (Exception e) { |
| Log.w(TAG, "Caught exception from dumpGfxInfo()", e); |
| } finally { |
| IoUtils.closeQuietly(info.fd); |
| StrictMode.setThreadPolicy(oldPolicy); |
| } |
| } |
| |
| private void handleDumpService(DumpComponentInfo info) { |
| final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| try { |
| Service s = mServices.get(info.token); |
| if (s != null) { |
| PrintWriter pw = new FastPrintWriter(new FileOutputStream( |
| info.fd.getFileDescriptor())); |
| s.dump(info.fd.getFileDescriptor(), pw, info.args); |
| pw.flush(); |
| } |
| } finally { |
| IoUtils.closeQuietly(info.fd); |
| StrictMode.setThreadPolicy(oldPolicy); |
| } |
| } |
| |
| private void handleDumpResources(DumpResourcesData info) { |
| final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| try { |
| PrintWriter pw = new FastPrintWriter(new FileOutputStream( |
| info.fd.getFileDescriptor())); |
| |
| Resources.dumpHistory(pw, ""); |
| pw.flush(); |
| if (info.finishCallback != null) { |
| info.finishCallback.sendResult(null); |
| } |
| } finally { |
| IoUtils.closeQuietly(info.fd); |
| StrictMode.setThreadPolicy(oldPolicy); |
| } |
| } |
| |
| private void handleDumpActivity(DumpComponentInfo info) { |
| final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| try { |
| ActivityClientRecord r = mActivities.get(info.token); |
| if (r != null && r.activity != null) { |
| PrintWriter pw = new FastPrintWriter(new FileOutputStream( |
| info.fd.getFileDescriptor())); |
| r.activity.dumpInternal(info.prefix, info.fd.getFileDescriptor(), pw, info.args); |
| pw.flush(); |
| } |
| } finally { |
| IoUtils.closeQuietly(info.fd); |
| StrictMode.setThreadPolicy(oldPolicy); |
| } |
| } |
| |
| private void handleDumpProvider(DumpComponentInfo info) { |
| final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| try { |
| ProviderClientRecord r = mLocalProviders.get(info.token); |
| if (r != null && r.mLocalProvider != null) { |
| PrintWriter pw = new FastPrintWriter(new FileOutputStream( |
| info.fd.getFileDescriptor())); |
| r.mLocalProvider.dump(info.fd.getFileDescriptor(), pw, info.args); |
| pw.flush(); |
| } |
| } finally { |
| IoUtils.closeQuietly(info.fd); |
| StrictMode.setThreadPolicy(oldPolicy); |
| } |
| } |
| |
| private void handleServiceArgs(ServiceArgsData data) { |
| CreateServiceData createData = mServicesData.get(data.token); |
| Service s = mServices.get(data.token); |
| if (s != null) { |
| try { |
| if (data.args != null) { |
| data.args.setExtrasClassLoader(s.getClassLoader()); |
| data.args.prepareToEnterProcess(isProtectedComponent(createData.info), |
| s.getAttributionSource()); |
| } |
| int res; |
| if (!data.taskRemoved) { |
| res = s.onStartCommand(data.args, data.flags, data.startId); |
| } else { |
| s.onTaskRemoved(data.args); |
| res = Service.START_TASK_REMOVED_COMPLETE; |
| } |
| |
| QueuedWork.waitToFinish(); |
| |
| try { |
| ActivityManager.getService().serviceDoneExecuting( |
| data.token, SERVICE_DONE_EXECUTING_START, data.startId, res, null); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(s, e)) { |
| throw new RuntimeException( |
| "Unable to start service " + s |
| + " with " + data.args + ": " + e.toString(), e); |
| } |
| } |
| } |
| } |
| |
| private void handleStopService(IBinder token) { |
| mServicesData.remove(token); |
| Service s = mServices.remove(token); |
| if (s != null) { |
| try { |
| if (localLOGV) Slog.v(TAG, "Destroying service " + s); |
| s.onDestroy(); |
| s.detachAndCleanUp(); |
| Context context = s.getBaseContext(); |
| if (context instanceof ContextImpl) { |
| final String who = s.getClassName(); |
| ((ContextImpl) context).scheduleFinalCleanup(who, "Service"); |
| } |
| |
| QueuedWork.waitToFinish(); |
| |
| try { |
| ActivityManager.getService().serviceDoneExecuting( |
| token, SERVICE_DONE_EXECUTING_STOP, 0, 0, null); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(s, e)) { |
| throw new RuntimeException( |
| "Unable to stop service " + s |
| + ": " + e.toString(), e); |
| } |
| Slog.i(TAG, "handleStopService: exception for " + token, e); |
| } |
| } else { |
| Slog.i(TAG, "handleStopService: token=" + token + " not found."); |
| } |
| //Slog.i(TAG, "Running services: " + mServices); |
| } |
| |
| private void handleTimeoutService(IBinder token, int startId) { |
| Service s = mServices.get(token); |
| if (s != null) { |
| try { |
| if (localLOGV) Slog.v(TAG, "Timeout short service " + s); |
| |
| // Unlike other service callbacks, we don't do serviceDoneExecuting() here. |
| // "service executing" state is used to boost the procstate / oom-adj, but |
| // for short-FGS timeout, we have a specific control for them anyway, so |
| // we don't have to do that. |
| s.callOnTimeout(startId); |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(s, e)) { |
| throw new RuntimeException( |
| "Unable to call onTimeout on service " + s |
| + ": " + e.toString(), e); |
| } |
| Slog.i(TAG, "handleTimeoutService: exception for " + token, e); |
| } |
| } else { |
| Slog.wtf(TAG, "handleTimeoutService: token=" + token + " not found."); |
| } |
| } |
| |
| private void handleTimeoutServiceForType(IBinder token, int startId, |
| @ServiceInfo.ForegroundServiceType int fgsType) { |
| Service s = mServices.get(token); |
| if (s != null) { |
| try { |
| if (localLOGV) Slog.v(TAG, "Timeout service " + s); |
| |
| s.callOnTimeLimitExceeded(startId, fgsType); |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(s, e)) { |
| throw new RuntimeException( |
| "Unable to call onTimeLimitExceeded on service " + s + ": " + e, e); |
| } |
| Slog.i(TAG, "handleTimeoutServiceForType: exception for " + token, e); |
| } |
| } else { |
| Slog.wtf(TAG, "handleTimeoutServiceForType: token=" + token + " not found."); |
| } |
| } |
| |
| /** |
| * Resume the activity. |
| * @param r Target activity record. |
| * @param finalStateRequest Flag indicating if this is part of final state resolution for a |
| * transaction. |
| * @param reason Reason for performing the action. |
| * |
| * @return {@code true} that was resumed, {@code false} otherwise. |
| */ |
| @VisibleForTesting |
| public boolean performResumeActivity(ActivityClientRecord r, boolean finalStateRequest, |
| String reason) { |
| if (localLOGV) { |
| Slog.v(TAG, "Performing resume of " + r + " finished=" + r.activity.mFinished); |
| } |
| if (r.activity.mFinished) { |
| return false; |
| } |
| if (r.getLifecycleState() == ON_RESUME) { |
| if (!finalStateRequest) { |
| final RuntimeException e = new IllegalStateException( |
| "Trying to resume activity which is already resumed"); |
| Slog.e(TAG, e.getMessage(), e); |
| Slog.e(TAG, r.getStateString()); |
| // TODO(lifecycler): A double resume request is possible when an activity |
| // receives two consequent transactions with relaunch requests and "resumed" |
| // final state requests and the second relaunch is omitted. We still try to |
| // handle two resume requests for the final state. For cases other than this |
| // one, we don't expect it to happen. |
| } |
| return false; |
| } |
| if (finalStateRequest) { |
| r.hideForNow = false; |
| r.activity.mStartedActivity = false; |
| } |
| try { |
| r.activity.onStateNotSaved(); |
| r.activity.mFragments.noteStateNotSaved(); |
| checkAndBlockForNetworkAccess(); |
| if (r.pendingIntents != null) { |
| deliverNewIntents(r, r.pendingIntents); |
| r.pendingIntents = null; |
| } |
| if (r.pendingResults != null) { |
| deliverResults(r, r.pendingResults, reason); |
| r.pendingResults = null; |
| } |
| r.activity.performResume(r.startsNotResumed, reason); |
| |
| r.state = null; |
| r.persistentState = null; |
| r.setState(ON_RESUME); |
| |
| reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming"); |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(r.activity, e)) { |
| throw new RuntimeException("Unable to resume activity " |
| + r.intent.getComponent().toShortString() + ": " + e.toString(), e); |
| } |
| } |
| return true; |
| } |
| |
| static final void cleanUpPendingRemoveWindows(ActivityClientRecord r, boolean force) { |
| if (r.mPreserveWindow && !force) { |
| return; |
| } |
| if (r.mPendingRemoveWindow != null) { |
| r.mPendingRemoveWindowManager.removeViewImmediate( |
| r.mPendingRemoveWindow.getDecorView()); |
| IBinder wtoken = r.mPendingRemoveWindow.getDecorView().getWindowToken(); |
| if (wtoken != null) { |
| WindowManagerGlobal.getInstance().closeAll(wtoken, |
| r.activity.getClass().getName(), "Activity"); |
| } |
| } |
| r.mPendingRemoveWindow = null; |
| r.mPendingRemoveWindowManager = null; |
| } |
| |
| @Override |
| public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest, |
| boolean isForward, boolean shouldSendCompatFakeFocus, String reason) { |
| // If we are getting ready to gc after going to the background, well |
| // we are back active so skip it. |
| unscheduleGcIdler(); |
| mSomeActivitiesChanged = true; |
| |
| // TODO Push resumeArgs into the activity for consideration |
| // skip below steps for double-resume and r.mFinish = true case. |
| if (!performResumeActivity(r, finalStateRequest, reason)) { |
| return; |
| } |
| if (mActivitiesToBeDestroyed.containsKey(r.token)) { |
| // Although the activity is resumed, it is going to be destroyed. So the following |
| // UI operations are unnecessary and also prevents exception because its token may |
| // be gone that window manager cannot recognize it. All necessary cleanup actions |
| // performed below will be done while handling destruction. |
| return; |
| } |
| |
| final Activity a = r.activity; |
| |
| if (localLOGV) { |
| Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity |
| + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished); |
| } |
| |
| final int forwardBit = isForward |
| ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; |
| |
| // If the window hasn't yet been added to the window manager, |
| // and this guy didn't finish itself or start another activity, |
| // then go ahead and add the window. |
| boolean willBeVisible = !a.mStartedActivity; |
| if (!willBeVisible) { |
| willBeVisible = ActivityClient.getInstance().willActivityBeVisible( |
| a.getActivityToken()); |
| } |
| if (r.window == null && !a.mFinished && willBeVisible) { |
| r.window = r.activity.getWindow(); |
| View decor = r.window.getDecorView(); |
| decor.setVisibility(View.INVISIBLE); |
| ViewManager wm = a.getWindowManager(); |
| WindowManager.LayoutParams l = r.window.getAttributes(); |
| a.mDecor = decor; |
| l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; |
| l.softInputMode |= forwardBit; |
| if (r.mPreserveWindow) { |
| a.mWindowAdded = true; |
| r.mPreserveWindow = false; |
| // Normally the ViewRoot sets up callbacks with the Activity |
| // in addView->ViewRootImpl#setView. If we are instead reusing |
| // the decor view we have to notify the view root that the |
| // callbacks may have changed. |
| ViewRootImpl impl = decor.getViewRootImpl(); |
| if (impl != null) { |
| impl.notifyChildRebuilt(); |
| } |
| } |
| if (a.mVisibleFromClient) { |
| if (!a.mWindowAdded) { |
| a.mWindowAdded = true; |
| wm.addView(decor, l); |
| } else { |
| // The activity will get a callback for this {@link LayoutParams} change |
| // earlier. However, at that time the decor will not be set (this is set |
| // in this method), so no action will be taken. This call ensures the |
| // callback occurs with the decor set. |
| a.onWindowAttributesChanged(l); |
| } |
| } |
| |
| // If the window has already been added, but during resume |
| // we started another activity, then don't yet make the |
| // window visible. |
| } else if (!willBeVisible) { |
| if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); |
| r.hideForNow = true; |
| } |
| |
| // Get rid of anything left hanging around. |
| cleanUpPendingRemoveWindows(r, false /* force */); |
| |
| // The window is now visible if it has been added, we are not |
| // simply finishing, and we are not starting another activity. |
| if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { |
| if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward); |
| ViewRootImpl impl = r.window.getDecorView().getViewRootImpl(); |
| WindowManager.LayoutParams l = impl != null |
| ? impl.mWindowAttributes : r.window.getAttributes(); |
| if ((l.softInputMode |
| & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) |
| != forwardBit) { |
| l.softInputMode = (l.softInputMode |
| & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) |
| | forwardBit; |
| if (r.activity.mVisibleFromClient) { |
| ViewManager wm = a.getWindowManager(); |
| View decor = r.window.getDecorView(); |
| wm.updateViewLayout(decor, l); |
| } |
| } |
| |
| r.activity.mVisibleFromServer = true; |
| mNumVisibleActivities++; |
| if (r.activity.mVisibleFromClient) { |
| r.activity.makeVisible(); |
| } |
| |
| if (shouldSendCompatFakeFocus) { |
| // Attaching to a window is asynchronous with the activity being resumed, |
| // so it's possible we will need to send a fake focus event after attaching |
| if (impl != null) { |
| impl.dispatchCompatFakeFocus(); |
| } else { |
| r.window.getDecorView().fakeFocusAfterAttachingToWindow(); |
| } |
| } |
| } |
| |
| mNewActivities.add(r); |
| if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r); |
| Looper.myQueue().addIdleHandler(new Idler()); |
| } |
| |
| |
| @Override |
| public void handleTopResumedActivityChanged(ActivityClientRecord r, boolean onTop, |
| String reason) { |
| if (DEBUG_ORDER) { |
| Slog.d(TAG, "Received position change to top: " + onTop + " for activity: " + r); |
| } |
| |
| if (r.isTopResumedActivity == onTop) { |
| if (!Build.IS_DEBUGGABLE) { |
| Slog.w(TAG, "Activity top position already set to onTop=" + onTop); |
| return; |
| } |
| // TODO(b/209744518): Remove this short-term workaround while fixing the binder failure. |
| Slog.e(TAG, "Activity top position already set to onTop=" + onTop); |
| } |
| |
| r.isTopResumedActivity = onTop; |
| |
| if (r.getLifecycleState() == ON_RESUME) { |
| reportTopResumedActivityChanged(r, onTop, "topStateChangedWhenResumed"); |
| } else { |
| if (DEBUG_ORDER) { |
| Slog.d(TAG, "Won't deliver top position change in state=" + r.getLifecycleState()); |
| } |
| } |
| } |
| |
| /** |
| * Call {@link Activity#onTopResumedActivityChanged(boolean)} if its top resumed state changed |
| * since the last report. |
| */ |
| private void reportTopResumedActivityChanged(ActivityClientRecord r, boolean onTop, |
| String reason) { |
| if (r.lastReportedTopResumedState != onTop) { |
| r.lastReportedTopResumedState = onTop; |
| r.activity.performTopResumedActivityChanged(onTop, reason); |
| } |
| } |
| |
| @Override |
| public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving, |
| boolean autoEnteringPip, PendingTransactionActions pendingActions, |
| String reason) { |
| if (userLeaving) { |
| performUserLeavingActivity(r); |
| } |
| |
| if (autoEnteringPip) { |
| // Set mIsInPictureInPictureMode earlier in case of auto-enter-pip, see also |
| // {@link Activity#enterPictureInPictureMode(PictureInPictureParams)}. |
| r.activity.mIsInPictureInPictureMode = true; |
| } |
| performPauseActivity(r, finished, reason, pendingActions); |
| |
| // Make sure any pending writes are now committed. |
| if (r.isPreHoneycomb()) { |
| QueuedWork.waitToFinish(); |
| } |
| mSomeActivitiesChanged = true; |
| } |
| |
| final void performUserLeavingActivity(ActivityClientRecord r) { |
| mInstrumentation.callActivityOnPictureInPictureRequested(r.activity); |
| mInstrumentation.callActivityOnUserLeaving(r.activity); |
| } |
| |
| final Bundle performPauseActivity(IBinder token, boolean finished, String reason, |
| PendingTransactionActions pendingActions) { |
| ActivityClientRecord r = mActivities.get(token); |
| return r != null ? performPauseActivity(r, finished, reason, pendingActions) : null; |
| } |
| |
| /** |
| * Pause the activity. |
| * @return Saved instance state for pre-Honeycomb apps if it was saved, {@code null} otherwise. |
| */ |
| private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, |
| PendingTransactionActions pendingActions) { |
| if (r.paused) { |
| if (r.activity.mFinished) { |
| // If we are finishing, we won't call onResume() in certain cases. |
| // So here we likewise don't want to call onPause() if the activity |
| // isn't resumed. |
| return null; |
| } |
| RuntimeException e = new RuntimeException( |
| "Performing pause of activity that is not resumed: " |
| + r.intent.getComponent().toShortString()); |
| Slog.e(TAG, e.getMessage(), e); |
| } |
| if (finished) { |
| r.activity.mFinished = true; |
| } |
| |
| // Pre-Honeycomb apps always save their state before pausing |
| final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb(); |
| if (shouldSaveState) { |
| callActivityOnSaveInstanceState(r); |
| } |
| |
| performPauseActivityIfNeeded(r, reason); |
| |
| // Notify any outstanding on paused listeners |
| ArrayList<OnActivityPausedListener> listeners; |
| synchronized (mOnPauseListeners) { |
| listeners = mOnPauseListeners.remove(r.activity); |
| } |
| int size = (listeners != null ? listeners.size() : 0); |
| for (int i = 0; i < size; i++) { |
| listeners.get(i).onPaused(r.activity); |
| } |
| |
| final Bundle oldState = pendingActions != null ? pendingActions.getOldState() : null; |
| if (oldState != null) { |
| // We need to keep around the original state, in case we need to be created again. |
| // But we only do this for pre-Honeycomb apps, which always save their state when |
| // pausing, so we can not have them save their state when restarting from a paused |
| // state. For HC and later, we want to (and can) let the state be saved as the |
| // normal part of stopping the activity. |
| if (r.isPreHoneycomb()) { |
| r.state = oldState; |
| } |
| } |
| |
| return shouldSaveState ? r.state : null; |
| } |
| |
| private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) { |
| if (r.paused) { |
| // You are already paused silly... |
| return; |
| } |
| |
| // Always reporting top resumed position loss when pausing an activity. If necessary, it |
| // will be restored in performResumeActivity(). |
| reportTopResumedActivityChanged(r, false /* onTop */, "pausing"); |
| |
| try { |
| r.activity.mCalled = false; |
| mInstrumentation.callActivityOnPause(r.activity); |
| if (!r.activity.mCalled) { |
| throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent) |
| + " did not call through to super.onPause()"); |
| } |
| } catch (SuperNotCalledException e) { |
| throw e; |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(r.activity, e)) { |
| throw new RuntimeException("Unable to pause activity " |
| + safeToComponentShortString(r.intent) + ": " + e.toString(), e); |
| } |
| } |
| r.setState(ON_PAUSE); |
| } |
| |
| // TODO(b/176961850): Make LocalActivityManager call performStopActivityInner. We cannot remove |
| // this since it's a high usage hidden API. |
| /** Called from {@link LocalActivityManager}. */ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 176961850, |
| publicAlternatives = "{@code N/A}") |
| final void performStopActivity(IBinder token, boolean saveState, String reason) { |
| ActivityClientRecord r = mActivities.get(token); |
| performStopActivityInner(r, null /* stopInfo */, saveState, false /* finalStateRequest */, |
| reason); |
| } |
| |
| private static final class ProviderRefCount { |
| public final ContentProviderHolder holder; |
| public final ProviderClientRecord client; |
| public int stableCount; |
| public int unstableCount; |
| |
| // When this is set, the stable and unstable ref counts are 0 and |
| // we have a pending operation scheduled to remove the ref count |
| // from the activity manager. On the activity manager we are still |
| // holding an unstable ref, though it is not reflected in the counts |
| // here. |
| public boolean removePending; |
| |
| ProviderRefCount(ContentProviderHolder inHolder, |
| ProviderClientRecord inClient, int sCount, int uCount) { |
| holder = inHolder; |
| client = inClient; |
| stableCount = sCount; |
| unstableCount = uCount; |
| } |
| } |
| |
| /** |
| * Core implementation of stopping an activity. |
| * @param r Target activity client record. |
| * @param info Action that will report activity stop to server. |
| * @param saveState Flag indicating whether the activity state should be saved. |
| * @param finalStateRequest Flag indicating if this call is handling final lifecycle state |
| * request for a transaction. |
| * @param reason Reason for performing this operation. |
| */ |
| private void performStopActivityInner(ActivityClientRecord r, StopInfo info, |
| boolean saveState, boolean finalStateRequest, String reason) { |
| if (localLOGV) Slog.v(TAG, "Performing stop of " + r); |
| if (r.stopped) { |
| if (r.activity.mFinished) { |
| // If we are finishing, we won't call onResume() in certain |
| // cases. So here we likewise don't want to call onStop() |
| // if the activity isn't resumed. |
| return; |
| } |
| if (!finalStateRequest) { |
| final RuntimeException e = new RuntimeException( |
| "Performing stop of activity that is already stopped: " |
| + r.intent.getComponent().toShortString()); |
| Slog.e(TAG, e.getMessage(), e); |
| Slog.e(TAG, r.getStateString()); |
| } |
| } |
| |
| // One must first be paused before stopped... |
| performPauseActivityIfNeeded(r, reason); |
| |
| if (info != null) { |
| try { |
| // First create a thumbnail for the activity... |
| // For now, don't create the thumbnail here; we are |
| // doing that by doing a screen snapshot. |
| info.setDescription(r.activity.onCreateDescription()); |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(r.activity, e)) { |
| throw new RuntimeException( |
| "Unable to save state of activity " |
| + r.intent.getComponent().toShortString() |
| + ": " + e.toString(), e); |
| } |
| } |
| } |
| |
| callActivityOnStop(r, saveState, reason); |
| } |
| |
| /** |
| * Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates |
| * the client record's state. |
| * All calls to stop an activity must be done through this method to make sure that |
| * {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call. |
| */ |
| private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) { |
| // Before P onSaveInstanceState was called before onStop, starting with P it's |
| // called after. Before Honeycomb state was always saved before onPause. |
| final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null |
| && !r.isPreHoneycomb(); |
| final boolean isPreP = r.isPreP(); |
| if (shouldSaveState && isPreP) { |
| callActivityOnSaveInstanceState(r); |
| } |
| |
| try { |
| r.activity.performStop(r.mPreserveWindow, reason); |
| } catch (SuperNotCalledException e) { |
| throw e; |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(r.activity, e)) { |
| throw new RuntimeException( |
| "Unable to stop activity " |
| + r.intent.getComponent().toShortString() |
| + ": " + e.toString(), e); |
| } |
| } |
| r.setState(ON_STOP); |
| |
| if (shouldSaveState && !isPreP) { |
| callActivityOnSaveInstanceState(r); |
| } |
| } |
| |
| private void updateVisibility(ActivityClientRecord r, boolean show) { |
| View v = r.activity.mDecor; |
| if (v != null) { |
| if (show) { |
| if (!r.activity.mVisibleFromServer) { |
| r.activity.mVisibleFromServer = true; |
| mNumVisibleActivities++; |
| if (r.activity.mVisibleFromClient) { |
| r.activity.makeVisible(); |
| } |
| } |
| } else { |
| if (r.activity.mVisibleFromServer) { |
| r.activity.mVisibleFromServer = false; |
| mNumVisibleActivities--; |
| v.setVisibility(View.INVISIBLE); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void handleStopActivity(ActivityClientRecord r, |
| PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) { |
| |
| final StopInfo stopInfo = new StopInfo(); |
| performStopActivityInner(r, stopInfo, true /* saveState */, finalStateRequest, |
| reason); |
| |
| if (localLOGV) Slog.v( |
| TAG, "Finishing stop of " + r + ": win=" + r.window); |
| |
| updateVisibility(r, false); |
| |
| // Make sure any pending writes are now committed. |
| if (!r.isPreHoneycomb()) { |
| QueuedWork.waitToFinish(); |
| } |
| |
| stopInfo.setActivity(r); |
| stopInfo.setState(r.state); |
| stopInfo.setPersistentState(r.persistentState); |
| pendingActions.setStopInfo(stopInfo); |
| mSomeActivitiesChanged = true; |
| } |
| |
| /** |
| * Schedule the call to tell the activity manager we have stopped. We don't do this |
| * immediately, because we want to have a chance for any other pending work (in particular |
| * memory trim requests) to complete before you tell the activity manager to proceed and allow |
| * us to go fully into the background. |
| */ |
| @Override |
| public void reportStop(PendingTransactionActions pendingActions) { |
| mH.post(pendingActions.getStopInfo()); |
| } |
| |
| @Override |
| public void performRestartActivity(ActivityClientRecord r, boolean start) { |
| if (r.stopped) { |
| r.activity.performRestart(start); |
| if (start) { |
| r.setState(ON_START); |
| } |
| } |
| } |
| |
| @Override |
| public void reportRefresh(ActivityClientRecord r) { |
| ActivityClient.getInstance().activityRefreshed(r.token); |
| } |
| |
| private void handleSetCoreSettings(Bundle coreSettings) { |
| synchronized (mCoreSettingsLock) { |
| mCoreSettings = coreSettings; |
| } |
| onCoreSettingsChange(); |
| } |
| |
| private void onCoreSettingsChange() { |
| if (updateDebugViewAttributeState()) { |
| // request all activities to relaunch for the changes to take place |
| relaunchAllActivities(true /* preserveWindows */, "onCoreSettingsChange"); |
| } |
| } |
| |
| private boolean updateDebugViewAttributeState() { |
| boolean previousState = View.sDebugViewAttributes; |
| |
| // mCoreSettings is only updated from the main thread, while this function is only called |
| // from main thread as well, so no need to lock here. |
| View.sDebugViewAttributesApplicationPackage = mCoreSettings.getString( |
| Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, ""); |
| String currentPackage = (mBoundApplication != null && mBoundApplication.appInfo != null) |
| ? mBoundApplication.appInfo.packageName : "<unknown-app>"; |
| View.sDebugViewAttributes = |
| mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0 |
| || View.sDebugViewAttributesApplicationPackage.equals(currentPackage); |
| return previousState != View.sDebugViewAttributes; |
| } |
| |
| private void relaunchAllActivities(boolean preserveWindows, String reason) { |
| Log.i(TAG, "Relaunch all activities: " + reason); |
| for (int i = mActivities.size() - 1; i >= 0; i--) { |
| scheduleRelaunchActivityIfPossible(mActivities.valueAt(i), preserveWindows); |
| } |
| } |
| |
| private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) { |
| mCompatibilityInfo = data.info; |
| LoadedApk apk = peekPackageInfo(data.pkg, false); |
| if (apk != null) { |
| apk.setCompatibilityInfo(data.info); |
| } |
| apk = peekPackageInfo(data.pkg, true); |
| if (apk != null) { |
| apk.setCompatibilityInfo(data.info); |
| } |
| mConfigurationController.handleConfigurationChanged(data.info); |
| } |
| |
| private void deliverResults(ActivityClientRecord r, List<ResultInfo> results, String reason) { |
| final int N = results.size(); |
| for (int i=0; i<N; i++) { |
| ResultInfo ri = results.get(i); |
| try { |
| if (ri.mData != null) { |
| ri.mData.setExtrasClassLoader(r.activity.getClassLoader()); |
| ri.mData.prepareToEnterProcess(isProtectedComponent(r.activityInfo), |
| r.activity.getAttributionSource()); |
| } |
| if (DEBUG_RESULTS) Slog.v(TAG, |
| "Delivering result to activity " + r + " : " + ri); |
| if (android.security.Flags.contentUriPermissionApis()) { |
| ComponentCaller caller = new ComponentCaller(r.token, ri.mCallerToken); |
| r.activity.dispatchActivityResult(ri.mResultWho, |
| ri.mRequestCode, ri.mResultCode, ri.mData, caller, reason); |
| } else { |
| r.activity.dispatchActivityResult(ri.mResultWho, |
| ri.mRequestCode, ri.mResultCode, ri.mData, reason); |
| } |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(r.activity, e)) { |
| throw new RuntimeException( |
| "Failure delivering result " + ri + " to activity " |
| + r.intent.getComponent().toShortString() |
| + ": " + e.toString(), e); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void handleSendResult(ActivityClientRecord r, List<ResultInfo> results, String reason) { |
| if (DEBUG_RESULTS) Slog.v(TAG, "Handling send result to " + r); |
| final boolean resumed = !r.paused; |
| if (!r.activity.mFinished && r.activity.mDecor != null |
| && r.hideForNow && resumed) { |
| // We had hidden the activity because it started another |
| // one... we have gotten a result back and we are not |
| // paused, so make sure our window is visible. |
| updateVisibility(r, true); |
| } |
| if (resumed) { |
| try { |
| // Now we are idle. |
| r.activity.mCalled = false; |
| mInstrumentation.callActivityOnPause(r.activity); |
| if (!r.activity.mCalled) { |
| throw new SuperNotCalledException( |
| "Activity " + r.intent.getComponent().toShortString() |
| + " did not call through to super.onPause()"); |
| } |
| } catch (SuperNotCalledException e) { |
| throw e; |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(r.activity, e)) { |
| throw new RuntimeException( |
| "Unable to pause activity " |
| + r.intent.getComponent().toShortString() |
| + ": " + e.toString(), e); |
| } |
| } |
| } |
| checkAndBlockForNetworkAccess(); |
| deliverResults(r, results, reason); |
| if (resumed) { |
| r.activity.performResume(false, reason); |
| } |
| } |
| |
| /** Core implementation of activity destroy call. */ |
| void performDestroyActivity(ActivityClientRecord r, boolean finishing, |
| boolean getNonConfigInstance, String reason) { |
| Class<? extends Activity> activityClass; |
| if (localLOGV) Slog.v(TAG, "Performing finish of " + r); |
| activityClass = r.activity.getClass(); |
| if (finishing) { |
| r.activity.mFinished = true; |
| } |
| |
| performPauseActivityIfNeeded(r, "destroy"); |
| |
| if (!r.stopped) { |
| callActivityOnStop(r, false /* saveState */, "destroy"); |
| } |
| if (getNonConfigInstance) { |
| try { |
| r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(r.activity, e)) { |
| throw new RuntimeException("Unable to retain activity " |
| + r.intent.getComponent().toShortString() + ": " + e.toString(), e); |
| } |
| } |
| } |
| try { |
| r.activity.mCalled = false; |
| mInstrumentation.callActivityOnDestroy(r.activity); |
| if (!r.activity.mCalled) { |
| throw new SuperNotCalledException("Activity " + safeToComponentShortString(r.intent) |
| + " did not call through to super.onDestroy()"); |
| } |
| if (r.window != null) { |
| r.window.closeAllPanels(); |
| } |
| } catch (SuperNotCalledException e) { |
| throw e; |
| } catch (Exception e) { |
| if (!mInstrumentation.onException(r.activity, e)) { |
| throw new RuntimeException("Unable to destroy activity " |
| + safeToComponentShortString(r.intent) + ": " + e.toString(), e); |
| } |
| } |
| r.setState(ON_DESTROY); |
| schedulePurgeIdler(); |
| synchronized (this) { |
| if (mSplashScreenGlobal != null) { |
| mSplashScreenGlobal.tokenDestroyed(r.token); |
| } |
| } |
| // updatePendingActivityConfiguration() reads from mActivities to update |
| // ActivityClientRecord which runs in a different thread. Protect modifications to |
| // mActivities to avoid race. |
| synchronized (mResourcesManager) { |
| mActivities.remove(r.token); |
| } |
| StrictMode.decrementExpectedActivityCount(activityClass); |
| } |
| |
| private static String safeToComponentShortString(Intent intent) { |
| ComponentName component = intent.getComponent(); |
| return component == null ? "[Unknown]" : component.toShortString(); |
| } |
| |
| @Override |
| public Map<IBinder, DestroyActivityItem> getActivitiesToBeDestroyed() { |
| return mActivitiesToBeDestroyed; |
| } |
| |
| @Override |
| public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, |
| boolean getNonConfigInstance, String reason) { |
| performDestroyActivity(r, finishing, getNonConfigInstance, reason); |
| cleanUpPendingRemoveWindows(r, finishing); |
| WindowManager wm = r.activity.getWindowManager(); |
| View v = r.activity.mDecor; |
| if (v != null) { |
| if (r.activity.mVisibleFromServer) { |
| mNumVisibleActivities--; |
| } |
| IBinder wtoken = v.getWindowToken(); |
| if (r.activity.mWindowAdded) { |
| if (r.mPreserveWindow) { |
| // Hold off on removing this until the new activity's window is being added. |
| r.mPendingRemoveWindow = r.window; |
| r.mPendingRemoveWindowManager = wm; |
| // We can only keep the part of the view hierarchy that we control, |
| // everything else must be removed, because it might not be able to |
| // behave properly when activity is relaunching. |
| r.window.clearContentView(); |
| } else { |
| final ViewRootImpl viewRoot = v.getViewRootImpl(); |
| if (viewRoot != null) { |
| // Clear callbacks to avoid the destroyed activity from receiving |
| // configuration or camera compat changes that are no longer effective. |
| viewRoot.setActivityConfigCallback(null); |
| } |
| wm.removeViewImmediate(v); |
| } |
| } |
| if (wtoken != null && r.mPendingRemoveWindow == null) { |
| WindowManagerGlobal.getInstance().closeAll(wtoken, |
| r.activity.getClass().getName(), "Activity"); |
| } else if (r.mPendingRemoveWindow != null) { |
| // We're preserving only one window, others should be closed so app views |
| // will be detached before the final tear down. It should be done now because |
| // some components (e.g. WebView) rely on detach callbacks to perform receiver |
| // unregister and other cleanup. |
| WindowManagerGlobal.getInstance().closeAllExceptView(r.token, v, |
| r.activity.getClass().getName(), "Activity"); |
| } |
| r.activity.mDecor = null; |
| } |
| if (r.mPendingRemoveWindow == null) { |
| // If we are delaying the removal of the activity window, then |
| // we can't clean up all windows here. Note that we can't do |
| // so later either, which means any windows that aren't closed |
| // by the app will leak. Well we try to warning them a lot |
| // about leaking windows, because that is a bug, so if they are |
| // using this recreate facility then they get to live with leaks. |
| WindowManagerGlobal.getInstance().closeAll(r.token, |
| r.activity.getClass().getName(), "Activity"); |
| } |
| |
| // Mocked out contexts won't be participating in the normal |
| // process lifecycle, but if we're running with a proper |
| // ApplicationContext we need to have it tear down things |
| // cleanly. |
| Context c = r.activity.getBaseContext(); |
| if (c instanceof ContextImpl) { |
| ((ContextImpl) c).scheduleFinalCleanup(r.activity.getClass().getName(), "Activity"); |
| } |
| if (finishing) { |
| ActivityClient.getInstance().activityDestroyed(r.token); |
| mNewActivities.remove(r); |
| } |
| mSomeActivitiesChanged = true; |
| } |
| |
| @Override |
| public ActivityClientRecord prepareRelaunchActivity(@NonNull IBinder token, |
| @Nullable List<ResultInfo> pendingResults, |
| @Nullable List<ReferrerIntent> pendingNewIntents, int configChanges, |
| @NonNull MergedConfiguration config, boolean preserveWindow, |
| @NonNull ActivityWindowInfo activityWindowInfo) { |
| ActivityClientRecord target = null; |
| boolean scheduleRelaunch = false; |
| |
| synchronized (mResourcesManager) { |
| for (int i=0; i<mRelaunchingActivities.size(); i++) { |
| ActivityClientRecord r = mRelaunchingActivities.get(i); |
| if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + this + ", trying: " + r); |
| if (r.token == token) { |
| target = r; |
| if (pendingResults != null) { |
| if (r.pendingResults != null) { |
| r.pendingResults.addAll(pendingResults); |
| } else { |
| r.pendingResults = pendingResults; |
| } |
| } |
| if (pendingNewIntents != null) { |
| if (r.pendingIntents != null) { |
| r.pendingIntents.addAll(pendingNewIntents); |
| } else { |
| r.pendingIntents = pendingNewIntents; |
| } |
| } |
| break; |
| } |
| } |
| |
| if (target == null) { |
| if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null"); |
| target = new ActivityClientRecord(); |
| target.token = token; |
| target.pendingResults = pendingResults; |
| target.pendingIntents = pendingNewIntents; |
| target.mPreserveWindow = preserveWindow; |
| mRelaunchingActivities.add(target); |
| scheduleRelaunch = true; |
| } |
| target.createdConfig = config.getGlobalConfiguration(); |
| target.overrideConfig = config.getOverrideConfiguration(); |
| target.pendingConfigChanges |= configChanges; |
| target.mActivityWindowInfo.set(activityWindowInfo); |
| } |
| |
| return scheduleRelaunch ? target : null; |
| } |
| |
| @Override |
| public void handleRelaunchActivity(@NonNull ActivityClientRecord tmp, |
| @NonNull PendingTransactionActions pendingActions) { |
| // If we are getting ready to gc after going to the background, well |
| // we are back active so skip it. |
| unscheduleGcIdler(); |
| mSomeActivitiesChanged = true; |
| |
| int configChanges = 0; |
| |
| // First: make sure we have the most recent configuration and most |
| // recent version of the activity, or skip it if some previous call |
| // had taken a more recent version. |
| synchronized (mResourcesManager) { |
| int N = mRelaunchingActivities.size(); |
| IBinder token = tmp.token; |
| tmp = null; |
| for (int i=0; i<N; i++) { |
| ActivityClientRecord r = mRelaunchingActivities.get(i); |
| if (r.token == token) { |
| tmp = r; |
| configChanges |= tmp.pendingConfigChanges; |
| mRelaunchingActivities.remove(i); |
| i--; |
| N--; |
| } |
| } |
| |
| if (tmp == null) { |
| if (DEBUG_CONFIGURATION) Slog.v(TAG, "Abort, activity not relaunching!"); |
| return; |
| } |
| |
| if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity " |
| + tmp.token + " with configChanges=0x" |
| + Integer.toHexString(configChanges)); |
| } |
| |
| Configuration changedConfig = mConfigurationController.getPendingConfiguration( |
| true /* clearPending */); |
| mPendingConfiguration = null; |
| |
| if (tmp.createdConfig != null) { |
| // If the activity manager is passing us its current config, |
| // assume that is really what we want regardless of what we |
| // may have pending. |
| final Configuration config = mConfigurationController.getConfiguration(); |
| if (config == null |
| || (tmp.createdConfig.isOtherSeqNewer(config) |
| && config.diff(tmp.createdConfig) != 0)) { |
| if (changedConfig == null |
| || tmp.createdConfig.isOtherSeqNewer(changedConfig)) { |
| changedConfig = tmp.createdConfig; |
| } |
| } |
| } |
| |
| if (DEBUG_CONFIGURATION) Slog.v(TAG, "Relaunching activity " |
| + tmp.token + ": changedConfig=" + changedConfig); |
| |
| // If there was a pending configuration change, execute it first. |
| if (changedConfig != null) { |
| mConfigurationController.updateDefaultDensity(changedConfig.densityDpi); |
| mConfigurationController.handleConfigurationChanged(changedConfig, null); |
| |
| // These are only done to maintain @UnsupportedAppUsage and should be removed someday. |
| mCurDefaultDisplayDpi = mConfigurationController.getCurDefaultDisplayDpi(); |
| mConfiguration = mConfigurationController.getConfiguration(); |
| } |
| |
| ActivityClientRecord r = mActivities.get(tmp.token); |
| if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r); |
| if (r == null) { |
| return; |
| } |
| |
| r.activity.mConfigChangeFlags |= configChanges; |
| r.mPreserveWindow = tmp.mPreserveWindow; |
| |
| r.activity.mChangingConfigurations = true; |
| |
| handleRelaunchActivityInner(r, tmp.pendingResults, tmp.pendingIntents, |
| pendingActions, tmp.startsNotResumed, tmp.overrideConfig, tmp.mActivityWindowInfo, |
| "handleRelaunchActivity"); |
| } |
| |
| void scheduleRelaunchActivity(IBinder token) { |
| final ActivityClientRecord r = mActivities.get(token); |
| if (r != null) { |
| Log.i(TAG, "Schedule relaunch activity: " + r.activityInfo.name); |
| scheduleRelaunchActivityIfPossible(r, !r.stopped /* preserveWindow */); |
| } |
| } |
| |
| /** |
| * Post a message to relaunch the activity. We do this instead of launching it immediately, |
| * because this will destroy the activity from which it was called and interfere with the |
| * lifecycle changes it was going through before. We need to make sure that we have finished |
| * handling current transaction item before relaunching the activity. |
| */ |
| private void scheduleRelaunchActivityIfPossible(@NonNull ActivityClientRecord r, |
| boolean preserveWindow) { |
| if ((r.activity != null && r.activity.mFinished) || r.token instanceof Binder) { |
| // Do not schedule relaunch if the activity is finishing or is a local object (e.g. |
| // created by ActivtiyGroup that server side doesn't recognize it). |
| return; |
| } |
| if (preserveWindow && r.window != null) { |
| r.mPreserveWindow = true; |
| } |
| mH.removeMessages(H.RELAUNCH_ACTIVITY, r.token); |
| sendMessage(H.RELAUNCH_ACTIVITY, r.token); |
| } |
| |
| /** Performs the activity relaunch locally vs. requesting from system-server. */ |
| public void handleRelaunchActivityLocally(IBinder token) { |
| final ActivityClientRecord r = mActivities.get(token); |
| if (r == null) { |
| Log.w(TAG, "Activity to relaunch no longer exists"); |
| return; |
| } |
| |
| final int prevState = r.getLifecycleState(); |
| |
| if (prevState < ON_START || prevState > ON_STOP) { |
| Log.w(TAG, "Activity state must be in [ON_START..ON_STOP] in order to be relaunched," |
| + "current state is " + prevState); |
| return; |
| } |
| |
| ActivityClient.getInstance().activityLocalRelaunch(r.token); |
| // Initialize a relaunch request. |
| final MergedConfiguration mergedConfiguration = new MergedConfiguration( |
| r.createdConfig != null |
| ? r.createdConfig : mConfigurationController.getConfiguration(), |
| r.overrideConfig); |
| final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain( |
| r.token, null /* pendingResults */, null /* pendingIntents */, |
| 0 /* configChanges */, mergedConfiguration, r.mPreserveWindow, |
| r.getActivityWindowInfo()); |
| // Make sure to match the existing lifecycle state in the end of the transaction. |
| final ActivityLifecycleItem lifecycleRequest = |
| TransactionExecutorHelper.getLifecycleRequestForCurrentState(r); |
| // Schedule the transaction. |
| final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); |
| transaction.addTransactionItem(activityRelaunchItem); |
| transaction.addTransactionItem(lifecycleRequest); |
| executeTransaction(transaction); |
| } |
| |
| private void handleRelaunchActivityInner(@NonNull ActivityClientRecord r, |
| @Nullable List<ResultInfo> pendingResults, |
| @Nullable List<ReferrerIntent> pendingIntents, |
| @NonNull PendingTransactionActions pendingActions, boolean startsNotResumed, |
| @NonNull Configuration overrideConfig, @NonNull ActivityWindowInfo activityWindowInfo, |
| @NonNull String reason) { |
| // Preserve last used intent, it may be set from Activity#setIntent(). |
| final Intent customIntent = r.activity.mIntent; |
| // Need to ensure state is saved. |
| if (!r.paused) { |
| performPauseActivity(r, false, reason, null /* pendingActions */); |
| } |
| if (!r.stopped) { |
| callActivityOnStop(r, true /* saveState */, reason); |
| } |
| |
| handleDestroyActivity(r, false /* finishing */, true /* getNonConfigInstance */, reason); |
| |
| r.activity = null; |
| r.window = null; |
| r.hideForNow = false; |
| // Merge any pending results and pending intents; don't just replace them |
| if (pendingResults != null) { |
| if (r.pendingResults == null) { |
| r.pendingResults = pendingResults; |
| } else { |
| r.pendingResults.addAll(pendingResults); |
| } |
| } |
| if (pendingIntents != null) { |
| if (r.pendingIntents == null) { |
| r.pendingIntents = pendingIntents; |
| } else { |
| r.pendingIntents.addAll(pendingIntents); |
| } |
| } |
| r.startsNotResumed = startsNotResumed; |
| r.overrideConfig = overrideConfig; |
| r.mActivityWindowInfo.set(activityWindowInfo); |
| |
| handleLaunchActivity(r, pendingActions, mLastReportedDeviceId, customIntent); |
| } |
| |
| @Override |
| public void reportRelaunch(ActivityClientRecord r) { |
| ActivityClient.getInstance().activityRelaunched(r.token); |
| } |
| |
| private void callActivityOnSaveInstanceState(ActivityClientRecord r) { |
| r.state = new Bundle(); |
| r.state.setAllowFds(false); |
| if (r.isPersistable()) { |
| r.persistentState = new PersistableBundle(); |
| mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state, |
| r.persistentState); |
| } else { |
| mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state); |
| } |
| } |
| |
| @Override |
| public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) { |
| ArrayList<ComponentCallbacks2> callbacks |
| = new ArrayList<ComponentCallbacks2>(); |
| |
| synchronized (mResourcesManager) { |
| final int NAPP = mAllApplications.size(); |
| for (int i=0; i<NAPP; i++) { |
| callbacks.add(mAllApplications.get(i)); |
| } |
| if (includeUiContexts) { |
| for (int i = mActivities.size() - 1; i >= 0; i--) { |
| final Activity a = mActivities.valueAt(i).activity; |
| if (a != null && !a.mFinished) { |
| callbacks.add(a); |
| } |
| } |
| } |
| final int NSVC = mServices.size(); |
| for (int i=0; i<NSVC; i++) { |
| final Service service = mServices.valueAt(i); |
| // If {@code includeUiContext} is set to false, WindowProviderService should not be |
| // collected because WindowProviderService is a UI Context. |
| if (includeUiContexts || !(service instanceof WindowProviderService)) { |
| callbacks.add(service); |
| } |
| } |
| } |
| synchronized (mProviderMap) { |
| final int NPRV = mLocalProviders.size(); |
| for (int i=0; i<NPRV; i++) { |
| callbacks.add(mLocalProviders.valueAt(i).mLocalProvider); |
| } |
| } |
| |
| return callbacks; |
| } |
| |
| /** |
| * Updates the configuration for an Activity. The ActivityClientRecord's |
| * {@link ActivityClientRecord#overrideConfig} is used to compute the final Configuration for |
| * that Activity. {@link ActivityClientRecord#tmpConfig} is used as a temporary for delivering |
| * the updated Configuration. |
| * @param r ActivityClientRecord representing the Activity. |
| * @param newBaseConfig The new configuration to use. This may be augmented with |
| * {@link ActivityClientRecord#overrideConfig}. |
| * @param displayId The id of the display where the Activity currently resides. |
| * @return {@link Configuration} instance sent to client, null if not sent. |
| */ |
| private Configuration performConfigurationChangedForActivity(ActivityClientRecord r, |
| Configuration newBaseConfig, int displayId, boolean alwaysReportChange) { |
| r.tmpConfig.setTo(newBaseConfig); |
| if (r.overrideConfig != null) { |
| r.tmpConfig.updateFrom(r.overrideConfig); |
| } |
| final Configuration reportedConfig = performActivityConfigurationChanged(r, |
| r.tmpConfig, r.overrideConfig, displayId, alwaysReportChange); |
| freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig)); |
| return reportedConfig; |
| } |
| |
| /** |
| * Decides whether to update an Activity's configuration and whether to inform it. |
| * @param r The activity client record to notify of configuration change. |
| * @param newConfig The new configuration. |
| * @param amOverrideConfig The override config that differentiates the Activity's configuration |
| * from the base global configuration. This is supplied by |
| * ActivityManager. |
| * @param displayId Id of the display where activity currently resides. |
| * @return Configuration sent to client, null if no changes and not moved to different display. |
| */ |
| private Configuration performActivityConfigurationChanged(ActivityClientRecord r, |
| Configuration newConfig, Configuration amOverrideConfig, int displayId, |
| boolean alwaysReportChange) { |
| final Activity activity = r.activity; |
| final IBinder activityToken = activity.getActivityToken(); |
| |
| // WindowConfiguration differences aren't considered as public, check it separately. |
| // multi-window / pip mode changes, if any, should be sent before the configuration |
| // change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition |
| handleWindowingModeChangeIfNeeded(r, newConfig); |
| |
| final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(), |
| displayId); |
| final Configuration currentResConfig = activity.getResources().getConfiguration(); |
| final int diff = currentResConfig.diffPublicOnly(newConfig); |
| final boolean hasPublicResConfigChange = diff != 0; |
| // TODO(b/173090263): Use diff instead after the improvement of AssetManager and |
| // ResourcesImpl constructions. |
| final boolean shouldUpdateResources = hasPublicResConfigChange |
| || shouldUpdateResources(activityToken, currentResConfig, newConfig, |
| amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange); |
| |
| // TODO(b/274944389): remove once a longer-term solution is implemented. |
| boolean skipActivityRelaunchWhenDocking = activity.getResources().getBoolean( |
| R.bool.config_skipActivityRelaunchWhenDocking); |
| int handledConfigChanges = activity.mActivityInfo.getRealConfigChanged(); |
| if (skipActivityRelaunchWhenDocking && onlyDeskInUiModeChanged(activity.mCurrentConfig, |
| newConfig)) { |
| // If we're not relaunching this activity when docking, we should send the configuration |
| // changed event. Pretend as if the activity is handling uiMode config changes in its |
| // manifest so that we'll report any dock changes. |
| handledConfigChanges |= ActivityInfo.CONFIG_UI_MODE; |
| } |
| |
| final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig, |
| r.mSizeConfigurations, handledConfigChanges, alwaysReportChange); |
| // Nothing significant, don't proceed with updating and reporting. |
| if (!shouldUpdateResources && !shouldReportChange) { |
| return null; |
| } |
| |
| // Propagate the configuration change to ResourcesManager and Activity. |
| |
| // ContextThemeWrappers may override the configuration for that context. We must check and |
| // apply any overrides defined. |
| Configuration contextThemeWrapperOverrideConfig = activity.getOverrideConfiguration(); |
| |
| // We only update an Activity's configuration if this is not a global configuration change. |
| // This must also be done before the callback, or else we violate the contract that the new |
| // resources are available in ComponentCallbacks2#onConfigurationChanged(Configuration). |
| // Also apply the ContextThemeWrapper override if necessary. |
| // NOTE: Make sure the configurations are not modified, as they are treated as immutable in |
| // many places. |
| final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull( |
| amOverrideConfig, contextThemeWrapperOverrideConfig); |
| mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId); |
| |
| // Apply the ContextThemeWrapper override if necessary. |
| // NOTE: Make sure the configurations are not modified, as they are treated as immutable |
| // in many places. |
| final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig, |
| contextThemeWrapperOverrideConfig); |
| |
| if (movedToDifferentDisplay) { |
| activity.dispatchMovedToDisplay(displayId, configToReport); |
| } |
| |
| activity.mConfigChangeFlags = 0; |
| if (shouldReportChange) { |
| activity.mCalled = false; |
| activity.mCurrentConfig = new Configuration(newConfig); |
| activity.onConfigurationChanged(configToReport); |
| if (!activity.mCalled) { |
| throw new SuperNotCalledException("Activity " + activity.getLocalClassName() + |
| " did not call through to super.onConfigurationChanged()"); |
| } |
| } |
| mConfigurationChangedListenerController |
| .dispatchOnConfigurationChanged(activity.getActivityToken()); |
| |
| return configToReport; |
| } |
| |
| /** |
| * Returns true if the uiMode configuration changed, and desk mode |
| * ({@link android.content.res.Configuration#UI_MODE_TYPE_DESK}) was the only change to uiMode. |
| */ |
| private boolean onlyDeskInUiModeChanged(Configuration oldConfig, Configuration newConfig) { |
| boolean deskModeChanged = isInDeskUiMode(oldConfig) != isInDeskUiMode(newConfig); |
| |
| // UI mode contains fields other than the UI mode type, so determine if any other fields |
| // changed. |
| boolean uiModeOtherFieldsChanged = |
| (oldConfig.uiMode & ~UI_MODE_TYPE_MASK) != (newConfig.uiMode & ~UI_MODE_TYPE_MASK); |
| |
| return deskModeChanged && !uiModeOtherFieldsChanged; |
| } |
| |
| private static boolean isInDeskUiMode(Configuration config) { |
| return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_DESK; |
| } |
| |
| /** |
| * Returns {@code true} if {@link Activity#onConfigurationChanged(Configuration)} should be |
| * dispatched. |
| * |
| * @param currentConfig The current configuration cached in {@link Activity#mCurrentConfig}. |
| * It is {@code null} before the first config update from the server side. |
| * @param newConfig The updated {@link Configuration} |
| * @param sizeBuckets The Activity's {@link SizeConfigurationBuckets} if not {@code null} |
| * @param handledConfigChanges Bit mask of configuration changes that the activity can handle |
| * @return {@code true} if the config change should be reported to the Activity |
| */ |
| @VisibleForTesting |
| public static boolean shouldReportChange(@Nullable Configuration currentConfig, |
| @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets sizeBuckets, |
| int handledConfigChanges, boolean alwaysReportChange) { |
| final int publicDiff = currentConfig.diffPublicOnly(newConfig); |
| // Don't report the change if there's no public diff between current and new config. |
| if (publicDiff == 0) { |
| return false; |
| } |
| |
| // Report the change regardless if the changes across size-config-buckets. |
| if (alwaysReportChange) { |
| return true; |
| } |
| |
| final int diffWithBucket = SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig, |
| newConfig, sizeBuckets); |
| // Compare to the diff which filter the change without crossing size buckets with |
| // {@code handledConfigChanges}. The small changes should not block Activity to receive |
| // its handled config updates. Also, if Activity handles all small changes, we should |
| // dispatch the updated config to it. |
| final int diff = diffWithBucket != 0 ? diffWithBucket : publicDiff; |
| // If this activity doesn't handle any of the config changes, then don't bother |
| // calling onConfigurationChanged. Otherwise, report to the activity for the |
| // changes. |
| return (~handledConfigChanges & diff) == 0; |
| } |
| |
| public final void applyConfigurationToResources(Configuration config) { |
| synchronized (mResourcesManager) { |
| mResourcesManager.applyConfigurationToResources(config, null); |
| } |
| } |
| |
| private void updateDeviceIdForNonUIContexts(int deviceId) { |
| // Invalid device id is treated as a no-op. |
| if (deviceId == Context.DEVICE_ID_INVALID) { |
| return; |
| } |
| if (deviceId == mLastReportedDeviceId) { |
| return; |
| } |
| mLastReportedDeviceId = deviceId; |
| ArrayList<Context> nonUIContexts = new ArrayList<>(); |
| // Update Application and Service contexts with implicit device association. |
| // UI Contexts are able to derived their device Id association from the display. |
| synchronized (mResourcesManager) { |
| final int numApps = mAllApplications.size(); |
| for (int i = 0; i < numApps; i++) { |
| nonUIContexts.add(mAllApplications.get(i)); |
| } |
| final int numServices = mServices.size(); |
| for (int i = 0; i < numServices; i++) { |
| final Service service = mServices.valueAt(i); |
| // WindowProviderService is a UI Context. |
| if (!service.isUiContext()) { |
| nonUIContexts.add(service); |
| } |
| } |
| } |
| for (Context context : nonUIContexts) { |
| try { |
| context.updateDeviceId(deviceId); |
| } catch (IllegalArgumentException e) { |
| // It can happen that the system already closed/removed a virtual device |
| // and the passed deviceId is no longer valid. |
| // TODO(b/263355088): check for validity of deviceId before updating |
| // instead of catching this exception once VDM add an API to validate ids. |
| } |
| } |
| } |
| |
| @Override |
| public void handleConfigurationChanged(Configuration config, int deviceId) { |
| mConfigurationController.handleConfigurationChanged(config); |
| updateDeviceIdForNonUIContexts(deviceId); |
| |
| // These are only done to maintain @UnsupportedAppUsage and should be removed someday. |
| mCurDefaultDisplayDpi = mConfigurationController.getCurDefaultDisplayDpi(); |
| mConfiguration = mConfigurationController.getConfiguration(); |
| mPendingConfiguration = mConfigurationController.getPendingConfiguration( |
| false /* clearPending */); |
| } |
| |
| @Override |
| public void handleWindowContextInfoChanged(@NonNull IBinder clientToken, |
| @NonNull WindowContextInfo info) { |
| WindowTokenClientController.getInstance().onWindowContextInfoChanged(clientToken, info); |
| } |
| |
| @Override |
| public void handleWindowContextWindowRemoval(@NonNull IBinder clientToken) { |
| WindowTokenClientController.getInstance().onWindowContextWindowRemoved(clientToken); |
| } |
| |
| /** |
| * Sends windowing mode change callbacks to {@link Activity} if applicable. |
| * |
| * See also {@link Activity#onMultiWindowModeChanged(boolean, Configuration)} and |
| * {@link Activity#onPictureInPictureModeChanged(boolean, Configuration)} |
| */ |
| private void handleWindowingModeChangeIfNeeded(ActivityClientRecord r, |
| Configuration newConfiguration) { |
| final Activity activity = r.activity; |
| final int newWindowingMode = newConfiguration.windowConfiguration.getWindowingMode(); |
| final int oldWindowingMode = r.mLastReportedWindowingMode; |
| if (oldWindowingMode == newWindowingMode) return; |
| // PiP callback is sent before the MW one. |
| if (newWindowingMode == WINDOWING_MODE_PINNED) { |
| activity.dispatchPictureInPictureModeChanged(true, newConfiguration); |
| } else if (oldWindowingMode == WINDOWING_MODE_PINNED) { |
| activity.dispatchPictureInPictureModeChanged(false, newConfiguration); |
| } |
| final boolean wasInMultiWindowMode = WindowConfiguration.inMultiWindowMode( |
| oldWindowingMode); |
| final boolean nowInMultiWindowMode = WindowConfiguration.inMultiWindowMode( |
| newWindowingMode); |
| if (wasInMultiWindowMode != nowInMultiWindowMode) { |
| activity.dispatchMultiWindowModeChanged(nowInMultiWindowMode, newConfiguration); |
| } |
| r.mLastReportedWindowingMode = newWindowingMode; |
| } |
| |
| private void applyPendingApplicationInfoChanges(String packageName) { |
| final ApplicationInfo ai; |
| synchronized (mResourcesManager) { |
| ai = mPendingAppInfoUpdates.remove(packageName); |
| } |
| if (ai == null) { |
| return; |
| } |
| handleApplicationInfoChanged(ai); |
| } |
| |
| /** |
| * Updates the application info. |
| * |
| * This only works in the system process. Must be called on the main thread. |
| */ |
| public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) { |
| Preconditions.checkState(mSystemThread, "Must only be called in the system process"); |
| handleApplicationInfoChanged(ai); |
| } |
| |
| @VisibleForTesting(visibility = PACKAGE) |
| public void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) { |
| // Updates triggered by package installation go through a package update |
| // receiver. Here we try to capture ApplicationInfo changes that are |
| // caused by other sources, such as overlays. That means we want to be as conservative |
| // about code changes as possible. Take the diff of the old ApplicationInfo and the new |
| // to see if anything needs to change. |
| LoadedApk apk; |
| LoadedApk resApk; |
| // Update all affected loaded packages with new package information |
| synchronized (mResourcesManager) { |
| WeakReference<LoadedApk> ref = mPackages.get(ai.packageName); |
| apk = ref != null ? ref.get() : null; |
| ref = mResourcePackages.get(ai.packageName); |
| resApk = ref != null ? ref.get() : null; |
| for (ActivityClientRecord ar : mActivities.values()) { |
| if (ar.activityInfo.applicationInfo.packageName.equals(ai.packageName)) { |
| ar.activityInfo.applicationInfo = ai; |
| if (apk != null || resApk != null) { |
| ar.packageInfo = apk != null ? apk : resApk; |
| } else { |
| apk = ar.packageInfo; |
| } |
| } |
| } |
| } |
| |
| if (apk != null) { |
| final ArrayList<String> oldPaths = new ArrayList<>(); |
| LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths); |
| apk.updateApplicationInfo(ai, oldPaths); |
| } |
| if (resApk != null) { |
| final ArrayList<String> oldPaths = new ArrayList<>(); |
| LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths); |
| resApk.updateApplicationInfo(ai, oldPaths); |
| } |
| |
| ResourcesImpl beforeImpl = getApplication().getResources().getImpl(); |
| |
| synchronized (mResourcesManager) { |
| // Update all affected Resources objects to use new ResourcesImpl |
| mResourcesManager.applyAllPendingAppInfoUpdates(); |
| } |
| |
| ResourcesImpl afterImpl = getApplication().getResources().getImpl(); |
| |
| if ((beforeImpl != afterImpl) && !Arrays.equals(beforeImpl.getAssets().getApkAssets(), |
| afterImpl.getAssets().getApkAssets())) { |
| List<String> beforeAssets = Arrays.asList(beforeImpl.getAssets().getApkPaths()); |
| List<String> afterAssets = Arrays.asList(afterImpl.getAssets().getApkPaths()); |
| |
| List<String> onlyBefore = new ArrayList<>(beforeAssets); |
| onlyBefore.removeAll(afterAssets); |
| List<String> onlyAfter = new ArrayList<>(afterAssets); |
| onlyAfter.removeAll(beforeAssets); |
| |
| Slog.i(TAG, "ApplicationInfo updating for " + ai.packageName + ", new timestamp: " |
| + ai.createTimestamp + "\nassets removed: " + onlyBefore + "\nassets added: " |
| + onlyAfter); |
| |
| if (DEBUG_APP_INFO) { |
| Slog.v(TAG, "ApplicationInfo updating for " + ai.packageName |
| + ", assets before change: " + beforeAssets + "\n assets after change: " |
| + afterAssets); |
| } |
| } |
| } |
| |
| /** |
| * Sets the supplied {@code overrideConfig} as pending for the {@code token}. Calling |
| * this method prevents any calls to |
| * {@link #handleActivityConfigurationChanged(ActivityClientRecord, Configuration, int, |
| * ActivityWindowInfo)} from processing any configurations older than {@code overrideConfig}. |
| */ |
| @Override |
| public void updatePendingActivityConfiguration(@NonNull IBinder token, |
| @NonNull Configuration overrideConfig) { |
| synchronized (mPendingOverrideConfigs) { |
| final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(token); |
| if (pendingOverrideConfig != null |
| && !pendingOverrideConfig.isOtherSeqNewer(overrideConfig)) { |
| if (DEBUG_CONFIGURATION) { |
| Slog.v(TAG, "Activity has newer configuration pending so this transaction will" |
| + " be dropped. overrideConfig=" + overrideConfig |
| + " pendingOverrideConfig=" + pendingOverrideConfig); |
| } |
| return; |
| } |
| mPendingOverrideConfigs.put(token, overrideConfig); |
| } |
| } |
| |
| @Override |
| public void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r, |
| @NonNull Configuration overrideConfig, int displayId, |
| @NonNull ActivityWindowInfo activityWindowInfo) { |
| handleActivityConfigurationChanged(r, overrideConfig, displayId, activityWindowInfo, |
| // This is the only place that uses alwaysReportChange=true. The entry point should |
| // be from ActivityConfigurationChangeItem or MoveToDisplayItem, so the server side |
| // has confirmed the activity should handle the configuration instead of relaunch. |
| // If Activity#onConfigurationChanged is called unexpectedly, then we can know it is |
| // something wrong from server side. |
| true /* alwaysReportChange */); |
| } |
| |
| /** |
| * Handle new activity configuration and/or move to a different display. This method is a noop |
| * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been |
| * called with a newer config than {@code overrideConfig}. |
| * |
| * @param r Target activity record. |
| * @param overrideConfig Activity override config. |
| * @param displayId Id of the display where activity was moved to, -1 if there was no move and |
| * value didn't change. |
| * @param activityWindowInfo the window info of the given activity. |
| */ |
| void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r, |
| @NonNull Configuration overrideConfig, int displayId, |
| @NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) { |
| final ClientTransactionListenerController controller = |
| ClientTransactionListenerController.getInstance(); |
| final Context contextToUpdate = r.activity; |
| controller.onContextConfigurationPreChanged(contextToUpdate); |
| try { |
| handleActivityConfigurationChangedInner(r, overrideConfig, displayId, |
| activityWindowInfo, alwaysReportChange); |
| } finally { |
| controller.onContextConfigurationPostChanged(contextToUpdate); |
| } |
| } |
| |
| private void handleActivityConfigurationChangedInner(@NonNull ActivityClientRecord r, |
| @NonNull Configuration overrideConfig, int displayId, |
| @NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) { |
| synchronized (mPendingOverrideConfigs) { |
| final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token); |
| if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) { |
| if (DEBUG_CONFIGURATION) { |
| Slog.v(TAG, "Activity has newer configuration pending so drop this" |
| + " transaction. overrideConfig=" + overrideConfig |
| + " pendingOverrideConfig=" + pendingOverrideConfig); |
| } |
| return; |
| } |
| mPendingOverrideConfigs.remove(r.token); |
| } |
| |
| if (displayId == INVALID_DISPLAY) { |
| // If INVALID_DISPLAY is passed assume that the activity should keep its current |
| // display. |
| displayId = r.activity.getDisplayId(); |
| } |
| final boolean movedToDifferentDisplay = isDifferentDisplay( |
| r.activity.getDisplayId(), displayId); |
| if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig) |
| && !movedToDifferentDisplay) { |
| if (DEBUG_CONFIGURATION) { |
| Slog.v(TAG, "Activity already handled newer configuration so drop this" |
| + " transaction. overrideConfig=" + overrideConfig + " r.overrideConfig=" |
| + r.overrideConfig); |
| } |
| return; |
| } |
| |
| // Perform updates. |
| r.overrideConfig = overrideConfig; |
| r.mActivityWindowInfo.set(activityWindowInfo); |
| |
| final ViewRootImpl viewRoot = r.activity.mDecor != null |
| ? r.activity.mDecor.getViewRootImpl() : null; |
| |
| if (DEBUG_CONFIGURATION) { |
| Slog.v(TAG, "Handle activity config changed, activity:" |
| + r.activityInfo.name + ", displayId=" + r.activity.getDisplayId() |
| + (movedToDifferentDisplay ? (", newDisplayId=" + displayId) : "") |
| + ", config=" + overrideConfig); |
| } |
| final Configuration reportedConfig = performConfigurationChangedForActivity(r, |
| mConfigurationController.getCompatConfiguration(), |
| movedToDifferentDisplay ? displayId : r.activity.getDisplayId(), |
| alwaysReportChange); |
| // Notify the ViewRootImpl instance about configuration changes. It may have initiated this |
| // update to make sure that resources are updated before updating itself. |
| if (viewRoot != null) { |
| if (movedToDifferentDisplay) { |
| viewRoot.onMovedToDisplay(displayId, reportedConfig); |
| } |
| viewRoot.updateConfiguration(displayId); |
| } |
| mSomeActivitiesChanged = true; |
| |
| // Trigger ActivityWindowInfo callback if changed. |
| handleActivityWindowInfoChanged(r); |
| } |
| |
| private void handleActivityWindowInfoChanged(@NonNull ActivityClientRecord r) { |
| if (!activityWindowInfoFlag()) { |
| return; |
| } |
| if (r.mActivityWindowInfo.equals(r.mLastReportedActivityWindowInfo)) { |
| return; |
| } |
| r.mLastReportedActivityWindowInfo.set(r.mActivityWindowInfo); |
| ClientTransactionListenerController.getInstance().onActivityWindowInfoChanged(r.token, |
| r.mActivityWindowInfo); |
| } |
| |
| final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) { |
| if (start) { |
| try { |
| switch (profileType) { |
| default: |
| mProfiler.setProfiler(profilerInfo); |
| mProfiler.startProfiling(); |
| break; |
| } |
| } catch (RuntimeException e) { |
| Slog.w(TAG, "Profiling failed on path " + profilerInfo.profileFile |
| + " -- can the process access this path?"); |
| } finally { |
| profilerInfo.closeFd(); |
| } |
| } else { |
| switch (profileType) { |
| default: |
| mProfiler.stopProfiling(); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Public entrypoint to stop profiling. This is required to end profiling when the app crashes, |
| * so that profiler data won't be lost. |
| * |
| * @hide |
| */ |
| public void stopProfiling() { |
| if (mProfiler != null) { |
| mProfiler.stopProfiling(); |
| } |
| } |
| |
| static void handleDumpHeap(DumpHeapData dhd) { |
| if (dhd.runGc) { |
| System.gc(); |
| System.runFinalization(); |
| System.gc(); |
| } |
| if (dhd.dumpBitmaps != null) { |
| Bitmap.dumpAll(dhd.dumpBitmaps); |
| } |
| try (ParcelFileDescriptor fd = dhd.fd) { |
| if (dhd.managed) { |
| Debug.dumpHprofData(dhd.path, fd.getFileDescriptor()); |
| } else if (dhd.mallocInfo) { |
| Debug.dumpNativeMallocInfo(fd.getFileDescriptor()); |
| } else { |
| Debug.dumpNativeHeap(fd.getFileDescriptor()); |
| } |
| } catch (IOException e) { |
| if (dhd.managed) { |
| Slog.w(TAG, "Managed heap dump failed on path " + dhd.path |
| + " -- can the process access this path?", e); |
| } else { |
| Slog.w(TAG, "Failed to dump heap", e); |
| } |
| } catch (RuntimeException e) { |
| // This should no longer happening now that we're copying the file descriptor. |
| Slog.wtf(TAG, "Heap dumper threw a runtime exception", e); |
| } |
| try { |
| ActivityManager.getService().dumpHeapFinished(dhd.path); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| if (dhd.finishCallback != null) { |
| dhd.finishCallback.sendResult(null); |
| } |
| if (dhd.dumpBitmaps != null) { |
| Bitmap.dumpAll(null); // clear dump |
| } |
| } |
| |
| final void handleDispatchPackageBroadcast(int cmd, String[] packages) { |
| boolean hasPkgInfo = false; |
| switch (cmd) { |
| case ApplicationThreadConstants.PACKAGE_REMOVED: |
| case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL: |
| { |
| final boolean killApp = cmd == ApplicationThreadConstants.PACKAGE_REMOVED; |
| if (packages == null) { |
| break; |
| } |
| synchronized (mResourcesManager) { |
| for (int i = packages.length - 1; i >= 0; i--) { |
| if (!hasPkgInfo) { |
| WeakReference<LoadedApk> ref = mPackages.get(packages[i]); |
| if (ref != null && ref.get() != null) { |
| hasPkgInfo = true; |
| } else { |
| ref = mResourcePackages.get(packages[i]); |
| if (ref != null && ref.get() != null) { |
| hasPkgInfo = true; |
| } |
| } |
| } |
| if (killApp) { |
| // Keep in sync with "perhaps it was removed" case below. |
| mPackages.remove(packages[i]); |
| mResourcePackages.remove(packages[i]); |
| } |
| } |
| } |
| break; |
| } |
| case ApplicationThreadConstants.PACKAGE_REPLACED: |
| { |
| if (packages == null) { |
| break; |
| } |
| |
| List<String> packagesHandled = new ArrayList<>(); |
| |
| synchronized (mResourcesManager) { |
| for (int i = packages.length - 1; i >= 0; i--) { |
| String packageName = packages[i]; |
| WeakReference<LoadedApk> ref = mPackages.get(packageName); |
| LoadedApk pkgInfo = ref != null ? ref.get() : null; |
| if (pkgInfo != null) { |
| hasPkgInfo = true; |
| } else { |
| ref = mResourcePackages.get(packageName); |
| pkgInfo = ref != null ? ref.get() : null; |
| if (pkgInfo != null) { |
| hasPkgInfo = true; |
| } |
| } |
| // If the package is being replaced, yet it still has a valid |
| // LoadedApk object, the package was updated with _DONT_KILL. |
| // Adjust it's internal references to the application info and |
| // resources. |
| if (pkgInfo != null) { |
| packagesHandled.add(packageName); |
| try { |
| final ApplicationInfo aInfo = |
| sPackageManager.getApplicationInfo( |
| packageName, |
| PackageManager.GET_SHARED_LIBRARY_FILES, |
| UserHandle.myUserId()); |
| |
| if (aInfo != null) { |
| if (mActivities.size() > 0) { |
| for (ActivityClientRecord ar : mActivities.values()) { |
| if (ar.activityInfo.applicationInfo.packageName |
| .equals(packageName)) { |
| ar.activityInfo.applicationInfo = aInfo; |
| ar.packageInfo = pkgInfo; |
| } |
| } |
| } |
| |
| final String[] oldResDirs = {pkgInfo.getResDir()}; |
| |
| final ArrayList<String> oldPaths = new ArrayList<>(); |
| LoadedApk.makePaths( |
| this, pkgInfo.getApplicationInfo(), oldPaths); |
| pkgInfo.updateApplicationInfo(aInfo, oldPaths); |
| |
| // Update affected Resources objects to use new ResourcesImpl |
| mResourcesManager.appendPendingAppInfoUpdate(oldResDirs, |
| aInfo); |
| mResourcesManager.applyAllPendingAppInfoUpdates(); |
| } |
| } catch (RemoteException e) { |
| } |
| } else { |
| // No package, perhaps it was removed? |
| Slog.d(TAG, "Package [" + packages[i] + "] reported as REPLACED," |
| + " but missing application info. Assuming REMOVED."); |
| mPackages.remove(packages[i]); |
| mResourcePackages.remove(packages[i]); |
| } |
| } |
| } |
| |
| try { |
| getPackageManager().notifyPackagesReplacedReceived( |
| packagesHandled.toArray(new String[0])); |
| } catch (RemoteException ignored) { |
| } |
| |
| break; |
| } |
| } |
| ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo); |
| } |
| |
| final void handleLowMemory() { |
| final ArrayList<ComponentCallbacks2> callbacks = |
| collectComponentCallbacks(true /* includeUiContexts */); |
| |
| final int N = callbacks.size(); |
| for (int i=0; i<N; i++) { |
| callbacks.get(i).onLowMemory(); |
| } |
| |
| // Ask SQLite to free up as much memory as it can, mostly from its page caches. |
| if (Process.myUid() != Process.SYSTEM_UID) { |
| int sqliteReleased = SQLiteDatabase.releaseMemory(); |
| EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased); |
| } |
| |
| // Ask graphics to free up as much as possible (font/image caches) |
| Canvas.freeCaches(); |
| |
| // Ask text layout engine to free also as much as possible |
| Canvas.freeTextLayoutCaches(); |
| |
| BinderInternal.forceGc("mem"); |
| } |
| |
| private void handleTrimMemory(int level) { |
| if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory: " + level); |
| } |
| if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level); |
| |
| try { |
| if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { |
| PropertyInvalidatedCache.onTrimMemory(); |
| } |
| |
| final ArrayList<ComponentCallbacks2> callbacks = |
| collectComponentCallbacks(true /* includeUiContexts */); |
| |
| final int N = callbacks.size(); |
| for (int i = 0; i < N; i++) { |
| callbacks.get(i).onTrimMemory(level); |
| } |
| } finally { |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| } |
| |
| WindowManagerGlobal.getInstance().trimMemory(level); |
| |
| if (SystemProperties.getInt("debug.am.run_gc_trim_level", Integer.MAX_VALUE) <= level) { |
| unscheduleGcIdler(); |
| doGcIfNeeded("tm"); |
| } |
| if (SystemProperties.getInt("debug.am.run_mallopt_trim_level", Integer.MAX_VALUE) |
| <= level) { |
| unschedulePurgeIdler(); |
| purgePendingResources(); |
| } |
| } |
| |
| private void setupGraphicsSupport(Context context) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setupGraphicsSupport"); |
| |
| // The system package doesn't have real data directories, so don't set up cache paths. |
| if (!"android".equals(context.getPackageName())) { |
| // This cache location probably points at credential-encrypted |
| // storage which may not be accessible yet; assign it anyway instead |
| // of pointing at device-encrypted storage. |
| final File cacheDir = context.getCacheDir(); |
| if (cacheDir != null) { |
| // Provide a usable directory for temporary files |
| String tmpdir = cacheDir.getAbsolutePath(); |
| System.setProperty("java.io.tmpdir", tmpdir); |
| try { |
| android.system.Os.setenv("TMPDIR", tmpdir, true); |
| } catch (ErrnoException ex) { |
| Log.w(TAG, "Unable to initialize $TMPDIR", ex); |
| } |
| } else { |
| Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property " |
| + "due to missing cache directory"); |
| } |
| |
| // Setup a location to store generated/compiled graphics code. |
| final Context deviceContext = context.createDeviceProtectedStorageContext(); |
| final File codeCacheDir = deviceContext.getCodeCacheDir(); |
| final File deviceCacheDir = deviceContext.getCacheDir(); |
| if (codeCacheDir != null && deviceCacheDir != null) { |
| try { |
| int uid = Process.myUid(); |
| String[] packages = getPackageManager().getPackagesForUid(uid); |
| if (packages != null) { |
| HardwareRenderer.setupDiskCache(deviceCacheDir); |
| RenderScriptCacheDir.setupDiskCache(codeCacheDir); |
| } |
| } catch (RemoteException e) { |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| throw e.rethrowFromSystemServer(); |
| } |
| } else { |
| Log.w(TAG, "Unable to use shader/script cache: missing code-cache directory"); |
| } |
| } |
| |
| // mCoreSettings is only updated from the main thread, while this function is only called |
| // from main thread as well, so no need to lock here. |
| GraphicsEnvironment.getInstance().setup(context, mCoreSettings); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| } |
| |
| /** |
| * Returns the correct library directory for the current ABI. |
| * <p> |
| * If we're dealing with a multi-arch application that has both 32 and 64 bit shared |
| * libraries, we might need to choose the secondary depending on what the current |
| * runtime's instruction set is. |
| */ |
| private String getInstrumentationLibrary(ApplicationInfo appInfo, InstrumentationInfo insInfo) { |
| if (appInfo.primaryCpuAbi != null && appInfo.secondaryCpuAbi != null |
| && appInfo.secondaryCpuAbi.equals(insInfo.secondaryCpuAbi)) { |
| // Get the instruction set supported by the secondary ABI. In the presence |
| // of a native bridge this might be different than the one secondary ABI used. |
| String secondaryIsa = |
| VMRuntime.getInstructionSet(appInfo.secondaryCpuAbi); |
| final String secondaryDexCodeIsa = |
| SystemProperties.get("ro.dalvik.vm.isa." + secondaryIsa); |
| secondaryIsa = secondaryDexCodeIsa.isEmpty() ? secondaryIsa : secondaryDexCodeIsa; |
| |
| final String runtimeIsa = VMRuntime.getRuntime().vmInstructionSet(); |
| if (runtimeIsa.equals(secondaryIsa)) { |
| return insInfo.secondaryNativeLibraryDir; |
| } |
| } |
| return insInfo.nativeLibraryDir; |
| } |
| |
| @UnsupportedAppUsage |
| private void handleBindApplication(AppBindData data) { |
| mDdmSyncStageUpdater.next(Stage.Bind); |
| |
| // Register the UI Thread as a sensitive thread to the runtime. |
| VMRuntime.registerSensitiveThread(); |
| // In the case the stack depth property exists, pass it down to the runtime. |
| String property = SystemProperties.get("debug.allocTracker.stackDepth"); |
| if (property.length() != 0) { |
| VMDebug.setAllocTrackerStackDepth(Integer.parseInt(property)); |
| } |
| if (data.trackAllocation) { |
| DdmVmInternal.setRecentAllocationsTrackingEnabled(true); |
| } |
| // Note when this process has started. |
| Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(), |
| data.startRequestedElapsedTime, data.startRequestedUptime); |
| |
| AppCompatCallbacks.install(data.disabledCompatChanges, data.mLoggableCompatChanges); |
| // Let libcore handle any compat changes after installing the list of compat changes. |
| AppSpecializationHooks.handleCompatChangesBeforeBindingApplication(); |
| |
| // Initialize the zip path validator callback depending on the targetSdk. |
| // This has to be after AppCompatCallbacks#install() so that the Compat |
| // checks work accordingly. |
| initZipPathValidatorCallback(); |
| |
| mBoundApplication = data; |
| mConfigurationController.setConfiguration(data.config); |
| mConfigurationController.setCompatConfiguration(data.config); |
| mConfiguration = mConfigurationController.getConfiguration(); |
| mCompatibilityInfo = data.compatInfo; |
| |
| mProfiler = new Profiler(); |
| String agent = null; |
| if (data.initProfilerInfo != null) { |
| mProfiler.profileFile = data.initProfilerInfo.profileFile; |
| mProfiler.profileFd = data.initProfilerInfo.profileFd; |
| mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval; |
| mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler; |
| mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput; |
| mProfiler.mClockType = data.initProfilerInfo.clockType; |
| mProfiler.mProfilerOutputVersion = data.initProfilerInfo.profilerOutputVersion; |
| if (data.initProfilerInfo.attachAgentDuringBind) { |
| agent = data.initProfilerInfo.agent; |
| } |
| } |
| |
| // send up app name; do this *before* waiting for debugger |
| Process.setArgV0(data.processName); |
| android.ddm.DdmHandleAppName.setAppName(data.processName, |
| data.appInfo.packageName, |
| UserHandle.myUserId()); |
| VMRuntime.setProcessPackageName(data.appInfo.packageName); |
| mDdmSyncStageUpdater.next(Stage.Named); |
| |
| // Pass data directory path to ART. This is used for caching information and |
| // should be set before any application code is loaded. |
| VMRuntime.setProcessDataDirectory(data.appInfo.dataDir); |
| |
| if (mProfiler.profileFd != null) { |
| mProfiler.startProfiling(); |
| } |
| |
| // If the app is Honeycomb MR1 or earlier, switch its AsyncTask |
| // implementation to use the pool executor. Normally, we use the |
| // serialized executor as the default. This has to happen in the |
| // main thread so the main looper is set right. |
| if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) { |
| AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
| } |
| |
| // Let the util.*Array classes maintain "undefined" for apps targeting Pie or earlier. |
| UtilConfig.setThrowExceptionForUpperArrayOutOfBounds( |
| data.appInfo.targetSdkVersion >= Build.VERSION_CODES.Q); |
| |
| Message.updateCheckRecycle(data.appInfo.targetSdkVersion); |
| |
| // Supply the targetSdkVersion to the UI rendering module, which may |
| // need it in cases where it does not have access to the appInfo. |
| android.graphics.Compatibility.setTargetSdkVersion(data.appInfo.targetSdkVersion); |
| |
| /* |
| * Before spawning a new process, reset the time zone to be the system time zone. |
| * This needs to be done because the system time zone could have changed after the |
| * the spawning of this process. Without doing this this process would have the incorrect |
| * system time zone. |
| */ |
| TimeZone.setDefault(null); |
| |
| /* |
| * Set the LocaleList. This may change once we create the App Context. |
| */ |
| LocaleList.setDefault(data.config.getLocales()); |
| |
| if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { |
| try { |
| Typeface.setSystemFontMap(data.mSerializedSystemFontMap); |
| } catch (IOException | ErrnoException e) { |
| Slog.e(TAG, "Failed to parse serialized system font map"); |
| Typeface.loadPreinstalledSystemFontMap(); |
| } |
| } |
| |
| synchronized (mResourcesManager) { |
| /* |
| * Update the system configuration since its preloaded and might not |
| * reflect configuration changes. The configuration object passed |
| * in AppBindData can be safely assumed to be up to date |
| */ |
| mResourcesManager.applyConfigurationToResources(data.config, data.compatInfo); |
| mCurDefaultDisplayDpi = data.config.densityDpi; |
| |
| // This calls mResourcesManager so keep it within the synchronized block. |
| mConfigurationController.applyCompatConfiguration(); |
| } |
| |
| final boolean isSdkSandbox = data.sdkSandboxClientAppPackage != null; |
| data.info = getPackageInfo(data.appInfo, mCompatibilityInfo, null /* baseLoader */, |
| false /* securityViolation */, true /* includeCode */, |
| false /* registerPackage */, isSdkSandbox); |
| if (isSdkSandbox) { |
| data.info.setSdkSandboxStorage(data.sdkSandboxClientAppVolumeUuid, |
| data.sdkSandboxClientAppPackage); |
| } |
| |
| if (agent != null) { |
| handleAttachAgent(agent, data.info); |
| } |
| |
| /** |
| * Switch this process to density compatibility mode if needed. |
| */ |
| if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) |
| == 0) { |
| mDensityCompatMode = true; |
| Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT); |
| } |
| mConfigurationController.updateDefaultDensity(data.config.densityDpi); |
| |
| // mCoreSettings is only updated from the main thread, while this function is only called |
| // from main thread as well, so no need to lock here. |
| final String use24HourSetting = mCoreSettings.getString(Settings.System.TIME_12_24); |
| Boolean is24Hr = null; |
| if (use24HourSetting != null) { |
| is24Hr = "24".equals(use24HourSetting) ? Boolean.TRUE : Boolean.FALSE; |
| } |
| // null : use locale default for 12/24 hour formatting, |
| // false : use 12 hour format, |
| // true : use 24 hour format. |
| DateFormat.set24HourTimePref(is24Hr); |
| |
| updateDebugViewAttributeState(); |
| |
| StrictMode.initThreadDefaults(data.appInfo); |
| StrictMode.initVmDefaults(data.appInfo); |
| |
| // Allow binder tracing, and application-generated systrace messages if we're profileable. |
| boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; |
| boolean isAppProfileable = isAppDebuggable || data.appInfo.isProfileable(); |
| Trace.setAppTracingAllowed(isAppProfileable); |
| if ((isAppProfileable || Build.IS_DEBUGGABLE) && data.enableBinderTracking) { |
| Binder.enableStackTracking(); |
| } |
| |
| // Initialize heap profiling. |
| if (isAppProfileable || Build.IS_DEBUGGABLE) { |
| nInitZygoteChildHeapProfiling(); |
| } |
| |
| // Allow renderer debugging features if we're debuggable. |
| HardwareRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE); |
| HardwareRenderer.setPackageName(data.appInfo.packageName); |
| |
| // Pass the current context to HardwareRenderer |
| HardwareRenderer.setContextForInit(getSystemContext()); |
| if (data.persistent) { |
| HardwareRenderer.setIsSystemOrPersistent(); |
| } |
| |
| // Instrumentation info affects the class loader, so load it before |
| // setting up the app context. |
| final InstrumentationInfo ii; |
| if (data.instrumentationName != null) { |
| ii = prepareInstrumentation(data); |
| } else { |
| ii = null; |
| } |
| |
| final IActivityManager mgr = ActivityManager.getService(); |
| final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); |
| mConfigurationController.updateLocaleListFromAppContext(appContext); |
| |
| // Initialize the default http proxy in this process. |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Setup proxies"); |
| try { |
| // In pre-boot mode (doing initial launch to collect password), not all system is up. |
| // This includes the connectivity service, so trying to obtain ConnectivityManager at |
| // that point would return null. Check whether the ConnectivityService is available, and |
| // avoid crashing with a NullPointerException if it is not. |
| final IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); |
| if (b != null) { |
| final ConnectivityManager cm = |
| appContext.getSystemService(ConnectivityManager.class); |
| Proxy.setHttpProxyConfiguration(cm.getDefaultProxy()); |
| } |
| } finally { |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| } |
| |
| if (!Process.isIsolated()) { |
| final int oldMask = StrictMode.allowThreadDiskWritesMask(); |
| try { |
| setupGraphicsSupport(appContext); |
| } finally { |
| StrictMode.setThreadPolicyMask(oldMask); |
| } |
| } else { |
| HardwareRenderer.setIsolatedProcess(true); |
| } |
| |
| // Install the Network Security Config Provider. This must happen before the application |
| // code is loaded to prevent issues with instances of TLS objects being created before |
| // the provider is installed. |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "NetworkSecurityConfigProvider.install"); |
| NetworkSecurityConfigProvider.install(appContext); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| |
| // For backward compatibility, TrafficStats needs static access to the application context. |
| // But for isolated apps which cannot access network related services, service discovery |
| // is restricted. Hence, calling this would result in NPE. |
| if (!Process.isIsolated()) { |
| TrafficStats.init(appContext); |
| } |
| |
| // Continue loading instrumentation. |
| if (ii != null) { |
| initInstrumentation(ii, data, appContext); |
| } else { |
| mInstrumentation = new Instrumentation(); |
| mInstrumentation.basicInit(this); |
| } |
| |
| if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) { |
| dalvik.system.VMRuntime.getRuntime().clearGrowthLimit(); |
| } else { |
| // Small heap, clamp to the current growth limit and let the heap release |
| // pages after the growth limit to the non growth limit capacity. b/18387825 |
| dalvik.system.VMRuntime.getRuntime().clampGrowthLimit(); |
| } |
| |
| // Allow disk access during application and provider setup. This could |
| // block processing ordered broadcasts, but later processing would |
| // probably end up doing the same disk access. |
| Application app; |
| final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); |
| final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy(); |
| |
| if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) { |
| mDdmSyncStageUpdater.next(Stage.Debugger); |
| if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) { |
| waitForDebugger(data); |
| } else if (data.debugMode == ApplicationThreadConstants.DEBUG_SUSPEND) { |
| suspendAllAndSendVmStart(data); |
| } |
| // Nothing special to do in case of DEBUG_ON. |
| } |
| mDdmSyncStageUpdater.next(Stage.Running); |
| |
| long timestampApplicationOnCreateNs = 0; |
| try { |
| // If the app is being launched for full backup or restore, bring it up in |
| // a restricted environment with the base application class. |
| app = data.info.makeApplicationInner(data.restrictedBackupMode, null); |
| |
| // Propagate autofill compat state |
| app.setAutofillOptions(data.autofillOptions); |
| |
| // Propagate Content Capture options |
| app.setContentCaptureOptions(data.contentCaptureOptions); |
| sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName); |
| |
| mInitialApplication = app; |
| final boolean updateHttpProxy; |
| synchronized (this) { |
| updateHttpProxy = mUpdateHttpProxyOnBind; |
| // This synchronized block ensures that any subsequent call to updateHttpProxy() |
| // will see a non-null mInitialApplication. |
| } |
| if (updateHttpProxy) { |
| ActivityThread.updateHttpProxy(app); |
| } |
| |
| // don't bring up providers in restricted mode; they may depend on the |
| // app's custom Application class |
| if (!data.restrictedBackupMode) { |
| if (!ArrayUtils.isEmpty(data.providers)) { |
| installContentProviders(app, data.providers); |
| } |
| } |
| |
| // Do this after providers, since instrumentation tests generally start their |
| // test thread at this point, and we don't want that racing. |
| try { |
| mInstrumentation.onCreate(data.instrumentationArgs); |
| } |
| catch (Exception e) { |
| throw new RuntimeException( |
| "Exception thrown in onCreate() of " |
| + data.instrumentationName + ": " + e.toString(), e); |
| } |
| try { |
| timestampApplicationOnCreateNs = SystemClock.elapsedRealtimeNanos(); |
| mInstrumentation.callApplicationOnCreate(app); |
| } catch (Exception e) { |
| timestampApplicationOnCreateNs = 0; |
| if (!mInstrumentation.onException(app, e)) { |
| throw new RuntimeException( |
| "Unable to create application " + app.getClass().getName() |
| + ": " + e.toString(), e); |
| } |
| } |
| } finally { |
| // If the app targets < O-MR1, or doesn't change the thread policy |
| // during startup, clobber the policy to maintain behavior of b/36951662 |
| if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1 |
| || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) { |
| StrictMode.setThreadPolicy(savedPolicy); |
| } |
| } |
| |
| // Preload fonts resources |
| FontsContract.setApplicationContextForResources(appContext); |
| if (!Process.isIsolated()) { |
| try { |
| final ApplicationInfo info = |
| getPackageManager().getApplicationInfo( |
| data.appInfo.packageName, |
| PackageManager.GET_META_DATA /*flags*/, |
| UserHandle.myUserId()); |
| if (info.metaData != null) { |
| final int preloadedFontsResource = info.metaData.getInt( |
| ApplicationInfo.METADATA_PRELOADED_FONTS, 0); |
| if (preloadedFontsResource != 0) { |
| data.info.getResources().preloadFonts(preloadedFontsResource); |
| } |
| } |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| try { |
| mgr.finishAttachApplication(mStartSeq, timestampApplicationOnCreateNs); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| |
| // Set binder transaction callback after finishing bindApplication |
| Binder.setTransactionCallback(new IBinderCallback() { |
| @Override |
| public void onTransactionError(int pid, int code, int flags, int err) { |
| final long now = SystemClock.uptimeMillis(); |
| if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE) { |
| Slog.d(TAG, "Too many transaction errors, throttling freezer binder callback."); |
| return; |
| } |
| mBinderCallbackLast = now; |
| try { |
| mgr.frozenBinderTransactionDetected(pid, code, flags, err); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| }); |
| } |
| |
| @UnsupportedAppUsage |
| private void waitForDebugger(AppBindData data) { |
| final IActivityManager mgr = ActivityManager.getService(); |
| Slog.w(TAG, "Application " + data.info.getPackageName() |
| + " is waiting for the debugger ..."); |
| |
| try { |
| mgr.showWaitingForDebugger(mAppThread, true); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| |
| Debug.waitForDebugger(); |
| |
| try { |
| mgr.showWaitingForDebugger(mAppThread, false); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| @UnsupportedAppUsage |
| private void suspendAllAndSendVmStart(AppBindData data) { |
| final IActivityManager mgr = ActivityManager.getService(); |
| Slog.w(TAG, "Application " + data.info.getPackageName() |
| + " is suspending. Debugger needs to resume to continue."); |
| |
| try { |
| mgr.showWaitingForDebugger(mAppThread, true); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| |
| Debug.suspendAllAndSendVmStart(); |
| |
| try { |
| mgr.showWaitingForDebugger(mAppThread, false); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * If targetSDK >= U: set the safe zip path validator callback which disallows dangerous zip |
| * entry names. |
| * Otherwise: clear the callback to the default validation. |
| */ |
| private void initZipPathValidatorCallback() { |
| if (CompatChanges.isChangeEnabled(VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL)) { |
| ZipPathValidator.setCallback(new SafeZipPathValidatorCallback()); |
| } else { |
| ZipPathValidator.clearCallback(); |
| } |
| } |
| |
| private void handleSetContentCaptureOptionsCallback(String packageName) { |
| if (mContentCaptureOptionsCallback != null) { |
| return; |
| } |
| |
| IBinder b = ServiceManager.getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); |
| if (b == null) { |
| return; |
| } |
| |
| IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b); |
| mContentCaptureOptionsCallback = new IContentCaptureOptionsCallback.Stub() { |
| @Override |
| public void setContentCaptureOptions(ContentCaptureOptions options) |
| throws RemoteException { |
| if (mInitialApplication != null) { |
| mInitialApplication.setContentCaptureOptions(options); |
| } |
| } |
| }; |
| try { |
| service.registerContentCaptureOptionsCallback(packageName, |
| mContentCaptureOptionsCallback); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "registerContentCaptureOptionsCallback() failed: " |
| + packageName, e); |
| mContentCaptureOptionsCallback = null; |
| } |
| } |
| |
| private void handleInstrumentWithoutRestart(AppBindData data) { |
| try { |
| data.compatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; |
| data.info = getPackageInfoNoCheck(data.appInfo); |
| mInstrumentingWithoutRestart = true; |
| final InstrumentationInfo ii = prepareInstrumentation(data); |
| final ContextImpl appContext = |
| ContextImpl.createAppContext(this, data.info); |
| |
| initInstrumentation(ii, data, appContext); |
| |
| try { |
| mInstrumentation.onCreate(data.instrumentationArgs); |
| } catch (Exception e) { |
| throw new RuntimeException( |
| "Exception thrown in onCreate() of " |
| + data.instrumentationName + ": " + e.toString(), e); |
| } |
| |
| } catch (Exception e) { |
| Slog.e(TAG, "Error in handleInstrumentWithoutRestart", e); |
| } |
| } |
| |
| private InstrumentationInfo prepareInstrumentation(AppBindData data) { |
| final InstrumentationInfo ii; |
| try { |
| ii = getPackageManager().getInstrumentationInfoAsUser(data.instrumentationName, |
| 0 /* flags */, UserHandle.myUserId()); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| if (ii == null) { |
| throw new RuntimeException( |
| "Unable to find instrumentation info for: " + data.instrumentationName); |
| } |
| |
| // Warn of potential ABI mismatches. |
| if (!Objects.equals(data.appInfo.primaryCpuAbi, ii.primaryCpuAbi) |
| || !Objects.equals(data.appInfo.secondaryCpuAbi, ii.secondaryCpuAbi)) { |
| Slog.w(TAG, "Package uses different ABI(s) than its instrumentation: " |
| + "package[" + data.appInfo.packageName + "]: " |
| + data.appInfo.primaryCpuAbi + ", " + data.appInfo.secondaryCpuAbi |
| + " instrumentation[" + ii.packageName + "]: " |
| + ii.primaryCpuAbi + ", " + ii.secondaryCpuAbi); |
| } |
| |
| mInstrumentationPackageName = ii.packageName; |
| mInstrumentationAppDir = ii.sourceDir; |
| mInstrumentationSplitAppDirs = ii.splitSourceDirs; |
| mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii); |
| mInstrumentedAppDir = data.info.getAppDir(); |
| mInstrumentedSplitAppDirs = data.info.getSplitAppDirs(); |
| mInstrumentedLibDir = data.info.getLibDir(); |
| |
| return ii; |
| } |
| |
| private void initInstrumentation( |
| InstrumentationInfo ii, AppBindData data, ContextImpl appContext) { |
| ApplicationInfo instrApp; |
| try { |
| instrApp = getPackageManager().getApplicationInfo(ii.packageName, 0, |
| UserHandle.myUserId()); |
| } catch (RemoteException e) { |
| instrApp = null; |
| } |
| if (instrApp == null) { |
| instrApp = new ApplicationInfo(); |
| } |
| ii.copyTo(instrApp); |
| instrApp.initForUser(UserHandle.myUserId()); |
| final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo, |
| appContext.getClassLoader(), false, true, false); |
| |
| // The test context's op package name == the target app's op package name, because |
| // the app ops manager checks the op package name against the real calling UID, |
| // which is what the target package name is associated with. |
| // In the case of instrumenting an sdk running in the sdk sandbox, appContext refers |
| // to the context of the sdk running in the sandbox. Since the sandbox does not have |
| // access to data outside the sandbox, we require the instrContext to point to the |
| // sdk in the sandbox as well, and not to the test context. |
| final ContextImpl instrContext = |
| (data.isSdkInSandbox) |
| ? appContext |
| : ContextImpl.createAppContext(this, pi, appContext.getOpPackageName()); |
| |
| try { |
| final ClassLoader cl = instrContext.getClassLoader(); |
| mInstrumentation = (Instrumentation) |
| cl.loadClass(data.instrumentationName.getClassName()).newInstance(); |
| } catch (Exception e) { |
| throw new RuntimeException( |
| "Unable to instantiate instrumentation " |
| + data.instrumentationName + ": " + e.toString(), e); |
| } |
| |
| final ComponentName component = new ComponentName(ii.packageName, ii.name); |
| mInstrumentation.init(this, instrContext, appContext, component, |
| data.instrumentationWatcher, data.instrumentationUiAutomationConnection); |
| |
| if (mProfiler.profileFile != null && !ii.handleProfiling |
| && mProfiler.profileFd == null) { |
| mProfiler.handlingProfiling = true; |
| final File file = new File(mProfiler.profileFile); |
| file.getParentFile().mkdirs(); |
| Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); |
| } |
| } |
| |
| private void handleFinishInstrumentationWithoutRestart() { |
| mInstrumentation.onDestroy(); |
| mInstrumentationPackageName = null; |
| mInstrumentationAppDir = null; |
| mInstrumentationSplitAppDirs = null; |
| mInstrumentationLibDir = null; |
| mInstrumentedAppDir = null; |
| mInstrumentedSplitAppDirs = null; |
| mInstrumentedLibDir = null; |
| mInstrumentingWithoutRestart = false; |
| } |
| |
| /*package*/ final void finishInstrumentation(int resultCode, Bundle results) { |
| IActivityManager am = ActivityManager.getService(); |
| if (mProfiler.profileFile != null && mProfiler.handlingProfiling |
| && mProfiler.profileFd == null) { |
| Debug.stopMethodTracing(); |
| } |
| //Slog.i(TAG, "am: " + ActivityManager.getService() |
| // + ", app thr: " + mAppThread); |
| try { |
| am.finishInstrumentation(mAppThread, resultCode, results); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| if (mInstrumentingWithoutRestart) { |
| sendMessage(H.FINISH_INSTRUMENTATION_WITHOUT_RESTART, null); |
| } |
| } |
| |
| @UnsupportedAppUsage |
| private void installContentProviders( |
| Context context, List<ProviderInfo> providers) { |
| final ArrayList<ContentProviderHolder> results = new ArrayList<>(); |
| |
| for (ProviderInfo cpi : providers) { |
| if (DEBUG_PROVIDER) { |
| StringBuilder buf = new StringBuilder(128); |
| buf.append("Pub "); |
| buf.append(cpi.authority); |
| buf.append(": "); |
| buf.append(cpi.name); |
| Log.i(TAG, buf.toString()); |
| } |
| ContentProviderHolder cph = installProvider(context, null, cpi, |
| false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); |
| if (cph != null) { |
| cph.noReleaseNeeded = true; |
| results.add(cph); |
| } |
| } |
| |
| try { |
| ActivityManager.getService().publishContentProviders( |
| getApplicationThread(), results); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| } |
| |
| @UnsupportedAppUsage |
| public final IContentProvider acquireProvider( |
| Context c, String auth, int userId, boolean stable) { |
| final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable); |
| if (provider != null) { |
| return provider; |
| } |
| |
| // There is a possible race here. Another thread may try to acquire |
| // the same provider at the same time. When this happens, we want to ensure |
| // that the first one wins. |
| // Note that we cannot hold the lock while acquiring and installing the |
| // provider since it might take a long time to run and it could also potentially |
| // be re-entrant in the case where the provider is in the same process. |
| ContentProviderHolder holder; |
| final ProviderKey key = getGetProviderKey(auth, userId); |
| try { |
| synchronized (key) { |
| holder = ActivityManager.getService().getContentProvider( |
| getApplicationThread(), c.getOpPackageName(), auth, userId, stable); |
| // If the returned holder is non-null but its provider is null and it's not |
| // local, we'll need to wait for the publishing of the provider. |
| if (holder != null && holder.provider == null && !holder.mLocal) { |
| synchronized (key.mLock) { |
| if (key.mHolder != null) { |
| if (DEBUG_PROVIDER) { |
| Slog.i(TAG, "already received provider: " + auth); |
| } |
| } else { |
| key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS); |
| } |
| holder = key.mHolder; |
| } |
| if (holder != null && holder.provider == null) { |
| // probably timed out |
| holder = null; |
| } |
| } |
| } |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } catch (InterruptedException e) { |
| holder = null; |
| } finally { |
| // Clear the holder from the key since the key itself is never cleared. |
| synchronized (key.mLock) { |
| key.mHolder = null; |
| } |
| } |
| if (holder == null) { |
| if (UserManager.get(c).isUserUnlocked(userId)) { |
| Slog.e(TAG, "Failed to find provider info for " + auth); |
| } else { |
| Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)"); |
| } |
| return null; |
| } |
| |
| // Install provider will increment the reference count for us, and break |
| // any ties in the race. |
| holder = installProvider(c, holder, holder.info, |
| true /*noisy*/, holder.noReleaseNeeded, stable); |
| return holder.provider; |
| } |
| |
| private ProviderKey getGetProviderKey(String auth, int userId) { |
| final ProviderKey key = new ProviderKey(auth, userId); |
| synchronized (mGetProviderKeys) { |
| ProviderKey lock = mGetProviderKeys.computeIfAbsent(key, k -> k); |
| return lock; |
| } |
| } |
| |
| private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) { |
| if (stable) { |
| prc.stableCount += 1; |
| if (prc.stableCount == 1) { |
| // We are acquiring a new stable reference on the provider. |
| int unstableDelta; |
| if (prc.removePending) { |
| // We have a pending remove operation, which is holding the |
| // last unstable reference. At this point we are converting |
| // that unstable reference to our new stable reference. |
| unstableDelta = -1; |
| // Cancel the removal of the provider. |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "incProviderRef: stable " |
| + "snatched provider from the jaws of death"); |
| } |
| prc.removePending = false; |
| // There is a race! It fails to remove the message, which |
| // will be handled in completeRemoveProvider(). |
| mH.removeMessages(H.REMOVE_PROVIDER, prc); |
| } else { |
| unstableDelta = 0; |
| } |
| try { |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "incProviderRef Now stable - " |
| + prc.holder.info.name + ": unstableDelta=" |
| + unstableDelta); |
| } |
| ActivityManager.getService().refContentProvider( |
| prc.holder.connection, 1, unstableDelta); |
| } catch (RemoteException e) { |
| //do nothing content provider object is dead any way |
| } |
| } |
| } else { |
| prc.unstableCount += 1; |
| if (prc.unstableCount == 1) { |
| // We are acquiring a new unstable reference on the provider. |
| if (prc.removePending) { |
| // Oh look, we actually have a remove pending for the |
| // provider, which is still holding the last unstable |
| // reference. We just need to cancel that to take new |
| // ownership of the reference. |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "incProviderRef: unstable " |
| + "snatched provider from the jaws of death"); |
| } |
| prc.removePending = false; |
| mH.removeMessages(H.REMOVE_PROVIDER, prc); |
| } else { |
| // First unstable ref, increment our count in the |
| // activity manager. |
| try { |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "incProviderRef: Now unstable - " |
| + prc.holder.info.name); |
| } |
| ActivityManager.getService().refContentProvider( |
| prc.holder.connection, 0, 1); |
| } catch (RemoteException e) { |
| //do nothing content provider object is dead any way |
| } |
| } |
| } |
| } |
| } |
| |
| @UnsupportedAppUsage |
| public final IContentProvider acquireExistingProvider( |
| Context c, String auth, int userId, boolean stable) { |
| synchronized (mProviderMap) { |
| final ProviderKey key = new ProviderKey(auth, userId); |
| final ProviderClientRecord pr = mProviderMap.get(key); |
| if (pr == null) { |
| return null; |
| } |
| |
| IContentProvider provider = pr.mProvider; |
| IBinder jBinder = provider.asBinder(); |
| if (!jBinder.isBinderAlive()) { |
| // The hosting process of the provider has died; we can't |
| // use this one. |
| Log.i(TAG, "Acquiring provider " + auth + " for user " + userId |
| + ": existing object's process dead"); |
| handleUnstableProviderDiedLocked(jBinder, true); |
| return null; |
| } |
| |
| // Only increment the ref count if we have one. If we don't then the |
| // provider is not reference counted and never needs to be released. |
| ProviderRefCount prc = mProviderRefCountMap.get(jBinder); |
| if (prc != null) { |
| incProviderRefLocked(prc, stable); |
| } |
| return provider; |
| } |
| } |
| |
| @UnsupportedAppUsage |
| public final boolean releaseProvider(IContentProvider provider, boolean stable) { |
| if (provider == null) { |
| return false; |
| } |
| |
| IBinder jBinder = provider.asBinder(); |
| synchronized (mProviderMap) { |
| ProviderRefCount prc = mProviderRefCountMap.get(jBinder); |
| if (prc == null) { |
| // The provider has no ref count, no release is needed. |
| return false; |
| } |
| |
| boolean lastRef = false; |
| if (stable) { |
| if (prc.stableCount == 0) { |
| if (DEBUG_PROVIDER) Slog.v(TAG, |
| "releaseProvider: stable ref count already 0, how?"); |
| return false; |
| } |
| prc.stableCount -= 1; |
| if (prc.stableCount == 0) { |
| // What we do at this point depends on whether there are |
| // any unstable refs left: if there are, we just tell the |
| // activity manager to decrement its stable count; if there |
| // aren't, we need to enqueue this provider to be removed, |
| // and convert to holding a single unstable ref while |
| // doing so. |
| lastRef = prc.unstableCount == 0; |
| try { |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "releaseProvider: No longer stable w/lastRef=" |
| + lastRef + " - " + prc.holder.info.name); |
| } |
| ActivityManager.getService().refContentProvider( |
| prc.holder.connection, -1, lastRef ? 1 : 0); |
| } catch (RemoteException e) { |
| //do nothing content provider object is dead any way |
| } |
| } |
| } else { |
| if (prc.unstableCount == 0) { |
| if (DEBUG_PROVIDER) Slog.v(TAG, |
| "releaseProvider: unstable ref count already 0, how?"); |
| return false; |
| } |
| prc.unstableCount -= 1; |
| if (prc.unstableCount == 0) { |
| // If this is the last reference, we need to enqueue |
| // this provider to be removed instead of telling the |
| // activity manager to remove it at this point. |
| lastRef = prc.stableCount == 0; |
| if (!lastRef) { |
| try { |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "releaseProvider: No longer unstable - " |
| + prc.holder.info.name); |
| } |
| ActivityManager.getService().refContentProvider( |
| prc.holder.connection, 0, -1); |
| } catch (RemoteException e) { |
| //do nothing content provider object is dead any way |
| } |
| } |
| } |
| } |
| |
| if (lastRef) { |
| if (!prc.removePending) { |
| // Schedule the actual remove asynchronously, since we don't know the context |
| // this will be called in. |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "releaseProvider: Enqueueing pending removal - " |
| + prc.holder.info.name); |
| } |
| prc.removePending = true; |
| Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc); |
| mH.sendMessageDelayed(msg, CONTENT_PROVIDER_RETAIN_TIME); |
| } else { |
| Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name); |
| } |
| } |
| return true; |
| } |
| } |
| |
| final void completeRemoveProvider(ProviderRefCount prc) { |
| synchronized (mProviderMap) { |
| if (!prc.removePending) { |
| // There was a race! Some other client managed to acquire |
| // the provider before the removal was completed. |
| // Abort the removal. We will do it later. |
| if (DEBUG_PROVIDER) Slog.v(TAG, "completeRemoveProvider: lost the race, " |
| + "provider still in use"); |
| return; |
| } |
| |
| // More complicated race!! Some client managed to acquire the |
| // provider and release it before the removal was completed. |
| // Continue the removal, and abort the next remove message. |
| prc.removePending = false; |
| |
| final IBinder jBinder = prc.holder.provider.asBinder(); |
| ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder); |
| if (existingPrc == prc) { |
| mProviderRefCountMap.remove(jBinder); |
| } |
| |
| for (int i=mProviderMap.size()-1; i>=0; i--) { |
| ProviderClientRecord pr = mProviderMap.valueAt(i); |
| IBinder myBinder = pr.mProvider.asBinder(); |
| if (myBinder == jBinder) { |
| mProviderMap.removeAt(i); |
| } |
| } |
| } |
| |
| try { |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "removeProvider: Invoking ActivityManagerService." |
| + "removeContentProvider(" + prc.holder.info.name + ")"); |
| } |
| ActivityManager.getService().removeContentProvider( |
| prc.holder.connection, false); |
| } catch (RemoteException e) { |
| //do nothing content provider object is dead any way |
| } |
| } |
| |
| @UnsupportedAppUsage |
| final void handleUnstableProviderDied(IBinder provider, boolean fromClient) { |
| synchronized (mProviderMap) { |
| handleUnstableProviderDiedLocked(provider, fromClient); |
| } |
| } |
| |
| final void handleUnstableProviderDiedLocked(IBinder provider, boolean fromClient) { |
| ProviderRefCount prc = mProviderRefCountMap.get(provider); |
| if (prc != null) { |
| if (DEBUG_PROVIDER) Slog.v(TAG, "Cleaning up dead provider " |
| + provider + " " + prc.holder.info.name); |
| mProviderRefCountMap.remove(provider); |
| for (int i=mProviderMap.size()-1; i>=0; i--) { |
| ProviderClientRecord pr = mProviderMap.valueAt(i); |
| if (pr != null && pr.mProvider.asBinder() == provider) { |
| Slog.i(TAG, "Removing dead content provider:" + pr.mProvider.toString()); |
| mProviderMap.removeAt(i); |
| } |
| } |
| |
| if (fromClient) { |
| // We found out about this due to execution in our client |
| // code. Tell the activity manager about it now, to ensure |
| // that the next time we go to do anything with the provider |
| // it knows it is dead (so we don't race with its death |
| // notification). |
| try { |
| ActivityManager.getService().unstableProviderDied( |
| prc.holder.connection); |
| } catch (RemoteException e) { |
| //do nothing content provider object is dead any way |
| } |
| } |
| } |
| } |
| |
| final void appNotRespondingViaProvider(IBinder provider) { |
| synchronized (mProviderMap) { |
| ProviderRefCount prc = mProviderRefCountMap.get(provider); |
| if (prc != null) { |
| try { |
| ActivityManager.getService() |
| .appNotRespondingViaProvider(prc.holder.connection); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| } |
| } |
| |
| private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider, |
| ContentProvider localProvider, ContentProviderHolder holder) { |
| final String auths[] = holder.info.authority.split(";"); |
| final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid); |
| |
| if (provider != null) { |
| // If this provider is hosted by the core OS and cannot be upgraded, |
| // then I guess we're okay doing blocking calls to it. |
| for (String auth : auths) { |
| switch (auth) { |
| case ContactsContract.AUTHORITY: |
| case CallLog.AUTHORITY: |
| case CallLog.SHADOW_AUTHORITY: |
| case BlockedNumberContract.AUTHORITY: |
| case CalendarContract.AUTHORITY: |
| case Downloads.Impl.AUTHORITY: |
| case "telephony": |
| Binder.allowBlocking(provider.asBinder()); |
| } |
| } |
| } |
| |
| final ProviderClientRecord pcr = new ProviderClientRecord( |
| auths, provider, localProvider, holder); |
| for (String auth : auths) { |
| final ProviderKey key = new ProviderKey(auth, userId); |
| final ProviderClientRecord existing = mProviderMap.get(key); |
| if (existing != null) { |
| Slog.w(TAG, "Content provider " + pcr.mHolder.info.name |
| + " already published as " + auth); |
| } else { |
| mProviderMap.put(key, pcr); |
| } |
| } |
| return pcr; |
| } |
| |
| /** |
| * Installs the provider. |
| * |
| * Providers that are local to the process or that come from the system server |
| * may be installed permanently which is indicated by setting noReleaseNeeded to true. |
| * Other remote providers are reference counted. The initial reference count |
| * for all reference counted providers is one. Providers that are not reference |
| * counted do not have a reference count (at all). |
| * |
| * This method detects when a provider has already been installed. When this happens, |
| * it increments the reference count of the existing provider (if appropriate) |
| * and returns the existing provider. This can happen due to concurrent |
| * attempts to acquire the same provider. |
| */ |
| @UnsupportedAppUsage |
| private ContentProviderHolder installProvider(Context context, |
| ContentProviderHolder holder, ProviderInfo info, |
| boolean noisy, boolean noReleaseNeeded, boolean stable) { |
| ContentProvider localProvider = null; |
| IContentProvider provider; |
| if (holder == null || holder.provider == null) { |
| if (DEBUG_PROVIDER || noisy) { |
| Slog.d(TAG, "Loading provider " + info.authority + ": " |
| + info.name); |
| } |
| Context c = null; |
| ApplicationInfo ai = info.applicationInfo; |
| if (context.getPackageName().equals(ai.packageName)) { |
| c = context; |
| } else if (mInitialApplication != null && |
| mInitialApplication.getPackageName().equals(ai.packageName)) { |
| c = mInitialApplication; |
| } else { |
| try { |
| c = context.createPackageContext(ai.packageName, |
| Context.CONTEXT_INCLUDE_CODE); |
| } catch (PackageManager.NameNotFoundException e) { |
| // Ignore |
| } |
| } |
| if (c == null) { |
| Slog.w(TAG, "Unable to get context for package " + |
| ai.packageName + |
| " while loading content provider " + |
| info.name); |
| return null; |
| } |
| |
| if (info.splitName != null) { |
| try { |
| c = c.createContextForSplit(info.splitName); |
| } catch (NameNotFoundException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| if (info.attributionTags != null && info.attributionTags.length > 0) { |
| final String attributionTag = info.attributionTags[0]; |
| c = c.createAttributionContext(attributionTag); |
| } |
| |
| try { |
| final java.lang.ClassLoader cl = c.getClassLoader(); |
| LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); |
| if (packageInfo == null) { |
| // System startup case. |
| packageInfo = getSystemContext().mPackageInfo; |
| } |
| localProvider = packageInfo.getAppFactory() |
| .instantiateProvider(cl, info.name); |
| provider = localProvider.getIContentProvider(); |
| if (provider == null) { |
| Slog.e(TAG, "Failed to instantiate class " + |
| info.name + " from sourceDir " + |
| info.applicationInfo.sourceDir); |
| return null; |
| } |
| if (DEBUG_PROVIDER) Slog.v( |
| TAG, "Instantiating local provider " + info.name); |
| // XXX Need to create the correct context for this provider. |
| localProvider.attachInfo(c, info); |
| } catch (java.lang.Exception e) { |
| if (!mInstrumentation.onException(null, e)) { |
| throw new RuntimeException( |
| "Unable to get provider " + info.name |
| + ": " + e.toString(), e); |
| } |
| return null; |
| } |
| } else { |
| provider = holder.provider; |
| if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": " |
| + info.name); |
| } |
| |
| ContentProviderHolder retHolder; |
| |
| synchronized (mProviderMap) { |
| if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider |
| + " / " + info.name); |
| IBinder jBinder = provider.asBinder(); |
| if (localProvider != null) { |
| ComponentName cname = new ComponentName(info.packageName, info.name); |
| ProviderClientRecord pr = mLocalProvidersByName.get(cname); |
| if (pr != null) { |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "installProvider: lost the race, " |
| + "using existing local provider"); |
| } |
| provider = pr.mProvider; |
| } else { |
| holder = new ContentProviderHolder(info); |
| holder.provider = provider; |
| holder.noReleaseNeeded = true; |
| pr = installProviderAuthoritiesLocked(provider, localProvider, holder); |
| mLocalProviders.put(jBinder, pr); |
| mLocalProvidersByName.put(cname, pr); |
| } |
| retHolder = pr.mHolder; |
| } else { |
| ProviderRefCount prc = mProviderRefCountMap.get(jBinder); |
| if (prc != null) { |
| if (DEBUG_PROVIDER) { |
| Slog.v(TAG, "installProvider: lost the race, updating ref count"); |
| } |
| // We need to transfer our new reference to the existing |
| // ref count, releasing the old one... but only if |
| // release is needed (that is, it is not running in the |
| // system process). |
| if (!noReleaseNeeded) { |
| incProviderRefLocked(prc, stable); |
| try { |
| ActivityManager.getService().removeContentProvider( |
| holder.connection, stable); |
| } catch (RemoteException e) { |
| //do nothing content provider object is dead any way |
| } |
| } |
| } else { |
| ProviderClientRecord client = installProviderAuthoritiesLocked( |
| provider, localProvider, holder); |
| if (noReleaseNeeded) { |
| prc = new ProviderRefCount(holder, client, 1000, 1000); |
| } else { |
| prc = stable |
| ? new ProviderRefCount(holder, client, 1, 0) |
| : new ProviderRefCount(holder, client, 0, 1); |
| } |
| mProviderRefCountMap.put(jBinder, prc); |
| } |
| retHolder = prc.holder; |
| } |
| } |
| return retHolder; |
| } |
| |
| private void handleRunIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) { |
| try { |
| Method main = Class.forName(entryPoint).getMethod("main", String[].class); |
| main.invoke(null, new Object[]{entryPointArgs}); |
| } catch (ReflectiveOperationException e) { |
| throw new AndroidRuntimeException("runIsolatedEntryPoint failed", e); |
| } |
| // The process will be empty after this method returns; exit the VM now. |
| System.exit(0); |
| } |
| |
| @UnsupportedAppUsage |
| private void attach(boolean system, long startSeq) { |
| sCurrentActivityThread = this; |
| mConfigurationController = new ConfigurationController(this); |
| mSystemThread = system; |
| mStartSeq = startSeq; |
| mDdmSyncStageUpdater.next(Stage.Attach); |
| |
| if (!system) { |
| android.ddm.DdmHandleAppName.setAppName("<pre-initialized>", |
| UserHandle.myUserId()); |
| RuntimeInit.setApplicationObject(mAppThread.asBinder()); |
| final IActivityManager mgr = ActivityManager.getService(); |
| try { |
| mgr.attachApplication(mAppThread, startSeq); |
| } catch (RemoteException ex) { |
| throw ex.rethrowFromSystemServer(); |
| } |
| // Watch for getting close to heap limit. |
| BinderInternal.addGcWatcher(new Runnable() { |
| @Override public void run() { |
| if (!mSomeActivitiesChanged) { |
| return; |
| } |
| Runtime runtime = Runtime.getRuntime(); |
| long dalvikMax = runtime.maxMemory(); |
| long dalvikUsed = runtime.totalMemory() - runtime.freeMemory(); |
| if (dalvikUsed > ((3*dalvikMax)/4)) { |
| if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024) |
| + " total=" + (runtime.totalMemory()/1024) |
| + " used=" + (dalvikUsed/1024)); |
| mSomeActivitiesChanged = false; |
| try { |
| ActivityTaskManager.getService().releaseSomeActivities(mAppThread); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| } |
| }); |
| } else { |
| // Don't set application object here -- if the system crashes, |
| // we can't display an alert, we just want to die die die. |
| android.ddm.DdmHandleAppName.setAppName("system_process", |
| UserHandle.myUserId()); |
| try { |
| mInstrumentation = new Instrumentation(); |
| mInstrumentation.basicInit(this); |
| ContextImpl context = ContextImpl.createAppContext( |
| this, getSystemContext().mPackageInfo); |
| mInitialApplication = context.mPackageInfo.makeApplicationInner(true, null); |
| mInitialApplication.onCreate(); |
| } catch (Exception e) { |
| throw new RuntimeException( |
| "Unable to instantiate Application():" + e.toString(), e); |
| } |
| } |
| |
| ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> { |
| synchronized (mResourcesManager) { |
| // We need to apply this change to the resources immediately, because upon returning |
| // the view hierarchy will be informed about it. |
| if (mResourcesManager.applyConfigurationToResources(globalConfig, |
| null /* compat */)) { |
| mConfigurationController.updateLocaleListFromAppContext( |
| mInitialApplication.getApplicationContext()); |
| |
| // This actually changed the resources! Tell everyone about it. |
| final Configuration updatedConfig = |
| mConfigurationController.updatePendingConfiguration(globalConfig); |
| if (updatedConfig != null) { |
| sendMessage(H.CONFIGURATION_CHANGED, globalConfig); |
| mPendingConfiguration = updatedConfig; |
| } |
| } |
| } |
| }; |
| ViewRootImpl.addConfigCallback(configChangedCallback); |
| } |
| |
| @UnsupportedAppUsage |
| public static ActivityThread systemMain() { |
| ThreadedRenderer.initForSystemProcess(); |
| ActivityThread thread = new ActivityThread(); |
| thread.attach(true, 0); |
| return thread; |
| } |
| |
| public static void updateHttpProxy(@NonNull Context context) { |
| final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class); |
| Proxy.setHttpProxyConfiguration(cm.getDefaultProxy()); |
| } |
| |
| @UnsupportedAppUsage |
| public final void installSystemProviders(List<ProviderInfo> providers) { |
| if (providers != null) { |
| installContentProviders(mInitialApplication, providers); |
| } |
| } |
| |
| /** |
| * Caller should NEVER mutate the Bundle returned from here |
| */ |
| Bundle getCoreSettings() { |
| synchronized (mCoreSettingsLock) { |
| return mCoreSettings; |
| } |
| } |
| |
| public int getIntCoreSetting(String key, int defaultValue) { |
| synchronized (mCoreSettingsLock) { |
| if (mCoreSettings != null) { |
| return mCoreSettings.getInt(key, defaultValue); |
| } |
| return defaultValue; |
| } |
| } |
| |
| /** |
| * Get the string value of the given key from core settings. |
| */ |
| public String getStringCoreSetting(String key, String defaultValue) { |
| synchronized (mCoreSettingsLock) { |
| if (mCoreSettings != null) { |
| return mCoreSettings.getString(key, defaultValue); |
| } |
| return defaultValue; |
| } |
| } |
| |
| float getFloatCoreSetting(String key, float defaultValue) { |
| synchronized (mCoreSettingsLock) { |
| if (mCoreSettings != null) { |
| return mCoreSettings.getFloat(key, defaultValue); |
| } |
| return defaultValue; |
| } |
| } |
| |
| private static class AndroidOs extends ForwardingOs { |
| /** |
| * Install selective syscall interception. For example, this is used to |
| * implement special filesystem paths that will be redirected to |
| * {@link ContentResolver#openFileDescriptor(Uri, String)}. |
| */ |
| public static void install() { |
| // If feature is disabled, we don't need to install |
| if (!DEPRECATE_DATA_COLUMNS) return; |
| |
| // Install interception and make sure it sticks! |
| Os def; |
| do { |
| def = Os.getDefault(); |
| } while (!Os.compareAndSetDefault(def, new AndroidOs(def))); |
| } |
| |
| private AndroidOs(Os os) { |
| super(os); |
| } |
| |
| private FileDescriptor openDeprecatedDataPath(String path, int mode) throws ErrnoException { |
| final Uri uri = ContentResolver.translateDeprecatedDataPath(path); |
| Log.v(TAG, "Redirecting " + path + " to " + uri); |
| |
| final ContentResolver cr = currentActivityThread().getApplication() |
| .getContentResolver(); |
| try { |
| final FileDescriptor fd = new FileDescriptor(); |
| fd.setInt$(cr.openFileDescriptor(uri, |
| FileUtils.translateModePosixToString(mode)).detachFd()); |
| return fd; |
| } catch (SecurityException e) { |
| throw new ErrnoException(e.getMessage(), OsConstants.EACCES); |
| } catch (FileNotFoundException e) { |
| throw new ErrnoException(e.getMessage(), OsConstants.ENOENT); |
| } |
| } |
| |
| private void deleteDeprecatedDataPath(String path) throws ErrnoException { |
| final Uri uri = ContentResolver.translateDeprecatedDataPath(path); |
| Log.v(TAG, "Redirecting " + path + " to " + uri); |
| |
| final ContentResolver cr = currentActivityThread().getApplication() |
| .getContentResolver(); |
| try { |
| if (cr.delete(uri, null, null) == 0) { |
| throw new FileNotFoundException(); |
| } |
| } catch (SecurityException e) { |
| throw new ErrnoException(e.getMessage(), OsConstants.EACCES); |
| } catch (FileNotFoundException e) { |
| throw new ErrnoException(e.getMessage(), OsConstants.ENOENT); |
| } |
| } |
| |
| @Override |
| public boolean access(String path, int mode) throws ErrnoException { |
| if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { |
| // If we opened it okay, then access check succeeded |
| IoUtils.closeQuietly( |
| openDeprecatedDataPath(path, FileUtils.translateModeAccessToPosix(mode))); |
| return true; |
| } else { |
| return super.access(path, mode); |
| } |
| } |
| |
| @Override |
| public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { |
| if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { |
| return openDeprecatedDataPath(path, mode); |
| } else { |
| return super.open(path, flags, mode); |
| } |
| } |
| |
| @Override |
| public StructStat stat(String path) throws ErrnoException { |
| if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { |
| final FileDescriptor fd = openDeprecatedDataPath(path, OsConstants.O_RDONLY); |
| try { |
| return android.system.Os.fstat(fd); |
| } finally { |
| IoUtils.closeQuietly(fd); |
| } |
| } else { |
| return super.stat(path); |
| } |
| } |
| |
| @Override |
| public void unlink(String path) throws ErrnoException { |
| if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { |
| deleteDeprecatedDataPath(path); |
| } else { |
| super.unlink(path); |
| } |
| } |
| |
| @Override |
| public void remove(String path) throws ErrnoException { |
| if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) { |
| deleteDeprecatedDataPath(path); |
| } else { |
| super.remove(path); |
| } |
| } |
| |
| @Override |
| public void rename(String oldPath, String newPath) throws ErrnoException { |
| try { |
| super.rename(oldPath, newPath); |
| } catch (ErrnoException e) { |
| // On emulated volumes, we have bind mounts for /Android/data and |
| // /Android/obb, which prevents move from working across those directories |
| // and other directories on the filesystem. To work around that, try to |
| // recover by doing a copy instead. |
| // Note that we only do this for "/storage/emulated", because public volumes |
| // don't have these bind mounts, neither do private volumes that are not |
| // the primary storage. |
| if (e.errno == OsConstants.EXDEV && oldPath.startsWith("/storage/emulated") |
| && newPath.startsWith("/storage/emulated")) { |
| Log.v(TAG, "Recovering failed rename " + oldPath + " to " + newPath); |
| try { |
| Files.move(new File(oldPath).toPath(), new File(newPath).toPath(), |
| StandardCopyOption.REPLACE_EXISTING); |
| } catch (IOException e2) { |
| Log.e(TAG, "Rename recovery failed ", e2); |
| throw e; |
| } |
| } else { |
| throw e; |
| } |
| } |
| } |
| } |
| |
| public static void main(String[] args) { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); |
| |
| // Install selective syscall interception |
| AndroidOs.install(); |
| |
| // CloseGuard defaults to true and can be quite spammy. We |
| // disable it here, but selectively enable it later (via |
| // StrictMode) on debug builds, but using DropBox, not logs. |
| CloseGuard.setEnabled(false); |
| |
| Environment.initForCurrentUser(); |
| |
| // Make sure TrustedCertificateStore looks in the right place for CA certificates |
| final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); |
| TrustedCertificateStore.setDefaultUserDirectory(configDir); |
| |
| // Call per-process mainline module initialization. |
| initializeMainlineModules(); |
| |
| Process.setArgV0("<pre-initialized>"); |
| |
| Looper.prepareMainLooper(); |
| |
| // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line. |
| // It will be in the format "seq=114" |
| long startSeq = 0; |
| if (args != null) { |
| for (int i = args.length - 1; i >= 0; --i) { |
| if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) { |
| startSeq = Long.parseLong( |
| args[i].substring(PROC_START_SEQ_IDENT.length())); |
| } |
| } |
| } |
| ActivityThread thread = new ActivityThread(); |
| thread.attach(false, startSeq); |
| |
| if (sMainThreadHandler == null) { |
| sMainThreadHandler = thread.getHandler(); |
| } |
| |
| if (false) { |
| Looper.myLooper().setMessageLogging(new |
| LogPrinter(Log.DEBUG, "ActivityThread")); |
| } |
| |
| // End of event ActivityThreadMain. |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| Looper.loop(); |
| |
| throw new RuntimeException("Main thread loop unexpectedly exited"); |
| } |
| |
| /** |
| * Call various initializer APIs in mainline modules that need to be called when each process |
| * starts. |
| */ |
| public static void initializeMainlineModules() { |
| TelephonyFrameworkInitializer.setTelephonyServiceManager(new TelephonyServiceManager()); |
| StatsFrameworkInitializer.setStatsServiceManager(new StatsServiceManager()); |
| MediaFrameworkPlatformInitializer.setMediaServiceManager(new MediaServiceManager()); |
| MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager()); |
| BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager()); |
| BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> { |
| BinderCallsStats.startForBluetooth(context); |
| }); |
| NfcFrameworkInitializer.setNfcServiceManager(new NfcServiceManager()); |
| DeviceConfigInitializer.setDeviceConfigServiceManager(new DeviceConfigServiceManager()); |
| SeFrameworkInitializer.setSeServiceManager(new SeServiceManager()); |
| if (android.server.Flags.telemetryApisService()) { |
| ProfilingFrameworkInitializer.setProfilingServiceManager(new ProfilingServiceManager()); |
| } |
| } |
| |
| private void purgePendingResources() { |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "purgePendingResources"); |
| nPurgePendingResources(); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| } |
| |
| /** |
| * Returns whether the provided {@link ActivityInfo} {@code ai} is a protected component. |
| * |
| * @see #isProtectedComponent(ComponentInfo, String) |
| */ |
| public static boolean isProtectedComponent(@NonNull ActivityInfo ai) { |
| return isProtectedComponent(ai, ai.permission); |
| } |
| |
| /** |
| * Returns whether the provided {@link ServiceInfo} {@code si} is a protected component. |
| * |
| * @see #isProtectedComponent(ComponentInfo, String) |
| */ |
| public static boolean isProtectedComponent(@NonNull ServiceInfo si) { |
| return isProtectedComponent(si, si.permission); |
| } |
| |
| /** |
| * Returns whether the provided {@link ComponentInfo} {@code ci} with the specified {@code |
| * permission} is a protected component. |
| * |
| * <p>A component is protected if it is not exported, or if the specified {@code permission} is |
| * a signature permission. |
| */ |
| private static boolean isProtectedComponent(@NonNull ComponentInfo ci, |
| @Nullable String permission) { |
| // Bail early when this process isn't looking for violations |
| if (!StrictMode.vmUnsafeIntentLaunchEnabled()) return false; |
| |
| // TODO: consider optimizing by having AMS pre-calculate this value |
| if (!ci.exported) { |
| return true; |
| } |
| if (permission != null) { |
| try { |
| PermissionInfo pi = getPermissionManager().getPermissionInfo(permission, |
| currentOpPackageName(), 0); |
| return (pi != null) && pi.getProtection() == PermissionInfo.PROTECTION_SIGNATURE; |
| } catch (RemoteException ignored) { |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns whether the action within the provided {@code intent} is a protected broadcast. |
| */ |
| public static boolean isProtectedBroadcast(@NonNull Intent intent) { |
| // Bail early when this process isn't looking for violations |
| if (!StrictMode.vmUnsafeIntentLaunchEnabled()) return false; |
| |
| // TODO: consider optimizing by having AMS pre-calculate this value |
| try { |
| return getPackageManager().isProtectedBroadcast(intent.getAction()); |
| } catch (RemoteException ignored) { |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isInDensityCompatMode() { |
| return mDensityCompatMode; |
| } |
| |
| // ------------------ Regular JNI ------------------------ |
| private native void nPurgePendingResources(); |
| private native void nInitZygoteChildHeapProfiling(); |
| } |