Merge "Split attachApplication to more accurately identify app startup"
This commit is contained in:
committed by
Android (Google) Code Review
commit
58f18e79ce
@@ -502,6 +502,7 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
|
||||
@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;
|
||||
|
||||
@@ -6809,6 +6810,14 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
Application app;
|
||||
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
|
||||
final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
|
||||
|
||||
final IActivityManager mgr = ActivityManager.getService();
|
||||
try {
|
||||
mgr.finishAttachApplication(mStartSeq);
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
try {
|
||||
// If the app is being launched for full backup or restore, bring it up in
|
||||
// a restricted environment with the base application class.
|
||||
@@ -7649,6 +7658,8 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
sCurrentActivityThread = this;
|
||||
mConfigurationController = new ConfigurationController(this);
|
||||
mSystemThread = system;
|
||||
mStartSeq = startSeq;
|
||||
|
||||
if (!system) {
|
||||
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
|
||||
UserHandle.myUserId());
|
||||
|
||||
@@ -147,6 +147,7 @@ interface IActivityManager {
|
||||
oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map,
|
||||
boolean abortBroadcast, int flags);
|
||||
void attachApplication(in IApplicationThread app, long startSeq);
|
||||
void finishAttachApplication(long startSeq);
|
||||
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
|
||||
@UnsupportedAppUsage
|
||||
void moveTaskToFront(in IApplicationThread caller, in String callingPackage, int task,
|
||||
@@ -718,8 +719,8 @@ interface IActivityManager {
|
||||
|
||||
/**
|
||||
* Control the app freezer state. Returns true in case of success, false if the operation
|
||||
* didn't succeed (for example, when the app freezer isn't supported).
|
||||
* Handling the freezer state via this method is reentrant, that is it can be
|
||||
* didn't succeed (for example, when the app freezer isn't supported).
|
||||
* Handling the freezer state via this method is reentrant, that is it can be
|
||||
* disabled and re-enabled multiple times in parallel. As long as there's a 1:1 disable to
|
||||
* enable match, the freezer is re-enabled at last enable only.
|
||||
* @param enable set it to true to enable the app freezer, false to disable it.
|
||||
|
||||
@@ -87,6 +87,7 @@ import static android.os.Process.getTotalMemory;
|
||||
import static android.os.Process.isSdkSandboxUid;
|
||||
import static android.os.Process.isThreadInProcess;
|
||||
import static android.os.Process.killProcess;
|
||||
import static android.os.Process.killProcessGroup;
|
||||
import static android.os.Process.killProcessQuiet;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Process.myUid;
|
||||
@@ -952,13 +953,6 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean doRemoveIfNoThreadInternal(int pid, ProcessRecord app) {
|
||||
if (app == null || app.getThread() != null) {
|
||||
return false;
|
||||
}
|
||||
return doRemoveInternal(pid, app);
|
||||
}
|
||||
}
|
||||
|
||||
private final PendingStartActivityUids mPendingStartActivityUids;
|
||||
@@ -990,7 +984,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
* method.
|
||||
*/
|
||||
@GuardedBy("this")
|
||||
void removePidLocked(int pid, ProcessRecord app) {
|
||||
boolean removePidLocked(int pid, ProcessRecord app) {
|
||||
final boolean removed;
|
||||
synchronized (mPidsSelfLocked) {
|
||||
removed = mPidsSelfLocked.doRemoveInternal(pid, app);
|
||||
@@ -1001,26 +995,6 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
}
|
||||
mAtmInternal.onProcessUnMapped(pid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the process record from the map if it doesn't have a thread.
|
||||
* <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
|
||||
* method.
|
||||
*/
|
||||
@GuardedBy("this")
|
||||
private boolean removePidIfNoThreadLocked(ProcessRecord app) {
|
||||
final boolean removed;
|
||||
final int pid = app.getPid();
|
||||
synchronized (mPidsSelfLocked) {
|
||||
removed = mPidsSelfLocked.doRemoveIfNoThreadInternal(pid, app);
|
||||
}
|
||||
if (removed) {
|
||||
synchronized (sActiveProcessInfoSelfLocked) {
|
||||
sActiveProcessInfoSelfLocked.remove(pid);
|
||||
}
|
||||
mAtmInternal.onProcessUnMapped(pid);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@@ -2364,7 +2338,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
mAppErrors = null;
|
||||
mPackageWatchdog = null;
|
||||
mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */);
|
||||
mBatteryStatsService = null;
|
||||
mBatteryStatsService = mInjector.getBatteryStatsService();
|
||||
mHandler = new MainHandler(handlerThread.getLooper());
|
||||
mHandlerThread = handlerThread;
|
||||
mConstants = new ActivityManagerConstants(mContext, this, mHandler);
|
||||
@@ -2379,7 +2353,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
mIntentFirewall = null;
|
||||
mProcessStats = new ProcessStatsService(this, mContext.getCacheDir());
|
||||
mCpHelper = new ContentProviderHelper(this, false);
|
||||
mServices = null;
|
||||
mServices = mInjector.getActiveServices(this);
|
||||
mSystemThread = null;
|
||||
mUiHandler = injector.getUiHandler(null /* service */);
|
||||
mUidObserverController = new UidObserverController(mUiHandler);
|
||||
@@ -4771,7 +4745,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
@GuardedBy("this")
|
||||
void handleProcessStartOrKillTimeoutLocked(ProcessRecord app, boolean isKillTimeout) {
|
||||
final int pid = app.getPid();
|
||||
boolean gone = isKillTimeout || removePidIfNoThreadLocked(app);
|
||||
boolean gone = isKillTimeout || removePidLocked(pid, app);
|
||||
|
||||
if (gone) {
|
||||
if (isKillTimeout) {
|
||||
@@ -4852,7 +4826,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
}
|
||||
|
||||
@GuardedBy("this")
|
||||
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
|
||||
private void attachApplicationLocked(@NonNull IApplicationThread thread,
|
||||
int pid, int callingUid, long startSeq) {
|
||||
|
||||
// Find the application record that is being attached... either via
|
||||
@@ -4917,7 +4891,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
// Ignore exceptions.
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// If this application record is still attached to a previous
|
||||
@@ -4942,7 +4916,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
mProcessList.startProcessLocked(app,
|
||||
new HostingRecord(HostingRecord.HOSTING_TYPE_LINK_FAIL, processName),
|
||||
ZYGOTE_POLICY_FLAG_EMPTY);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
|
||||
@@ -4965,8 +4939,6 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
app.setUnlocked(StorageManager.isUserKeyUnlocked(app.userId));
|
||||
}
|
||||
|
||||
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
|
||||
|
||||
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
|
||||
List<ProviderInfo> providers = normalMode
|
||||
? mCpHelper.generateApplicationProvidersLocked(app)
|
||||
@@ -5132,7 +5104,7 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
app.killLocked("error during bind", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
|
||||
true);
|
||||
handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove this record from the list of starting applications.
|
||||
@@ -5140,91 +5112,6 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
|
||||
"Attach application locked removing on hold: " + app);
|
||||
mProcessesOnHold.remove(app);
|
||||
|
||||
boolean badApp = false;
|
||||
boolean didSomething = false;
|
||||
|
||||
// See if the top visible activity is waiting to run in this process...
|
||||
if (normalMode) {
|
||||
try {
|
||||
didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
|
||||
badApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find any services that should be running in this process...
|
||||
if (!badApp) {
|
||||
try {
|
||||
didSomething |= mServices.attachApplicationLocked(app, processName);
|
||||
checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked");
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
|
||||
badApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a next-broadcast receiver is in this process...
|
||||
if (!badApp) {
|
||||
try {
|
||||
for (BroadcastQueue queue : mBroadcastQueues) {
|
||||
didSomething |= queue.onApplicationAttachedLocked(app);
|
||||
}
|
||||
checkTime(startTime, "attachApplicationLocked: after dispatching broadcasts");
|
||||
} catch (Exception e) {
|
||||
// If the app died trying to launch the receiver we declare it 'bad'
|
||||
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
|
||||
badApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the next backup agent is in this process...
|
||||
if (!badApp && backupTarget != null && backupTarget.app == app) {
|
||||
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
|
||||
"New app is backup target, launching agent for " + app);
|
||||
notifyPackageUse(backupTarget.appInfo.packageName,
|
||||
PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
|
||||
try {
|
||||
thread.scheduleCreateBackupAgent(backupTarget.appInfo,
|
||||
backupTarget.backupMode, backupTarget.userId,
|
||||
backupTarget.backupDestination);
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
|
||||
badApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (badApp) {
|
||||
app.killLocked("error during init", ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
|
||||
true);
|
||||
handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!didSomething) {
|
||||
updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
|
||||
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
|
||||
}
|
||||
|
||||
|
||||
final HostingRecord hostingRecord = app.getHostingRecord();
|
||||
String shortAction = getShortAction(hostingRecord.getAction());
|
||||
FrameworkStatsLog.write(
|
||||
FrameworkStatsLog.PROCESS_START_TIME,
|
||||
app.info.uid,
|
||||
pid,
|
||||
app.info.packageName,
|
||||
FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD,
|
||||
app.getStartElapsedTime(),
|
||||
(int) (bindApplicationTimeMillis - app.getStartUptime()),
|
||||
(int) (SystemClock.uptimeMillis() - app.getStartUptime()),
|
||||
hostingRecord.getType(),
|
||||
hostingRecord.getName(),
|
||||
shortAction,
|
||||
HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()),
|
||||
HostingRecord.getTriggerTypeForStatsd(hostingRecord.getTriggerType()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -5241,6 +5128,143 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private void finishAttachApplicationInner(long startSeq, int uid, int pid) {
|
||||
final long startTime = SystemClock.uptimeMillis();
|
||||
// Find the application record that is being attached... either via
|
||||
// the pid if we are running in multiple processes, or just pull the
|
||||
// next app record if we are emulating process with anonymous threads.
|
||||
final ProcessRecord app;
|
||||
synchronized (mPidsSelfLocked) {
|
||||
app = mPidsSelfLocked.get(pid);
|
||||
}
|
||||
|
||||
if (app != null && app.getStartUid() == uid && app.getStartSeq() == startSeq) {
|
||||
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
|
||||
} else {
|
||||
Slog.wtf(TAG, "Mismatched or missing ProcessRecord: " + app + ". Pid: " + pid
|
||||
+ ". Uid: " + uid);
|
||||
killProcess(pid);
|
||||
killProcessGroup(uid, pid);
|
||||
mProcessList.noteAppKill(pid, uid,
|
||||
ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
|
||||
ApplicationExitInfo.SUBREASON_UNKNOWN,
|
||||
"wrong startSeq");
|
||||
app.killLocked("unexpected process record",
|
||||
ApplicationExitInfo.REASON_OTHER, true);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
final boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
|
||||
final String processName = app.processName;
|
||||
boolean badApp = false;
|
||||
boolean didSomething = false;
|
||||
|
||||
// See if the top visible activity is waiting to run in this process...
|
||||
if (normalMode) {
|
||||
try {
|
||||
didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
|
||||
badApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find any services that should be running in this process...
|
||||
if (!badApp) {
|
||||
try {
|
||||
didSomething |= mServices.attachApplicationLocked(app, processName);
|
||||
checkTime(startTime, "finishAttachApplicationInner: "
|
||||
+ "after mServices.attachApplicationLocked");
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Exception thrown starting services in " + app, e);
|
||||
badApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a next-broadcast receiver is in this process...
|
||||
if (!badApp) {
|
||||
try {
|
||||
for (BroadcastQueue queue : mBroadcastQueues) {
|
||||
didSomething |= queue.onApplicationAttachedLocked(app);
|
||||
}
|
||||
checkTime(startTime, "finishAttachApplicationInner: "
|
||||
+ "after dispatching broadcasts");
|
||||
} catch (Exception e) {
|
||||
// If the app died trying to launch the receiver we declare it 'bad'
|
||||
Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
|
||||
badApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the next backup agent is in this process...
|
||||
final BackupRecord backupTarget = mBackupTargets.get(app.userId);
|
||||
if (!badApp && backupTarget != null && backupTarget.app == app) {
|
||||
if (DEBUG_BACKUP) {
|
||||
Slog.v(TAG_BACKUP,
|
||||
"New app is backup target, launching agent for " + app);
|
||||
}
|
||||
|
||||
notifyPackageUse(backupTarget.appInfo.packageName,
|
||||
PackageManager.NOTIFY_PACKAGE_USE_BACKUP);
|
||||
try {
|
||||
app.getThread().scheduleCreateBackupAgent(backupTarget.appInfo,
|
||||
backupTarget.backupMode, backupTarget.userId,
|
||||
backupTarget.backupDestination);
|
||||
} catch (Exception e) {
|
||||
Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e);
|
||||
badApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (badApp) {
|
||||
app.killLocked("error during init",
|
||||
ApplicationExitInfo.REASON_INITIALIZATION_FAILURE, true);
|
||||
handleAppDiedLocked(app, pid, false, true, false /* fromBinderDied */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!didSomething) {
|
||||
updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
|
||||
checkTime(startTime, "finishAttachApplicationInner: after updateOomAdjLocked");
|
||||
}
|
||||
|
||||
final HostingRecord hostingRecord = app.getHostingRecord();
|
||||
final String shortAction = getShortAction(hostingRecord.getAction());
|
||||
FrameworkStatsLog.write(
|
||||
FrameworkStatsLog.PROCESS_START_TIME,
|
||||
app.info.uid,
|
||||
pid,
|
||||
app.info.packageName,
|
||||
FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD,
|
||||
app.getStartElapsedTime(),
|
||||
(int) (app.getBindApplicationTime() - app.getStartUptime()),
|
||||
(int) (SystemClock.uptimeMillis() - app.getStartUptime()),
|
||||
hostingRecord.getType(),
|
||||
hostingRecord.getName(),
|
||||
shortAction,
|
||||
HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()),
|
||||
HostingRecord.getTriggerTypeForStatsd(hostingRecord.getTriggerType()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void finishAttachApplication(long startSeq) {
|
||||
final int pid = Binder.getCallingPid();
|
||||
final int uid = Binder.getCallingUid();
|
||||
|
||||
if (pid == MY_PID && uid == SYSTEM_UID) {
|
||||
return;
|
||||
}
|
||||
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
finishAttachApplicationInner(startSeq, uid, pid);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last part of the string of an intent's action.
|
||||
*/
|
||||
@@ -18805,6 +18829,21 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
return new ProcessList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link BatteryStatsService} instance
|
||||
*/
|
||||
public BatteryStatsService getBatteryStatsService() {
|
||||
return new BatteryStatsService(mContext, SystemServiceManager.ensureSystemDir(),
|
||||
BackgroundThread.get().getHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ActiveServices} instance
|
||||
*/
|
||||
public ActiveServices getActiveServices(ActivityManagerService service) {
|
||||
return new ActiveServices(service);
|
||||
}
|
||||
|
||||
private boolean ensureHasNetworkManagementInternal() {
|
||||
if (mNmi == null) {
|
||||
mNmi = LocalServices.getService(NetworkManagementInternal.class);
|
||||
|
||||
@@ -2508,7 +2508,7 @@ public final class ProcessList {
|
||||
}
|
||||
|
||||
@GuardedBy("mService")
|
||||
private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
|
||||
String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
|
||||
StringBuilder sb = null;
|
||||
if (app.isKilledByAm()) {
|
||||
if (sb == null) sb = new StringBuilder();
|
||||
|
||||
@@ -199,6 +199,11 @@ class ProcessRecord implements WindowProcessListener {
|
||||
*/
|
||||
private volatile long mStartElapsedTime;
|
||||
|
||||
/**
|
||||
* When the process was sent the bindApplication request
|
||||
*/
|
||||
private volatile long mBindApplicationTime;
|
||||
|
||||
/**
|
||||
* This will be same as {@link #uid} usually except for some apps used during factory testing.
|
||||
*/
|
||||
@@ -739,6 +744,10 @@ class ProcessRecord implements WindowProcessListener {
|
||||
return mStartElapsedTime;
|
||||
}
|
||||
|
||||
long getBindApplicationTime() {
|
||||
return mBindApplicationTime;
|
||||
}
|
||||
|
||||
int getStartUid() {
|
||||
return mStartUid;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.am;
|
||||
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Process.myUid;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.server.DropBoxManagerInternal;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.am.ActivityManagerService.Injector;
|
||||
import com.android.server.appop.AppOpsService;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
import com.android.server.wm.ActivityTaskManagerService;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* Tests to verify process starts are completed or timeout correctly
|
||||
*/
|
||||
@MediumTest
|
||||
@SuppressWarnings("GuardedBy")
|
||||
public class AsyncProcessStartTest {
|
||||
private static final String TAG = "AsyncProcessStartTest";
|
||||
|
||||
private static final String PACKAGE = "com.foo";
|
||||
|
||||
@Rule
|
||||
public final ApplicationExitInfoTest.ServiceThreadRule
|
||||
mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
|
||||
|
||||
private Context mContext;
|
||||
private HandlerThread mHandlerThread;
|
||||
|
||||
@Mock
|
||||
private AppOpsService mAppOpsService;
|
||||
@Mock
|
||||
private DropBoxManagerInternal mDropBoxManagerInt;
|
||||
@Mock
|
||||
private PackageManagerInternal mPackageManagerInt;
|
||||
@Mock
|
||||
private UsageStatsManagerInternal mUsageStatsManagerInt;
|
||||
@Mock
|
||||
private ActivityManagerInternal mActivityManagerInt;
|
||||
@Mock
|
||||
private ActivityTaskManagerInternal mActivityTaskManagerInt;
|
||||
@Mock
|
||||
private BatteryStatsService mBatteryStatsService;
|
||||
|
||||
private ActivityManagerService mRealAms;
|
||||
private ActivityManagerService mAms;
|
||||
|
||||
private ProcessList mRealProcessList = new ProcessList();
|
||||
private ProcessList mProcessList;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
mHandlerThread = new HandlerThread(TAG);
|
||||
mHandlerThread.start();
|
||||
|
||||
LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
|
||||
LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
|
||||
|
||||
LocalServices.removeServiceForTest(PackageManagerInternal.class);
|
||||
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
|
||||
|
||||
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
|
||||
LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInt);
|
||||
|
||||
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
|
||||
LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInt);
|
||||
|
||||
doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
|
||||
doReturn(true).when(mActivityTaskManagerInt).attachApplication(any());
|
||||
doNothing().when(mActivityTaskManagerInt).onProcessMapped(anyInt(), any());
|
||||
|
||||
mRealAms = new ActivityManagerService(
|
||||
new TestInjector(mContext), mServiceThreadRule.getThread());
|
||||
mRealAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
|
||||
mRealAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
|
||||
mRealAms.mAtmInternal = mActivityTaskManagerInt;
|
||||
mRealAms.mPackageManagerInt = mPackageManagerInt;
|
||||
mRealAms.mUsageStatsService = mUsageStatsManagerInt;
|
||||
mRealAms.mProcessesReady = true;
|
||||
mAms = spy(mRealAms);
|
||||
mRealProcessList.mService = mAms;
|
||||
mProcessList = spy(mRealProcessList);
|
||||
|
||||
doAnswer((invocation) -> {
|
||||
Log.v(TAG, "Intercepting isProcStartValidLocked() for "
|
||||
+ Arrays.toString(invocation.getArguments()));
|
||||
return null;
|
||||
}).when(mProcessList).isProcStartValidLocked(any(), anyLong());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mHandlerThread.quit();
|
||||
}
|
||||
|
||||
private class TestInjector extends Injector {
|
||||
TestInjector(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppOpsService getAppOpsService(File file, Handler handler) {
|
||||
return mAppOpsService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Handler getUiHandler(ActivityManagerService service) {
|
||||
return mHandlerThread.getThreadHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessList getProcessList(ActivityManagerService service) {
|
||||
return mRealProcessList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryStatsService getBatteryStatsService() {
|
||||
return mBatteryStatsService;
|
||||
}
|
||||
}
|
||||
|
||||
private ProcessRecord makeActiveProcessRecord(String packageName, boolean wedge)
|
||||
throws Exception {
|
||||
final ApplicationInfo ai = makeApplicationInfo(packageName);
|
||||
return makeActiveProcessRecord(ai, wedge);
|
||||
}
|
||||
|
||||
private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, boolean wedge)
|
||||
throws Exception {
|
||||
final IApplicationThread thread = mock(IApplicationThread.class);
|
||||
final IBinder threadBinder = new Binder();
|
||||
doReturn(threadBinder).when(thread).asBinder();
|
||||
doAnswer((invocation) -> {
|
||||
Log.v(TAG, "Intercepting bindApplication() for "
|
||||
+ Arrays.toString(invocation.getArguments()));
|
||||
if (!wedge) {
|
||||
mRealAms.finishAttachApplication(0);
|
||||
}
|
||||
return null;
|
||||
}).when(thread).bindApplication(
|
||||
any(), any(),
|
||||
any(), any(),
|
||||
any(), any(),
|
||||
any(), any(),
|
||||
any(),
|
||||
any(), anyInt(),
|
||||
anyBoolean(), anyBoolean(),
|
||||
anyBoolean(), anyBoolean(), any(),
|
||||
any(), any(), any(),
|
||||
any(), any(),
|
||||
any(), any(),
|
||||
any(),
|
||||
anyLong(), anyLong());
|
||||
|
||||
final ProcessRecord r = spy(new ProcessRecord(mAms, ai, ai.processName, ai.uid));
|
||||
r.setPid(myPid());
|
||||
r.setStartUid(myUid());
|
||||
r.setHostingRecord(new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST));
|
||||
r.makeActive(thread, mAms.mProcessStats);
|
||||
doNothing().when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean(),
|
||||
anyBoolean());
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static ApplicationInfo makeApplicationInfo(String packageName) {
|
||||
final ApplicationInfo ai = new ApplicationInfo();
|
||||
ai.packageName = packageName;
|
||||
ai.processName = packageName;
|
||||
ai.uid = myUid();
|
||||
return ai;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we don't kill a normal process
|
||||
*/
|
||||
@Test
|
||||
public void testNormal() throws Exception {
|
||||
ProcessRecord app = startProcessAndWait(false);
|
||||
|
||||
verify(app, never()).killLocked(any(), anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that we kill a wedged process after the process start timeout
|
||||
*/
|
||||
@Test
|
||||
public void testWedged() throws Exception {
|
||||
ProcessRecord app = startProcessAndWait(true);
|
||||
|
||||
verify(app).killLocked(any(), anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
private ProcessRecord startProcessAndWait(boolean wedge) throws Exception {
|
||||
final ProcessRecord app = makeActiveProcessRecord(PACKAGE, wedge);
|
||||
final ApplicationInfo appInfo = makeApplicationInfo(PACKAGE);
|
||||
|
||||
mProcessList.handleProcessStartedLocked(app, app.getPid(), /* usingWrapper */ false,
|
||||
/* expectedStartSeq */ 0, /* procAttached */ false);
|
||||
|
||||
app.getThread().bindApplication(PACKAGE, appInfo,
|
||||
null, null,
|
||||
null,
|
||||
null,
|
||||
null, null,
|
||||
null,
|
||||
null, 0,
|
||||
false, false,
|
||||
true, false,
|
||||
null,
|
||||
null, null,
|
||||
null,
|
||||
null, null, null,
|
||||
null, null,
|
||||
0, 0);
|
||||
|
||||
// Sleep until timeout should have triggered
|
||||
SystemClock.sleep(ActivityManagerService.PROC_START_TIMEOUT + 1000);
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user