Introduce SystemUI OverviewProxyService
This article based on
AOSP
9.0
From AOSP
9.0, if we click the recents button in navigation bar, the Launcher3
will show its recents view, instead of SystemUI
. In another word, from AOSP
9.0, the SystemUI
provide a method to implement recents out the SystemUI
. And if there is an another implementation, the SystemUI
will use its fallback implementation. The bridge that permits to implement another recents is OverviewProxyService
in SystemUI
.
Initialize OverviewProxyService
Dependency
In Dependency.start
, there is a piece of code to initialize OverviewProxyService
:
mProviders.put(OverviewProxyService.class, () -> new OverviewProxyService(mContext));
And then, other code can use Dependency.get(OverviewProxyService.class)
to get the OverviewProxyService
instance, such as following code snippet in Recents
:
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
OverviewProxyService
In OverviewProxyService
, it get the recents component name from system config:
mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
com.android.internal.R.string.config_recentsComponentName));
The default config_recentsComponentName
is com.android.launcher3/com.android.quickstep.RecentsActivity
, the RecentsActivity
in Launcher3
.
And then use got component name to create quick step service Intent
:
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
Next, the OverviewProxyService
will check the enable state of RecentsActivity
:
private void updateEnabledState() {
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
MATCH_DIRECT_BOOT_UNAWARE,
ActivityManagerWrapper.getInstance().getCurrentUserId()) != null;
}
If the RecentsActivity
is enabled, there are all left things.
NotificationLockscreenUserManager
In NotificationLockscreenuserManager
, if it receives the ACTION_USER_UNLOCKED
broadcast, it will invoke OverviewProxyService.startConnectionToCurrentUser
to start to connect the Launcher3
quick step service:
if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
// Start the overview connection to the launcher service
Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
}
OverviewProxyService
The OverviewProxyService.startConnectionToCurrentUser
will use normal method to bind quick step service:
Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
boolean bound = false;
try {
bound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
} catch (SecurityException e) {
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
If it is failed to bind quick step service, it will try again later; If it is successfully to bind quick step service, it will cast the received IBinder
service to IOverviewProxy
:
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
....
mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
...
try {
mOverviewProxy.onBind(mSysUiProxy);
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call onBind()", e);
}
...
}
};
And then use onBind
method to pass its mSysUiProxy
:
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
....
}
interface ISystemUiProxy {
GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
int maxLayer, boolean useIdentityTransform, int rotation) = 0;
void startScreenPinning(int taskId) = 1;
void setInteractionState(int flags) = 4;
void onSplitScreenInvoked() = 5;
void onOverviewShown(boolean fromHome) = 6;
Rect getNonMinimizedSplitScreenSecondaryBounds() = 7;
void setBackButtonAlpha(float alpha, boolean animate) = 8;
}
Let’ do a summary of definite period, when the user unlocked, the OverviewProxyService
will connect to quick step service, defined in pre-defined RecentsActivity
package, in our occasion is Launcher3
. The quick step service should receive the action android.intent.action.QUICKSTEP_SERVICE
, and return IOverviewProxy
instance as onBind
method result. The OverviewProxyService
will use IOverviewProxy
’s onBind
method to pass ISystemUiProxy
instance to quick step service. In Launcher3
, the quick step service is called TouchInteractionService
. So the sequence of communication is as following graph:
,--------------------. ,-----------------------.
|OverviewProxyService| |TouchInteractionService|
`---------+----------' `-----------+-----------'
| bind service |
| ------------------------------------------------>
| |
| return IOverviewProxy instance |
| <- - - - - - - - - - - - - - - - - - - - - - - -
| |
| use IOverviewProxy.onBind to pass ISystemUiProxy|
| ------------------------------------------------>
,---------+----------. ,-----------+-----------.
|OverviewProxyService| |TouchInteractionService|
`--------------------' `-----------------------'
Now the OverviewProxyService
in SystemUI
has a proxy called IOverviewProxy
to send command to Launcher3
, and TouchInteractionService
in Launcher3
has another proxy called ISysUiProxy
to send command to SystemUI
. What we should dig into is when to communication between them.
Show recents
Despite of using ALT + TAB
, or clicking recents app button in navigation bar to show recents app, they will call the PhoneWindowManager.showRecentApps(boolean)
to show the recents app. And later sequence is as following diagram:
,------------------. ,----------. ,------------. ,-------. ,------------------------------.
|PhoneWindowManager| |IStatusBar| |CommandQueue| |Recents| |IOverviewProxy.onOverviewShown|
`--------+---------' `----+-----' `-----+------' `---+---' `--------------+---------------'
| showRecentsApps(boolean)| | | |
| ------------------------> | | |
| | | | |
| | showRecentApps | | |
| | ---------------------> | |
| | | | |
| | | showRecentApps | |
| | | ------------------->| |
| | | | |
| | | | showRecentApps |
| | | |---------------------------->|
,--------+---------. ,----+-----. ,-----+------. ,---+---. ,--------------+---------------.
|PhoneWindowManager| |IStatusBar| |CommandQueue| |Recents| |IOverviewProxy.onOverviewShown|
`------------------' `----------' `------------' `-------' `------------------------------'
If Recents
finds the existing IOverviewProxy
, it will call the IOverviewProxy.onOverviewShown
and return; otherwise it will show the fallback recents in SystemUI
. Other operation such as hide recents, use the likely sequence.
Set back button alpha
When TouchInteractionService
receives the ISystemUiProxy
, it will pass it to its inner state, such as OverviewInteractionState
. If OverviewInteractionState
wants to set the alpha of back button in navigation bar, it will call the ISystemUiProxy.setBackButtonAlpha
:
private void applyBackButtonAlpha(float alpha, boolean animate) {
if (mISystemUiProxy == null) {
return;
}
try {
mISystemUiProxy.setBackButtonAlpha(alpha, animate);
} catch (RemoteException e) {
Log.w(TAG, "Unable to update overview back button alpha", e);
}
}
In OverviewProxyService
’s ISystemUiProxy
instance, it will response the command at its setBackButtonAlpha
method:
public void setBackButtonAlpha(float alpha, boolean animate) {
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
notifyBackButtonAlphaChanged(alpha, animate);
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
The left thing is SystemUI
’s response to set back button alpha command by set the alpha for back button.
Summary
The core of OverviewProxyService
is to connect to service with the specific action, and exchange the defined proxy instance to communicate with each. When SystemUI
wants to change the state, it will notify the Launcher3
with proxy IOverviewProxy
, and if Launcher3
wants to change the state, it will notify the SystemUI
with proxy ISystemUiProxy
. The left thing is to hook the proxy interface invoking in correct point. The structure is clear and simple, and we can use the similar structure to do the similar things.