Merge "Move launcher activities to the back when back pressed on root task" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-02-20 01:15:53 +00:00
committed by Android (Google) Code Review
8 changed files with 128 additions and 14 deletions

View File

@@ -400,6 +400,9 @@ android.app.INotificationManager
android.app.IProcessObserver$Stub$Proxy
android.app.IProcessObserver$Stub
android.app.IProcessObserver
android.app.IRequestFinishCallback$Stub$Proxy
android.app.IRequestFinishCallback$Stub
android.app.IRequestFinishCallback
android.app.ISearchManager$Stub$Proxy
android.app.ISearchManager$Stub
android.app.ISearchManager

View File

@@ -156,6 +156,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -3811,6 +3812,22 @@ public class Activity extends ContextThemeWrapper
return false;
}
private static final class RequestFinishCallback extends IRequestFinishCallback.Stub {
private final WeakReference<Activity> mActivityRef;
RequestFinishCallback(WeakReference<Activity> activityRef) {
mActivityRef = activityRef;
}
@Override
public void requestFinish() {
Activity activity = mActivityRef.get();
if (activity != null) {
activity.mHandler.post(activity::finishAfterTransition);
}
}
}
/**
* Called when the activity has detected the user's press of the back
* key. The default implementation simply finishes the current activity,
@@ -3834,7 +3851,8 @@ public class Activity extends ContextThemeWrapper
// Inform activity task manager that the activity received a back press while at the
// root of the task. This call allows ActivityTaskManager to intercept or move the task
// to the back.
ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken);
ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken,
new RequestFinishCallback(new WeakReference<>(this)));
// Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
// be restored now.

View File

@@ -480,9 +480,9 @@ public class ActivityClient {
}
}
void onBackPressedOnTaskRoot(IBinder token) {
void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
try {
getActivityClientController().onBackPressedOnTaskRoot(token);
getActivityClientController().onBackPressedOnTaskRoot(token, callback);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}

View File

@@ -17,6 +17,7 @@
package android.app;
import android.app.ActivityManager;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.Intent;
@@ -141,7 +142,8 @@ interface IActivityClientController {
* Reports that an Activity received a back key press when there were no additional activities
* on the back stack.
*/
oneway void onBackPressedOnTaskRoot(in IBinder token);
oneway void onBackPressedOnTaskRoot(in IBinder activityToken,
in IRequestFinishCallback callback);
/** Reports that the splash screen view has attached to activity. */
oneway void splashScreenAttached(in IBinder token);

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2021 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;
/**
* This callback allows ActivityTaskManager to ask the calling Activity
* to finish in response to a call to onBackPressedOnTaskRoot.
*
* {@hide}
*/
oneway interface IRequestFinishCallback {
void requestFinish();
}

View File

@@ -43,6 +43,7 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityClientController;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
@@ -50,6 +51,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
@@ -1124,24 +1127,78 @@ class ActivityClientController extends IActivityClientController.Stub {
}
@Override
public void onBackPressedOnTaskRoot(IBinder token) {
public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
final long origId = Binder.clearCallingIdentity();
try {
final Intent baseActivityIntent;
final boolean launchedFromHome;
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r == null) {
return;
}
if (r == null) return;
if (mService.mWindowOrganizerController.mTaskOrganizerController
.handleInterceptBackPressedOnTaskRoot(r.getRootTask())) {
// This task is handled by a task organizer that has requested the back pressed
// callback.
} else {
moveActivityTaskToBack(token, false /* nonRoot */);
return;
}
final Intent baseIntent = r.getTask().getBaseIntent();
final boolean activityIsBaseActivity = baseIntent != null
&& r.mActivityComponent.equals(baseIntent.getComponent());
baseActivityIntent = activityIsBaseActivity ? r.intent : null;
launchedFromHome = r.launchedFromHomeProcess;
}
// If the activity is one of the main entry points for the application, then we should
// refrain from finishing the activity and instead move it to the back to keep it in
// memory. The requirements for this are:
// 1. The current activity is the base activity for the task.
// 2. a. If the activity was launched by the home process, we trust that its intent
// was resolved, so we check if the it is a main intent for the application.
// b. Otherwise, we query Package Manager to verify whether the activity is a
// launcher activity for the application.
if (baseActivityIntent != null
&& ((launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent))
|| isLauncherActivity(baseActivityIntent.getComponent()))) {
moveActivityTaskToBack(token, false /* nonRoot */);
return;
}
// The default option for handling the back button is to finish the Activity.
try {
callback.requestFinish();
} catch (RemoteException e) {
Slog.e(TAG, "Failed to invoke request finish callback", e);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
/**
* Queries PackageManager to see if the given activity is one of the main entry point for the
* application. This should not be called with the WM lock held.
*/
@SuppressWarnings("unchecked")
private boolean isLauncherActivity(@NonNull ComponentName activity) {
final Intent queryIntent = new Intent(Intent.ACTION_MAIN);
queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
queryIntent.setPackage(activity.getPackageName());
try {
final ParceledListSlice<ResolveInfo> resolved =
mService.getPackageManager().queryIntentActivities(
queryIntent, null, 0, mContext.getUserId());
if (resolved == null) return false;
for (final ResolveInfo ri : resolved.getList()) {
if (ri.getComponentInfo().getComponentName().equals(activity)) {
return true;
}
}
} catch (RemoteException e) {
Slog.e(TAG, "Failed to query intent activities", e);
}
return false;
}
}

View File

@@ -436,6 +436,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final int launchedFromUid; // always the uid who started the activity.
final String launchedFromPackage; // always the package who started the activity.
final @Nullable String launchedFromFeatureId; // always the feature in launchedFromPackage
final boolean launchedFromHomeProcess; // as per original caller
final Intent intent; // the original intent that generated us
final String shortComponentName; // the short component name of the intent
final String resolvedType; // as per original caller;
@@ -1667,6 +1668,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
launchedFromUid = _launchedFromUid;
launchedFromPackage = _launchedFromPackage;
launchedFromFeatureId = _launchedFromFeature;
launchedFromHomeProcess = _caller != null && _caller.isHomeProcess();
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
componentSpecified = _componentSpecified;

View File

@@ -61,6 +61,7 @@ import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
@@ -976,7 +977,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
assertTrue(stack2.isOrganized());
// Verify a back pressed does not call the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
new IRequestFinishCallback.Default());
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, never()).onBackPressedOnTaskRoot(any());
@@ -986,7 +988,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
stack.mRemoteToken.toWindowContainerToken(), true);
// Verify now that the back press does call the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
new IRequestFinishCallback.Default());
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
@@ -996,7 +999,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
stack.mRemoteToken.toWindowContainerToken(), false);
// Verify now that the back press no longer calls the organizer
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
new IRequestFinishCallback.Default());
// Ensure events dispatch to organizer.
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
@@ -1201,7 +1205,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
mWm.mWindowPlacerLocked.deferLayout();
stack.removeImmediately();
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token);
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token,
new IRequestFinishCallback.Default());
waitUntilHandlersIdle();
ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);