diff options
10 files changed, 340 insertions, 0 deletions
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java index 1f5f2e4c8237..5dd7ab0f99fa 100644 --- a/core/java/android/app/HomeVisibilityListener.java +++ b/core/java/android/app/HomeVisibilityListener.java @@ -69,6 +69,11 @@ public abstract class HomeVisibilityListener { public HomeVisibilityListener() { mObserver = new android.app.IProcessObserver.Stub() { @Override + public void onProcessStarted(int pid, int processUid, int packageUid, + String packageName, String processName) { + } + + @Override public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) { refreshHomeVisibility(); } diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl index 7be3620f317b..5c5e72cf9d6f 100644 --- a/core/java/android/app/IProcessObserver.aidl +++ b/core/java/android/app/IProcessObserver.aidl @@ -18,6 +18,17 @@ package android.app; /** {@hide} */ oneway interface IProcessObserver { + /** + * Invoked when an app process starts up. + * + * @param pid The pid of the process. + * @param processUid The UID associated with the process. + * @param packageUid The UID associated with the package. + * @param packageName The name of the package. + * @param processName The name of the process. + */ + void onProcessStarted(int pid, int processUid, int packageUid, + @utf8InCpp String packageName, @utf8InCpp String processName); void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities); void onForegroundServicesChanged(int pid, int uid, int serviceTypes); void onProcessDied(int pid, int uid); diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt index 47bff8de377e..0d1853534927 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt @@ -78,6 +78,14 @@ abstract class TvPipTestBase : PipTestBase(rotationToString(ROTATION_0), ROTATIO uiAutomation.dropShellPermissionIdentity() } + override fun onProcessStarted( + pid: Int, + processUid: Int, + packageUid: Int, + packageName: String, + processName: String + ) {} + override fun onForegroundActivitiesChanged(pid: Int, uid: Int, foreground: Boolean) {} override fun onForegroundServicesChanged(pid: Int, uid: Int, serviceTypes: Int) {} diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 57c52c2cf408..45f657d713ad 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -3754,6 +3754,11 @@ final class ActivityManagerShellCommand extends ShellCommand { } @Override + public void onProcessStarted(int pid, int processUid, int packageUid, String packageName, + String processName) { + } + + @Override public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) { } diff --git a/services/core/java/com/android/server/am/AppFGSTracker.java b/services/core/java/com/android/server/am/AppFGSTracker.java index 1f98aba5bbd7..fb89b8e4f3b4 100644 --- a/services/core/java/com/android/server/am/AppFGSTracker.java +++ b/services/core/java/com/android/server/am/AppFGSTracker.java @@ -102,6 +102,11 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac } @Override + public void onProcessStarted(int pid, int processUid, int packageUid, String packageName, + String processName) { + } + + @Override public void onProcessDied(int pid, int uid) { } }; diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index fa5dbd2543d3..f5c34a5da1c1 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2852,6 +2852,7 @@ public final class ProcessList { ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT); } } + dispatchProcessStarted(app, pid); checkSlow(app.getStartTime(), "startProcess: done updating pids map"); return true; } @@ -4977,6 +4978,22 @@ public final class ProcessList { } } + void dispatchProcessStarted(ProcessRecord app, int pid) { + int i = mProcessObservers.beginBroadcast(); + while (i > 0) { + i--; + final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); + if (observer != null) { + try { + observer.onProcessStarted(pid, app.uid, app.info.uid, + app.info.packageName, app.processName); + } catch (RemoteException e) { + } + } + } + mProcessObservers.finishBroadcast(); + } + void dispatchProcessDied(int pid, int uid) { int i = mProcessObservers.beginBroadcast(); while (i > 0) { diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java index 684d6a0fc596..cdd147a0ec37 100644 --- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java +++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java @@ -177,6 +177,11 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan } @Override + public void onProcessStarted(int pid, int processUid, int packageUid, String packageName, + String processName) { + } + + @Override public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) { } }; diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 6ec6a123a4e7..77cb08bc02bd 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -204,6 +204,10 @@ public final class DeviceStateManagerService extends SystemService { } @Override + public void onProcessStarted(int pid, int processUid, int packageUid, String packageName, + String processName) {} + + @Override public void onProcessDied(int pid, int uid) {} @Override diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 550aed51c8e2..978f46808e3b 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -214,6 +214,11 @@ public final class MediaProjectionManagerService extends SystemService } @Override + public void onProcessStarted(int pid, int processUid, int packageUid, + String packageName, String processName) { + } + + @Override public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) { MediaProjectionManagerService.this.handleForegroundServicesChanged(pid, uid, serviceTypes); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java new file mode 100644 index 000000000000..fcf761fb6607 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java @@ -0,0 +1,275 @@ +/* + * 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.spy; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManagerInternal; +import android.app.IApplicationThread; +import android.app.IProcessObserver; +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.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 that process events are dispatched to process observers. + */ +@MediumTest +@SuppressWarnings("GuardedBy") +public class ProcessObserverTest { + private static final String TAG = "ProcessObserverTest"; + + 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; + + final IProcessObserver mProcessObserver = mock(IProcessObserver.Stub.class); + + @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.mConstants.loadDeviceConfigConstants(); + 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); + + doReturn(mProcessObserver).when(mProcessObserver).asBinder(); + mProcessList.registerProcessObserver(mProcessObserver); + + 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 recentAccessesFile, File storageFile, + 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) + throws Exception { + final ApplicationInfo ai = makeApplicationInfo(packageName); + return makeActiveProcessRecord(ai); + } + + private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai) + 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 (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) { + mRealAms.finishAttachApplication(0); + } + return null; + }).when(thread).bindApplication( + any(), any(), + any(), any(), anyBoolean(), + 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 a process start event is dispatched to process observers. + */ + @Test + public void testNormal() throws Exception { + ProcessRecord app = startProcess(); + verify(mProcessObserver).onProcessStarted( + app.getPid(), app.uid, app.info.uid, PACKAGE, PACKAGE); + } + + private ProcessRecord startProcess() throws Exception { + final ProcessRecord app = makeActiveProcessRecord(PACKAGE); + final ApplicationInfo appInfo = makeApplicationInfo(PACKAGE); + mProcessList.handleProcessStartedLocked(app, app.getPid(), /* usingWrapper */ false, + /* expectedStartSeq */ 0, /* procAttached */ false); + app.getThread().bindApplication(PACKAGE, appInfo, + null, null, false, + null, + null, + null, null, + null, + null, 0, + false, false, + true, false, + null, + null, null, + null, + null, null, null, + null, null, + 0, 0); + return app; + } + + // TODO: [b/302724778] Remove manual JNI load + static { + System.loadLibrary("mockingservicestestjni"); + } +} |