Do not kill dream process when stopping dream

The DreamController currently force stops the dream with
ActivityTaskManagerService.removeRootTask. Unintuitively, that method
kills the client process if the dream task is the last task in the
process.

We want to keep the dream client process warm and leave it up to the LMK
to decide when to kill it.

In this CL we pass an IAppTask to the DreamController, which allows it
to stop the dream task without killing the process.

Bug: 281960746
Test: atest DreamManagerServiceTests
Test: atest DreamOverlayTest
Test: atest DreamServiceTest

Change-Id: I83f267657812cb776fc65919bc0f5727fdc15b30
This commit is contained in:
Galia Peycheva
2023-06-09 16:06:58 +00:00
parent c7753c320f
commit 2e14118f43
7 changed files with 129 additions and 53 deletions

View File

@@ -24,7 +24,6 @@ import android.app.GrantedUriPermission;
import android.app.IApplicationThread;
import android.app.IActivityClientController;
import android.app.IActivityController;
import android.app.IAppTask;
import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
@@ -107,14 +106,6 @@ interface IActivityTaskManager {
in ProfilerInfo profilerInfo, in Bundle options, int userId);
boolean startNextMatchingActivity(in IBinder callingActivity,
in Intent intent, in Bundle options);
/**
* The DreamActivity has to be started in a special way that does not involve the PackageParser.
* The DreamActivity is a framework component inserted in the dream application process. Hence,
* it is not declared in the application's manifest and cannot be parsed. startDreamActivity
* creates the activity and starts it without reaching out to the PackageParser.
*/
boolean startDreamActivity(in Intent intent);
int startActivityIntentSender(in IApplicationThread caller,
in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent,
in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,

View File

@@ -26,7 +26,6 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.TestApi;
import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1268,9 +1267,7 @@ public class DreamService extends Service implements Window.Callback {
fetchDreamLabel(this, serviceInfo, isPreviewMode));
try {
if (!ActivityTaskManager.getService().startDreamActivity(i)) {
detach();
}
mDreamManager.startDreamActivity(i);
} catch (SecurityException e) {
Log.w(mTag,
"Received SecurityException trying to start DreamActivity. "

View File

@@ -17,6 +17,7 @@
package android.service.dreams;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.IBinder;
@@ -45,4 +46,5 @@ interface IDreamManager {
void setDreamComponentsForUser(int userId, in ComponentName[] componentNames);
void setSystemDreamComponent(in ComponentName componentName);
void registerDreamOverlayService(in ComponentName componentName);
void startDreamActivity(in Intent intent);
}

View File

@@ -16,11 +16,11 @@
package com.android.server.dreams;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import android.app.ActivityTaskManager;
import android.app.BroadcastOptions;
import android.app.IAppTask;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -213,6 +213,27 @@ final class DreamController {
}
}
/**
* Provides an appTask for the dream with token {@code dreamToken}, so that the dream controller
* can stop the dream task when necessary.
*/
void setDreamAppTask(Binder dreamToken, IAppTask appTask) {
if (mCurrentDream == null || mCurrentDream.mToken != dreamToken
|| mCurrentDream.mAppTask != null) {
Slog.e(TAG, "Illegal dream activity start. mCurrentDream.mToken = "
+ mCurrentDream.mToken + ", illegal dreamToken = " + dreamToken
+ ". Ending this dream activity.");
try {
appTask.finishAndRemoveTask();
} catch (RemoteException | RuntimeException e) {
Slog.e(TAG, "Unable to stop illegal dream activity.");
}
return;
}
mCurrentDream.mAppTask = appTask;
}
/**
* Stops dreaming.
*
@@ -303,8 +324,14 @@ final class DreamController {
mSentStartBroadcast = false;
}
mActivityTaskManager.removeRootTasksWithActivityTypes(
new int[] {ACTIVITY_TYPE_DREAM});
if (mCurrentDream != null && mCurrentDream.mAppTask != null) {
// Finish the dream task in case it hasn't finished by itself already.
try {
mCurrentDream.mAppTask.finishAndRemoveTask();
} catch (RemoteException | RuntimeException e) {
Slog.e(TAG, "Unable to stop dream activity.");
}
}
mListener.onDreamStopped(dream.mToken);
}
@@ -364,6 +391,7 @@ final class DreamController {
public final boolean mIsPreviewMode;
public final boolean mCanDoze;
public final int mUserId;
public IAppTask mAppTask;
public PowerManager.WakeLock mWakeLock;
public boolean mBound;

View File

@@ -27,6 +27,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IAppTask;
import android.app.TaskInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -37,6 +38,7 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
@@ -116,6 +118,7 @@ public final class DreamManagerService extends SystemService {
private final PowerManagerInternal mPowerManagerInternal;
private final PowerManager.WakeLock mDozeWakeLock;
private final ActivityTaskManagerInternal mAtmInternal;
private final PackageManagerInternal mPmInternal;
private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
private final DreamUiEventLogger mDreamUiEventLogger;
@@ -216,6 +219,7 @@ public final class DreamManagerService extends SystemService {
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
mAtmInternal = getLocalService(ActivityTaskManagerInternal.class);
mPmInternal = getLocalService(PackageManagerInternal.class);
mUserManager = context.getSystemService(UserManager.class);
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, DOZE_WAKE_LOCK_TAG);
mDozeConfig = new AmbientDisplayConfiguration(mContext);
@@ -1064,6 +1068,64 @@ public final class DreamManagerService extends SystemService {
Binder.restoreCallingIdentity(ident);
}
}
@Override // Binder call
public void startDreamActivity(@NonNull Intent intent) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
// We post here, because startDreamActivity and setDreamAppTask have to run
// synchronously and DreamController#setDreamAppTask has to run on mHandler.
mHandler.post(() -> {
final Binder dreamToken;
final String dreamPackageName;
synchronized (mLock) {
if (mCurrentDream == null) {
Slog.e(TAG, "Attempt to start DreamActivity, but the device is not "
+ "dreaming. Aborting without starting the DreamActivity.");
return;
}
dreamToken = mCurrentDream.token;
dreamPackageName = mCurrentDream.name.getPackageName();
}
if (!canLaunchDreamActivity(dreamPackageName, intent.getPackage(),
callingUid)) {
Slog.e(TAG, "The dream activity can be started only when the device is dreaming"
+ " and only by the active dream package.");
return;
}
final IAppTask appTask = mAtmInternal.startDreamActivity(intent, callingUid,
callingPid);
if (appTask == null) {
Slog.e(TAG, "Could not start dream activity.");
stopDreamInternal(true, "DreamActivity not started");
return;
}
mController.setDreamAppTask(dreamToken, appTask);
});
}
boolean canLaunchDreamActivity(String dreamPackageName, String packageName,
int callingUid) {
if (dreamPackageName == null || packageName == null) {
Slog.e(TAG, "Cannot launch dream activity due to invalid state. dream component= "
+ dreamPackageName + ", packageName=" + packageName);
return false;
}
if (!mPmInternal.isSameApp(packageName, callingUid, UserHandle.getUserId(callingUid))) {
Slog.e(TAG, "Cannot launch dream activity because package="
+ packageName + " does not match callingUid=" + callingUid);
return false;
}
if (packageName.equals(dreamPackageName)) {
return true;
}
Slog.e(TAG, "Dream packageName does not match active dream. Package " + packageName
+ " does not match " + dreamPackageName);
return false;
}
}
private final class LocalService extends DreamManagerInternal {

View File

@@ -23,6 +23,7 @@ import android.app.ActivityManager;
import android.app.AppProtoEnums;
import android.app.BackgroundStartPrivileges;
import android.app.IActivityManager;
import android.app.IAppTask;
import android.app.IApplicationThread;
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
@@ -307,6 +308,12 @@ public abstract class ActivityTaskManagerInternal {
*/
public abstract void notifyActiveDreamChanged(@Nullable ComponentName activeDreamComponent);
/**
* Starts a dream activity in the DreamService's process.
*/
public abstract IAppTask startDreamActivity(@NonNull Intent intent, int callingUid,
int callingPid);
/**
* Set a uid that is allowed to bypass stopped app switches, launching an app
* whenever it wants.

View File

@@ -1466,23 +1466,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return false;
}
private void enforceCallerIsDream(String callerPackageName) {
final long origId = Binder.clearCallingIdentity();
try {
if (!canLaunchDreamActivity(callerPackageName)) {
throw new SecurityException("The dream activity can be started only when the device"
+ " is dreaming and only by the active dream package.");
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public boolean startDreamActivity(@NonNull Intent intent) {
assertPackageMatchesCallingUid(intent.getPackage());
enforceCallerIsDream(intent.getPackage());
private IAppTask startDreamActivityInternal(@NonNull Intent intent, int callingUid,
int callingPid) {
final ActivityInfo a = new ActivityInfo();
a.theme = com.android.internal.R.style.Theme_Dream;
a.exported = true;
@@ -1500,7 +1485,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
synchronized (mGlobalLock) {
final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid());
final WindowProcessController process = mProcessMap.getProcess(callingPid);
a.packageName = process.mInfo.packageName;
a.applicationInfo = process.mInfo;
@@ -1508,26 +1493,25 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
a.uiOptions = process.mInfo.uiOptions;
a.taskAffinity = "android:" + a.packageName + "/dream";
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
try {
getActivityStartController().obtainStarter(intent, "dream")
.setCallingUid(callingUid)
.setCallingPid(callingPid)
.setCallingPackage(intent.getPackage())
.setActivityInfo(a)
.setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
// To start the dream from background, we need to start it from a persistent
// system process. Here we set the real calling uid to the system server uid
.setRealCallingUid(Binder.getCallingUid())
.setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
.execute();
return true;
} finally {
Binder.restoreCallingIdentity(origId);
}
final ActivityRecord[] outActivity = new ActivityRecord[1];
getActivityStartController().obtainStarter(intent, "dream")
.setCallingUid(callingUid)
.setCallingPid(callingPid)
.setCallingPackage(intent.getPackage())
.setActivityInfo(a)
.setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
.setOutActivity(outActivity)
// To start the dream from background, we need to start it from a persistent
// system process. Here we set the real calling uid to the system server uid
.setRealCallingUid(Binder.getCallingUid())
.setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
.execute();
final ActivityRecord started = outActivity[0];
final IAppTask appTask = started == null ? null :
new AppTaskImpl(this, started.getTask().mTaskId, callingUid);
return appTask;
}
}
@@ -5924,6 +5908,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
@Override
public IAppTask startDreamActivity(@NonNull Intent intent, int callingUid, int callingPid) {
return startDreamActivityInternal(intent, callingUid, callingPid);
}
@Override
public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {