diff options
28 files changed, 391 insertions, 254 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f5ee4672b560..93c0c4d7a024 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3406,6 +3406,7 @@ package android.window { method @NonNull public android.window.WindowContainerTransaction createTaskFragment(@NonNull android.window.TaskFragmentCreationParams); method @NonNull public android.window.WindowContainerTransaction deleteTaskFragment(@NonNull android.window.WindowContainerToken); method public int describeContents(); + method @NonNull public android.window.WindowContainerTransaction finishActivity(@NonNull android.os.IBinder); method @NonNull public android.window.WindowContainerTransaction removeTask(@NonNull android.window.WindowContainerToken); method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean); method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean); diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index ffbdf08e99dc..cfad1afe1b5b 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -706,6 +706,23 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Finishes the Activity. + * Comparing to directly calling {@link android.app.Activity#finish()}, calling this can make + * sure the finishing happens in the same transaction with other operations. + * @param activityToken activity to be finished. + */ + @NonNull + public WindowContainerTransaction finishActivity(@NonNull IBinder activityToken) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY) + .setContainer(activityToken) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; + } + + /** * Sets/removes the always on top flag for this {@code windowContainer}. See * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}. * Please note that this method is only intended to be used for a @@ -1163,6 +1180,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18; public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19; public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20; + public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1484,6 +1502,8 @@ public final class WindowContainerTransaction implements Parcelable { + " alwaysOnTop=" + mAlwaysOnTop + "}"; case HIERARCHY_OP_TYPE_REMOVE_TASK: return "{RemoveTask: task=" + mContainer + "}"; + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: + return "{finishActivity: activity=" + mContainer + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop diff --git a/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl b/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl new file mode 100644 index 000000000000..b2236c9f4907 --- /dev/null +++ b/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl @@ -0,0 +1,25 @@ +/* + * 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.internal.app; + +/** + * IPC interface for an application to receive callbacks from the log access dialog callback. + */ +oneway interface ILogAccessDialogCallback { + void approveAccessForClient(int uid, String packageName); + void declineAccessForClient(int uid, String packageName); +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/core/java/com/android/internal/app/LogAccessDialogActivity.java index 811e96ce6d82..4adb8673084b 100644 --- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java +++ b/core/java/com/android/internal/app/LogAccessDialogActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.logcat; +package com.android.internal.app; import android.annotation.StyleRes; import android.app.Activity; @@ -27,7 +27,14 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.RemoteException; import android.os.UserHandle; +import android.text.Html; +import android.text.Spannable; +import android.text.TextUtils; +import android.text.method.LinkMovementMethod; +import android.text.style.TypefaceSpan; +import android.text.style.URLSpan; import android.util.Slog; import android.view.ContextThemeWrapper; import android.view.InflateException; @@ -37,7 +44,6 @@ import android.widget.Button; import android.widget.TextView; import com.android.internal.R; -import com.android.server.LocalServices; /** * Dialog responsible for obtaining user consent per-use log access @@ -45,17 +51,19 @@ import com.android.server.LocalServices; public class LogAccessDialogActivity extends Activity implements View.OnClickListener { private static final String TAG = LogAccessDialogActivity.class.getSimpleName(); + public static final String EXTRA_CALLBACK = "EXTRA_CALLBACK"; + private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000; private static final int MSG_DISMISS_DIALOG = 0; - private final LogcatManagerService.LogcatManagerServiceInternal mLogcatManagerInternal = - LocalServices.getService(LogcatManagerService.LogcatManagerServiceInternal.class); - private String mPackageName; private int mUid; + private ILogAccessDialogCallback mCallback; private String mAlertTitle; + private String mAlertBody; + private String mAlertLearnMore; private AlertDialog.Builder mAlertDialog; private AlertDialog mAlert; private View mAlertView; @@ -81,6 +89,9 @@ public class LogAccessDialogActivity extends Activity implements return; } + mAlertBody = getResources().getString(R.string.log_access_confirmation_body); + mAlertLearnMore = getResources().getString(R.string.log_access_confirmation_learn_more); + // create View boolean isDarkTheme = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; @@ -118,6 +129,13 @@ public class LogAccessDialogActivity extends Activity implements return false; } + mCallback = ILogAccessDialogCallback.Stub.asInterface( + intent.getExtras().getBinder(EXTRA_CALLBACK)); + if (mCallback == null) { + Slog.e(TAG, "Missing callback"); + return false; + } + mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); if (mPackageName == null || mPackageName.length() == 0) { Slog.e(TAG, "Missing package name extra"); @@ -165,13 +183,22 @@ public class LogAccessDialogActivity extends Activity implements return titleString; } + private Spannable styleFont(String text) { + Spannable s = (Spannable) Html.fromHtml(text); + for (URLSpan span : s.getSpans(0, s.length(), URLSpan.class)) { + TypefaceSpan typefaceSpan = new TypefaceSpan("google-sans"); + s.setSpan(typefaceSpan, s.getSpanStart(span), s.getSpanEnd(span), 0); + } + return s; + } + /** * Returns the dialog view. * If we cannot retrieve the package name, it returns null and we decline the full device log * access */ private View createView(@StyleRes int themeId) { - Context themedContext = new ContextThemeWrapper(getApplicationContext(), themeId); + Context themedContext = new ContextThemeWrapper(this, themeId); final View view = LayoutInflater.from(themedContext).inflate( R.layout.log_access_user_consent_dialog_permission, null /*root*/); @@ -182,6 +209,19 @@ public class LogAccessDialogActivity extends Activity implements ((TextView) view.findViewById(R.id.log_access_dialog_title)) .setText(mAlertTitle); + if (!TextUtils.isEmpty(mAlertLearnMore)) { + Spannable mSpannableLearnMore = styleFont(mAlertLearnMore); + + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setText(TextUtils.concat(mAlertBody, "\n\n", mSpannableLearnMore)); + + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setMovementMethod(LinkMovementMethod.getInstance()); + } else { + ((TextView) view.findViewById(R.id.log_access_dialog_body)) + .setText(mAlertBody); + } + Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button); button_allow.setOnClickListener(this); @@ -194,19 +234,27 @@ public class LogAccessDialogActivity extends Activity implements @Override public void onClick(View view) { - switch (view.getId()) { - case R.id.log_access_dialog_allow_button: - mLogcatManagerInternal.approveAccessForClient(mUid, mPackageName); - finish(); - break; - case R.id.log_access_dialog_deny_button: - declineLogAccess(); - finish(); - break; + try { + switch (view.getId()) { + case R.id.log_access_dialog_allow_button: + mCallback.approveAccessForClient(mUid, mPackageName); + finish(); + break; + case R.id.log_access_dialog_deny_button: + declineLogAccess(); + finish(); + break; + } + } catch (RemoteException e) { + finish(); } } private void declineLogAccess() { - mLogcatManagerInternal.declineAccessForClient(mUid, mPackageName); + try { + mCallback.declineAccessForClient(mUid, mPackageName); + } catch (RemoteException e) { + finish(); + } } } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 72de78c148f8..708713b3c0ed 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -29,7 +29,9 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR; @@ -219,6 +221,8 @@ public class InteractionJankMonitor { public static final int CUJ_TASKBAR_EXPAND = 60; public static final int CUJ_TASKBAR_COLLAPSE = 61; public static final int CUJ_SHADE_CLEAR_ALL = 62; + public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63; + public static final int CUJ_LOCKSCREEN_OCCLUSION = 64; private static final int NO_STATSD_LOGGING = -1; @@ -290,6 +294,8 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION, }; private static volatile InteractionJankMonitor sInstance; @@ -372,7 +378,9 @@ public class InteractionJankMonitor { CUJ_USER_DIALOG_OPEN, CUJ_TASKBAR_EXPAND, CUJ_TASKBAR_COLLAPSE, - CUJ_SHADE_CLEAR_ALL + CUJ_SHADE_CLEAR_ALL, + CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION, + CUJ_LOCKSCREEN_OCCLUSION }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -864,6 +872,10 @@ public class InteractionJankMonitor { return "TASKBAR_COLLAPSE"; case CUJ_SHADE_CLEAR_ALL: return "SHADE_CLEAR_ALL"; + case CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION: + return "LAUNCHER_UNLOCK_ENTRANCE_ANIMATION"; + case CUJ_LOCKSCREEN_OCCLUSION: + return "LOCKSCREEN_OCCLUSION"; } return "UNKNOWN"; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b91bd1898a4c..5ae133bbe6e6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6783,8 +6783,9 @@ android:exported="false"> </activity> - <activity android:name="com.android.server.logcat.LogAccessDialogActivity" + <activity android:name="com.android.internal.app.LogAccessDialogActivity" android:theme="@style/Theme.Translucent.NoTitleBar" + android:process=":ui" android:excludeFromRecents="true" android:exported="false"> </activity> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6ec98e82211c..5763345aba4d 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5755,10 +5755,21 @@ <string name="log_access_confirmation_deny">Don\u2019t allow</string> <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]--> - <string name="log_access_confirmation_body">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. + <string name="log_access_confirmation_body" product="default">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device. </string> + <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]--> + <string name="log_access_confirmation_body" product="tv">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs. + \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.\n\nLearn more at g.co/android/devicelogs. + </string> + + <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]--> + <string name="log_access_confirmation_learn_more" product="default" translatable="false"><a href="https://support.google.com/android?p=system_logs#topic=7313011">Learn more</a></string> + + <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]--> + <string name="log_access_confirmation_learn_more" product="tv" translatable="false"></string> + <!-- Privacy notice do not show [CHAR LIMIT=20] --> <string name="log_access_do_not_show_again">Don\u2019t show again</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 546bb21df2a6..6e574bd788a9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3925,8 +3925,10 @@ <java-symbol type="string" name="log_access_confirmation_deny" /> <java-symbol type="string" name="log_access_confirmation_title" /> <java-symbol type="string" name="log_access_confirmation_body" /> + <java-symbol type="string" name="log_access_confirmation_learn_more" /> <java-symbol type="layout" name="log_access_user_consent_dialog_permission" /> <java-symbol type="id" name="log_access_dialog_title" /> + <java-symbol type="id" name="log_access_dialog_body" /> <java-symbol type="id" name="log_access_dialog_allow_button" /> <java-symbol type="id" name="log_access_dialog_deny_button" /> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 626e0d990a6d..18712aed1be6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -432,7 +432,7 @@ class TaskFragmentContainer { // In case we have requested to reparent the activity to another container (as // pendingAppeared), we don't want to finish it with this container. && mController.getContainerWithActivity(activity) == this) { - activity.finish(); + wct.finishActivity(activity.getActivityToken()); } } @@ -457,7 +457,7 @@ class TaskFragmentContainer { || controller.shouldRetainAssociatedActivity(this, activity)) { continue; } - activity.finish(); + wct.finishActivity(activity.getActivityToken()); } mActivitiesToFinishOnExit.clear(); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 179696a063b1..25d034756265 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -207,7 +207,7 @@ public class SplitControllerTest { verify(mSplitPresenter, never()).deleteTaskFragment(any(), any()); verify(mSplitController).removeContainer(tf); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); } @Test @@ -1004,9 +1004,9 @@ public class SplitControllerTest { assertTrue(primaryContainer.isFinished()); assertTrue(secondaryContainer0.isFinished()); assertTrue(secondaryContainer1.isFinished()); - verify(mActivity).finish(); - verify(secondaryActivity0).finish(); - verify(secondaryActivity1).finish(); + verify(mTransaction).finishActivity(mActivity.getActivityToken()); + verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken()); + verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken()); assertTrue(taskContainer.mContainers.isEmpty()); assertTrue(taskContainer.mSplitContainers.isEmpty()); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index 73428a2dc800..35415d816d8b 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -107,30 +107,29 @@ public class TaskFragmentContainerTest { final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, null /* pendingAppearedIntent */, taskContainer, mController); doReturn(container).when(mController).getContainerWithActivity(mActivity); - final WindowContainerTransaction wct = new WindowContainerTransaction(); // Only remove the activity, but not clear the reference until appeared. - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity).finish(); + verify(mTransaction).finishActivity(mActivity.getActivityToken()); verify(mPresenter, never()).deleteTaskFragment(any(), any()); verify(mController, never()).removeContainer(any()); // Calling twice should not finish activity again. - clearInvocations(mActivity); - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + clearInvocations(mTransaction); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); verify(mPresenter, never()).deleteTaskFragment(any(), any()); verify(mController, never()).removeContainer(any()); // Remove all references after the container has appeared in server. doReturn(new ArrayList<>()).when(mInfo).getActivities(); container.setInfo(mTransaction, mInfo); - container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); + container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController); - verify(mActivity, never()).finish(); - verify(mPresenter).deleteTaskFragment(wct, container.getTaskFragmentToken()); + verify(mTransaction, never()).finishActivity(any()); + verify(mPresenter).deleteTaskFragment(mTransaction, container.getTaskFragmentToken()); verify(mController).removeContainer(container); } @@ -150,7 +149,7 @@ public class TaskFragmentContainerTest { // The activity is requested to be reparented, so don't finish it. container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController); - verify(mActivity, never()).finish(); + verify(mTransaction, never()).finishActivity(any()); verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken()); verify(mController).removeContainer(container0); } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index d8995b419f50..891ab45c7a6e 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -48,6 +48,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -118,6 +119,7 @@ public final class MediaRouter2 { private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>(); private final AtomicInteger mNextRequestId = new AtomicInteger(1); + private final AtomicBoolean mIsScanning = new AtomicBoolean(/* initialValue= */ false); final Handler mHandler; @@ -234,7 +236,9 @@ public final class MediaRouter2 { @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) public void startScan() { if (isSystemRouter()) { - sManager.startScan(); + if (!mIsScanning.getAndSet(true)) { + sManager.registerScanRequest(); + } } } @@ -260,7 +264,9 @@ public final class MediaRouter2 { @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) public void stopScan() { if (isSystemRouter()) { - sManager.stopScan(); + if (mIsScanning.getAndSet(false)) { + sManager.unregisterScanRequest(); + } } } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index c84f5b09f166..d79740c2fdff 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -79,9 +79,11 @@ public final class MediaRouter2Manager { final String mPackageName; private final Context mContext; - @GuardedBy("sLock") - private Client mClient; + + private final Client mClient; + private final IMediaRouterService mMediaRouterService; + private final AtomicInteger mScanRequestCount = new AtomicInteger(/* initialValue= */ 0); final Handler mHandler; final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords = new CopyOnWriteArrayList<>(); @@ -119,7 +121,12 @@ public final class MediaRouter2Manager { .getSystemService(Context.MEDIA_SESSION_SERVICE); mPackageName = mContext.getPackageName(); mHandler = new Handler(context.getMainLooper()); - mHandler.post(this::getOrCreateClient); + mClient = new Client(); + try { + mMediaRouterService.registerManager(mClient, mPackageName); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } } /** @@ -155,22 +162,18 @@ public final class MediaRouter2Manager { } /** - * Starts scanning remote routes. - * <p> - * Route discovery can happen even when the {@link #startScan()} is not called. - * This is because the scanning could be started before by other apps. - * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean - * that the routes found before are removed and added again. - * <p> - * Use {@link Callback} to get the route related events. - * <p> - * @see #stopScan() + * Registers a request to scan for remote routes. + * + * <p>Increases the count of active scanning requests. When the count transitions from zero to + * one, sends a request to the system server to start scanning. + * + * <p>Clients must {@link #unregisterScanRequest() unregister their scan requests} when scanning + * is no longer needed, to avoid unnecessary resource usage. */ - public void startScan() { - Client client = getOrCreateClient(); - if (client != null) { + public void registerScanRequest() { + if (mScanRequestCount.getAndIncrement() == 0) { try { - mMediaRouterService.startScan(client); + mMediaRouterService.startScan(mClient); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -178,23 +181,26 @@ public final class MediaRouter2Manager { } /** - * Stops scanning remote routes to reduce resource consumption. - * <p> - * Route discovery can be continued even after this method is called. - * This is because the scanning is only turned off when all the apps stop scanning. - * Therefore, calling this method does not necessarily mean the routes are removed. - * Also, for the same reason it does not mean that {@link Callback#onRoutesAdded(List)} - * is not called afterwards. - * <p> - * Use {@link Callback} to get the route related events. + * Unregisters a scan request made by {@link #registerScanRequest()}. + * + * <p>Decreases the count of active scanning requests. When the count transitions from one to + * zero, sends a request to the system server to stop scanning. * - * @see #startScan() + * @throws IllegalStateException If called while there are no active scan requests. */ - public void stopScan() { - Client client = getOrCreateClient(); - if (client != null) { + public void unregisterScanRequest() { + if (mScanRequestCount.updateAndGet( + count -> { + if (count == 0) { + throw new IllegalStateException( + "No active scan requests to unregister."); + } else { + return --count; + } + }) + == 0) { try { - mMediaRouterService.stopScan(client); + mMediaRouterService.stopScan(mClient); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -358,8 +364,7 @@ public final class MediaRouter2Manager { @Nullable public RoutingSessionInfo getSystemRoutingSession(@Nullable String packageName) { try { - return mMediaRouterService.getSystemSessionInfoForPackage( - getOrCreateClient(), packageName); + return mMediaRouterService.getSystemSessionInfoForPackage(mClient, packageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -423,15 +428,11 @@ public final class MediaRouter2Manager { */ @NonNull public List<RoutingSessionInfo> getRemoteSessions() { - Client client = getOrCreateClient(); - if (client != null) { - try { - return mMediaRouterService.getRemoteSessions(client); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + return mMediaRouterService.getRemoteSessions(mClient); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return Collections.emptyList(); } /** @@ -514,14 +515,11 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.setRouteVolumeWithManager(client, requestId, route, volume); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.setRouteVolumeWithManager(mClient, requestId, route, volume); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -543,15 +541,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.setSessionVolumeWithManager( - client, requestId, sessionInfo.getId(), volume); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.setSessionVolumeWithManager( + mClient, requestId, sessionInfo.getId(), volume); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -808,15 +803,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.selectRouteWithManager( - client, requestId, sessionInfo.getId(), route); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.selectRouteWithManager( + mClient, requestId, sessionInfo.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -850,15 +842,12 @@ public final class MediaRouter2Manager { return; } - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.deselectRouteWithManager( - client, requestId, sessionInfo.getId(), route); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.deselectRouteWithManager( + mClient, requestId, sessionInfo.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -875,15 +864,11 @@ public final class MediaRouter2Manager { public void releaseSession(@NonNull RoutingSessionInfo sessionInfo) { Objects.requireNonNull(sessionInfo, "sessionInfo must not be null"); - Client client = getOrCreateClient(); - if (client != null) { - try { - int requestId = mNextRequestId.getAndIncrement(); - mMediaRouterService.releaseSessionWithManager( - client, requestId, sessionInfo.getId()); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + int requestId = mNextRequestId.getAndIncrement(); + mMediaRouterService.releaseSessionWithManager(mClient, requestId, sessionInfo.getId()); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -896,14 +881,11 @@ public final class MediaRouter2Manager { @NonNull MediaRoute2Info route) { int requestId = createTransferRequest(session, route); - Client client = getOrCreateClient(); - if (client != null) { - try { - mMediaRouterService.transferToRouteWithManager( - client, requestId, session.getId(), route); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + mMediaRouterService.transferToRouteWithManager( + mClient, requestId, session.getId(), route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -916,14 +898,11 @@ public final class MediaRouter2Manager { int requestId = createTransferRequest(oldSession, route); - Client client = getOrCreateClient(); - if (client != null) { - try { - mMediaRouterService.requestCreateSessionWithManager( - client, requestId, oldSession, route); - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } + try { + mMediaRouterService.requestCreateSessionWithManager( + mClient, requestId, oldSession, route); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -967,22 +946,6 @@ public final class MediaRouter2Manager { sessionInfo.getOwnerPackageName()); } - private Client getOrCreateClient() { - synchronized (sLock) { - if (mClient != null) { - return mClient; - } - Client client = new Client(); - try { - mMediaRouterService.registerManager(client, mPackageName); - mClient = client; - return client; - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); - } - } - } - /** * Interface for receiving events about media routing changes. */ diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java index b4aad9df64a7..4086dec99218 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java @@ -39,6 +39,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import android.Manifest; @@ -121,7 +122,7 @@ public class MediaRouter2ManagerTest { MediaRouter2ManagerTestActivity.startActivity(mContext); mManager = MediaRouter2Manager.getInstance(mContext); - mManager.startScan(); + mManager.registerScanRequest(); mRouter2 = MediaRouter2.getInstance(mContext); // If we need to support thread pool executors, change this to thread pool executor. @@ -152,7 +153,7 @@ public class MediaRouter2ManagerTest { @After public void tearDown() { - mManager.stopScan(); + mManager.unregisterScanRequest(); // order matters (callbacks should be cleared at the last) releaseAllSessions(); @@ -818,6 +819,13 @@ public class MediaRouter2ManagerTest { assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); } + @Test + public void unregisterScanRequest_enforcesANonNegativeCount() { + mManager.unregisterScanRequest(); // One request was made in the test setup. + assertThrows(IllegalStateException.class, () -> mManager.unregisterScanRequest()); + mManager.registerScanRequest(); // So that the cleanup doesn't fail. + } + /** * Tests if getSelectableRoutes and getDeselectableRoutes filter routes based on * selected routes diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 58c15eb8073c..7ec0fcdfeb64 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -96,14 +96,14 @@ public class InfoMediaManager extends MediaManager { public void startScan() { mMediaDevices.clear(); mRouterManager.registerCallback(mExecutor, mMediaRouterCallback); - mRouterManager.startScan(); + mRouterManager.registerScanRequest(); refreshDevices(); } @Override public void stopScan() { mRouterManager.unregisterCallback(mMediaRouterCallback); - mRouterManager.stopScan(); + mRouterManager.unregisterScanRequest(); } /** diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java index f0210fd4c914..5d6598d63a1b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java @@ -49,6 +49,8 @@ public final class InteractionJankMonitorWrapper { InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET; public static final int CUJ_SPLIT_SCREEN_ENTER = InteractionJankMonitor.CUJ_SPLIT_SCREEN_ENTER; + public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = + InteractionJankMonitor.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; @IntDef({ CUJ_APP_LAUNCH_FROM_RECENTS, @@ -57,6 +59,7 @@ public final class InteractionJankMonitorWrapper { CUJ_APP_CLOSE_TO_PIP, CUJ_QUICK_SWITCH, CUJ_APP_LAUNCH_FROM_WIDGET, + CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt index f82e7dbd5d12..b78fa9a6b392 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt @@ -52,7 +52,6 @@ data class KeyguardFaceListenModel( val becauseCannotSkipBouncer: Boolean, val biometricSettingEnabledForUser: Boolean, val bouncerFullyShown: Boolean, - val bouncerIsOrWillShow: Boolean, val faceAuthenticated: Boolean, val faceDisabled: Boolean, val faceLockedOut: Boolean, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 32c1cf9802ab..6745cab2fcb5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -2597,7 +2597,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // on bouncer if both fp and fingerprint are enrolled. final boolean awakeKeyguardExcludingBouncerShowing = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep - && !statusBarShadeLocked && !mBouncerIsOrWillBeShowing; + && !statusBarShadeLocked && !mBouncerFullyShown; final int user = getCurrentUser(); final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user); final boolean isLockDown = @@ -2667,7 +2667,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab becauseCannotSkipBouncer, biometricEnabledForUser, mBouncerFullyShown, - mBouncerIsOrWillBeShowing, faceAuthenticated, faceDisabledForUser, isFaceLockedOut(), @@ -3243,8 +3242,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onKeyguardBouncerStateChanged(mBouncerIsOrWillBeShowing); } } - updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, - FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN); + updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } if (wasBouncerFullyShown != mBouncerFullyShown) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 421424033da7..38b98eb45aec 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -22,6 +22,7 @@ import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED; @@ -845,6 +846,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, if (launchIsFullScreen) { mCentralSurfaces.instantCollapseNotificationPanel(); } + + mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION); } @NonNull @@ -991,6 +994,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */); Log.d(TAG, "Unocclude animation cancelled. Occluded state is now: " + mOccluded); + + mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_OCCLUSION); } @Override @@ -999,6 +1004,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { Log.d(TAG, "UnoccludeAnimator#onAnimationStart. Set occluded = false."); + mInteractionJankMonitor.begin( + createInteractionJankMonitorConf(CUJ_LOCKSCREEN_OCCLUSION) + .setTag("UNOCCLUDE")); setOccluded(false /* isOccluded */, true /* animate */); if (apps == null || apps.length == 0 || apps[0] == null) { @@ -1057,6 +1065,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, try { finishedCallback.onAnimationFinished(); mUnoccludeAnimator = null; + + mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION); } catch (RemoteException e) { e.printStackTrace(); } @@ -2573,7 +2583,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, }; try { mInteractionJankMonitor.begin( - createInteractionJankMonitorConf("RunRemoteAnimation")); + createInteractionJankMonitorConf( + CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "RunRemoteAnimation")); runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps, wallpapers, nonApps, callback); } catch (RemoteException e) { @@ -2589,7 +2600,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mSurfaceBehindRemoteAnimationRunning = true; mInteractionJankMonitor.begin( - createInteractionJankMonitorConf("DismissPanel")); + createInteractionJankMonitorConf( + CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel")); // Pass the surface and metadata to the unlock animation controller. mKeyguardUnlockAnimationControllerLazy.get() @@ -2597,7 +2609,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, apps, startTime, mSurfaceBehindRemoteAnimationRequested); } else { mInteractionJankMonitor.begin( - createInteractionJankMonitorConf("RemoteAnimationDisabled")); + createInteractionJankMonitorConf( + CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "RemoteAnimationDisabled")); mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration); @@ -2677,10 +2690,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, sendUserPresentBroadcast(); } - private Configuration.Builder createInteractionJankMonitorConf(String tag) { - return Configuration.Builder.withView(CUJ_LOCKSCREEN_UNLOCK_ANIMATION, - mKeyguardViewControllerLazy.get().getViewRootImpl().getView()) - .setTag(tag); + private Configuration.Builder createInteractionJankMonitorConf(int cuj) { + return createInteractionJankMonitorConf(cuj, null /* tag */); + } + + private Configuration.Builder createInteractionJankMonitorConf(int cuj, @Nullable String tag) { + final Configuration.Builder builder = Configuration.Builder.withView( + cuj, mKeyguardViewControllerLazy.get().getViewRootImpl().getView()); + + return tag != null ? builder.setTag(tag) : builder; } /** @@ -3291,6 +3309,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { super.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); + mInteractionJankMonitor.begin( + createInteractionJankMonitorConf(CUJ_LOCKSCREEN_OCCLUSION) + .setTag("OCCLUDE")); + // This is the first signal we have from WM that we're going to be occluded. Set our // internal state to reflect that immediately, vs. waiting for the launch animator to // begin. Otherwise, calls to setShowingLocked, etc. will not know that we're about to @@ -3307,6 +3329,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, + "Setting occluded state to: " + isKeyguardOccluded); setOccluded(isKeyguardOccluded /* occluded */, false /* animate */); + mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_OCCLUSION); } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 5df0ca25a285..19b401d80600 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -224,15 +224,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, Log.d(TAG, "No media controller for " + mPackageName); } } - if (mLocalMediaManager == null) { - if (DEBUG) { - Log.d(TAG, "No local media manager " + mPackageName); - } - return; - } mCallback = cb; - mLocalMediaManager.unregisterCallback(this); - mLocalMediaManager.stopScan(); mLocalMediaManager.registerCallback(this); mLocalMediaManager.startScan(); } @@ -254,10 +246,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, if (mMediaController != null) { mMediaController.unregisterCallback(mCb); } - if (mLocalMediaManager != null) { - mLocalMediaManager.unregisterCallback(this); - mLocalMediaManager.stopScan(); - } + mLocalMediaManager.unregisterCallback(this); + mLocalMediaManager.stopScan(); synchronized (mMediaDevicesLock) { mCachedMediaDevices.clear(); mMediaDevices.clear(); @@ -661,10 +651,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mLocalMediaManager.getCurrentConnectedDevice(); } - private MediaDevice getMediaDeviceById(String id) { - return mLocalMediaManager.getMediaDeviceById(new ArrayList<>(mMediaDevices), id); - } - boolean addDeviceToPlayMedia(MediaDevice device) { mMetricLogger.logInteractionExpansion(device); return mLocalMediaManager.addDeviceToPlayMedia(device); @@ -686,10 +672,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return mLocalMediaManager.getDeselectableMediaDevice(); } - void adjustSessionVolume(String sessionId, int volume) { - mLocalMediaManager.adjustSessionVolume(sessionId, volume); - } - void adjustSessionVolume(int volume) { mLocalMediaManager.adjustSessionVolume(volume); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt index 2714cf427e19..485a7e5738f1 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt @@ -86,7 +86,6 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel( becauseCannotSkipBouncer = false, biometricSettingEnabledForUser = false, bouncerFullyShown = false, - bouncerIsOrWillShow = false, onlyFaceEnrolled = false, faceAuthenticated = false, faceDisabled = false, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index ae980f58cb3f..409457e23ce9 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -1554,7 +1554,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { anyBoolean()); CancellationSignal cancelSignal = mCancellationSignalCaptor.getValue(); - bouncerWillBeVisibleSoon(); + bouncerFullyVisible(); mTestableLooper.processAllMessages(); assertThat(cancelSignal.isCanceled()).isTrue(); @@ -1752,11 +1752,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { setKeyguardBouncerVisibility(true); } - private void bouncerWillBeVisibleSoon() { - mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true, false); - mTestableLooper.processAllMessages(); - } - private void setKeyguardBouncerVisibility(boolean isVisible) { mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible); mTestableLooper.processAllMessages(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java index eb8ecae305bc..9be201e99b2b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -241,46 +241,29 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { } @Test - public void onStart_isBroadcasting_verifyRegisterLeBroadcastServiceCallBack() { + public void whenBroadcasting_verifyLeBroadcastServiceCallBackIsRegisteredAndUnregistered() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); mIsBroadcasting = true; mMediaOutputBaseDialogImpl.onStart(); - verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any()); - } - - @Test - public void onStart_notBroadcasting_noRegisterLeBroadcastServiceCallBack() { - when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( - mLocalBluetoothLeBroadcast); - mIsBroadcasting = false; - - mMediaOutputBaseDialogImpl.onStart(); - - verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any()); - } - - @Test - public void onStart_isBroadcasting_verifyUnregisterLeBroadcastServiceCallBack() { - when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( - mLocalBluetoothLeBroadcast); - mIsBroadcasting = true; mMediaOutputBaseDialogImpl.onStop(); - verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any()); } @Test - public void onStop_notBroadcasting_noUnregisterLeBroadcastServiceCallBack() { + public void + whenNotBroadcasting_verifyLeBroadcastServiceCallBackIsNotRegisteredOrUnregistered() { when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( mLocalBluetoothLeBroadcast); mIsBroadcasting = false; + mMediaOutputBaseDialogImpl.onStart(); mMediaOutputBaseDialogImpl.onStop(); + verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any()); verify(mLocalBluetoothLeBroadcast, never()).unregisterServiceCallBack(any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index 465654ed585f..cb31fde26bf2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -171,15 +171,6 @@ public class MediaOutputControllerTest extends SysuiTestCase { } @Test - public void start_LocalMediaManagerIsNull_verifyNotStartScan() { - mMediaOutputController.mLocalMediaManager = null; - mMediaOutputController.start(mCb); - - verify(mLocalMediaManager, never()).registerCallback(mMediaOutputController); - verify(mLocalMediaManager, never()).startScan(); - } - - @Test public void stop_verifyLocalMediaManagerDeinit() { mMediaOutputController.start(mCb); reset(mLocalMediaManager); diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java index 1bcc21e66302..43732d473cf4 100644 --- a/services/core/java/com/android/server/logcat/LogcatManagerService.java +++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java @@ -16,6 +16,8 @@ package com.android.server.logcat; +import static android.os.Process.getParentPid; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -38,6 +40,8 @@ import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ILogAccessDialogCallback; +import com.android.internal.app.LogAccessDialogActivity; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -45,6 +49,7 @@ import com.android.server.SystemService; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; @@ -98,7 +103,7 @@ public final class LogcatManagerService extends SystemService { private final Injector mInjector; private final Supplier<Long> mClock; private final BinderService mBinderService; - private final LogcatManagerServiceInternal mLocalService; + private final LogAccessDialogCallback mDialogCallback; private final Handler mHandler; private ActivityManagerInternal mActivityManagerInternal; private ILogd mLogdService; @@ -203,7 +208,8 @@ public final class LogcatManagerService extends SystemService { } } - final class LogcatManagerServiceInternal { + final class LogAccessDialogCallback extends ILogAccessDialogCallback.Stub { + @Override public void approveAccessForClient(int uid, @NonNull String packageName) { final LogAccessClient client = new LogAccessClient(uid, packageName); if (DEBUG) { @@ -213,6 +219,7 @@ public final class LogcatManagerService extends SystemService { mHandler.sendMessageAtTime(msg, mClock.get()); } + @Override public void declineAccessForClient(int uid, @NonNull String packageName) { final LogAccessClient client = new LogAccessClient(uid, packageName); if (DEBUG) { @@ -299,7 +306,7 @@ public final class LogcatManagerService extends SystemService { mInjector = injector; mClock = injector.createClock(); mBinderService = new BinderService(); - mLocalService = new LogcatManagerServiceInternal(); + mDialogCallback = new LogAccessDialogCallback(); mHandler = new LogAccessRequestHandler(injector.getLooper(), this); } @@ -308,15 +315,14 @@ public final class LogcatManagerService extends SystemService { try { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); publishBinderService("logcat", mBinderService); - publishLocalService(LogcatManagerServiceInternal.class, mLocalService); } catch (Throwable t) { Slog.e(TAG, "Could not start the LogcatManagerService.", t); } } @VisibleForTesting - LogcatManagerServiceInternal getLocalService() { - return mLocalService; + LogAccessDialogCallback getDialogCallback() { + return mDialogCallback; } @VisibleForTesting @@ -340,13 +346,6 @@ public final class LogcatManagerService extends SystemService { * access */ private String getPackageName(LogAccessRequest request) { - if (mActivityManagerInternal != null) { - String packageName = mActivityManagerInternal.getPackageNameByPid(request.mPid); - if (packageName != null) { - return packageName; - } - } - PackageManager pm = mContext.getPackageManager(); if (pm == null) { // Decline the logd access if PackageManager is null @@ -355,15 +354,28 @@ public final class LogcatManagerService extends SystemService { } String[] packageNames = pm.getPackagesForUid(request.mUid); - if (ArrayUtils.isEmpty(packageNames)) { // Decline the logd access if the app name is unknown Slog.e(TAG, "Unknown calling package name, declining the logd access"); return null; } - String firstPackageName = packageNames[0]; + if (mActivityManagerInternal != null) { + int pid = request.mPid; + String packageName = mActivityManagerInternal.getPackageNameByPid(pid); + while ((packageName == null || !ArrayUtils.contains(packageNames, packageName)) + && pid != -1) { + pid = getParentPid(pid); + packageName = mActivityManagerInternal.getPackageNameByPid(pid); + } + + if (packageName != null && ArrayUtils.contains(packageNames, packageName)) { + return packageName; + } + } + Arrays.sort(packageNames); + String firstPackageName = packageNames[0]; if (firstPackageName == null || firstPackageName.isEmpty()) { // Decline the logd access if the package name from uid is unknown Slog.e(TAG, "Unknown calling package name, declining the logd access"); @@ -430,6 +442,7 @@ public final class LogcatManagerService extends SystemService { mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_PENDING_TIMEOUT, client), mClock.get() + PENDING_CONFIRMATION_TIMEOUT_MILLIS); final Intent mIntent = createIntent(client); + mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM); } @@ -530,6 +543,7 @@ public final class LogcatManagerService extends SystemService { intent.putExtra(Intent.EXTRA_PACKAGE_NAME, client.mPackageName); intent.putExtra(Intent.EXTRA_UID, client.mUid); + intent.putExtra(LogAccessDialogActivity.EXTRA_CALLBACK, mDialogCallback.asBinder()); return intent; } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 9456f0fcced7..2e1477ddf0f1 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -25,6 +25,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER; @@ -1007,6 +1008,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub isInLockTaskMode); break; } + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: { + final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); + if (activity == null || activity.finishing) { + break; + } + if (activity.isVisible()) { + // Prevent the transition from being executed too early if the activity is + // visible. + activity.finishIfPossible("finish-activity-op", false /* oomAdj */); + } else { + activity.destroyIfPossible("finish-activity-op"); + } + break; + } case HIERARCHY_OP_TYPE_LAUNCH_TASK: { mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, "launchTask HierarchyOp"); @@ -1620,6 +1635,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub organizer); } break; + case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: + // Allow finish activity if it has the activity token. + break; default: // Other types of hierarchy changes are not allowed. String msg = "Permission Denial: " + func + " from pid=" diff --git a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java index f33001774263..2cd5314e961f 100644 --- a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java @@ -16,6 +16,8 @@ package com.android.server.logcat; +import static android.os.Process.INVALID_UID; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -28,6 +30,7 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.content.ContextWrapper; +import android.content.pm.PackageManager; import android.os.ILogd; import android.os.Looper; import android.os.UserHandle; @@ -69,10 +72,12 @@ public class LogcatManagerServiceTest { @Mock private ActivityManagerInternal mActivityManagerInternalMock; @Mock + private PackageManager mPackageManagerMock; + @Mock private ILogd mLogdMock; private LogcatManagerService mService; - private LogcatManagerService.LogcatManagerServiceInternal mLocalService; + private LogcatManagerService.LogAccessDialogCallback mDialogCallback; private ContextWrapper mContextSpy; private OffsettableClock mClock; private TestLooper mTestLooper; @@ -81,10 +86,17 @@ public class LogcatManagerServiceTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); + when(mActivityManagerInternalMock.getInstrumentationSourceUid(anyInt())) + .thenReturn(INVALID_UID); + mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); mClock = new OffsettableClock.Stopped(); mTestLooper = new TestLooper(mClock::now); - + when(mContextSpy.getPackageManager()).thenReturn(mPackageManagerMock); + when(mPackageManagerMock.getPackagesForUid(APP1_UID)).thenReturn( + new String[]{APP1_PACKAGE_NAME}); + when(mPackageManagerMock.getPackagesForUid(APP2_UID)).thenReturn( + new String[]{APP2_PACKAGE_NAME}); when(mActivityManagerInternalMock.getPackageNameByPid(APP1_PID)).thenReturn( APP1_PACKAGE_NAME); when(mActivityManagerInternalMock.getPackageNameByPid(APP2_PID)).thenReturn( @@ -106,7 +118,7 @@ public class LogcatManagerServiceTest { return mLogdMock; } }); - mLocalService = mService.getLocalService(); + mDialogCallback = mService.getDialogCallback(); mService.onStart(); } @@ -136,6 +148,20 @@ public class LogcatManagerServiceTest { } @Test + public void test_RequestFromBackground_ApprovedIfInstrumented() throws Exception { + when(mActivityManagerInternalMock.getInstrumentationSourceUid(APP1_UID)) + .thenReturn(APP1_UID); + when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( + ActivityManager.PROCESS_STATE_RECEIVER); + mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); + mTestLooper.dispatchAll(); + + verify(mLogdMock).approve(APP1_UID, APP1_GID, APP1_PID, FD1); + verify(mLogdMock, never()).decline(APP1_UID, APP1_GID, APP1_PID, FD1); + verify(mContextSpy, never()).startActivityAsUser(any(), any()); + } + + @Test public void test_RequestFromForegroundService_DeclinedWithoutPrompt() throws Exception { when(mActivityManagerInternalMock.getUidProcessState(APP1_UID)).thenReturn( ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); @@ -181,7 +207,7 @@ public class LogcatManagerServiceTest { mTestLooper.dispatchAll(); verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -196,7 +222,7 @@ public class LogcatManagerServiceTest { mTestLooper.dispatchAll(); verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM)); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -214,7 +240,7 @@ public class LogcatManagerServiceTest { verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -234,7 +260,7 @@ public class LogcatManagerServiceTest { verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt()); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD1); @@ -249,7 +275,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); @@ -267,7 +293,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2); @@ -287,7 +313,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); mService.getBinderService().startThread(APP2_UID, APP2_GID, APP2_PID, FD2); @@ -304,7 +330,7 @@ public class LogcatManagerServiceTest { ActivityManager.PROCESS_STATE_TOP); mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1); mTestLooper.dispatchAll(); - mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); + mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME); mTestLooper.dispatchAll(); advanceTime(LogcatManagerService.STATUS_EXPIRATION_TIMEOUT_MILLIS); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 61cf8cc76d83..1404de253476 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -730,6 +730,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testApplyTransaction_finishActivity() { + final ActivityRecord activity = createActivityRecord(mDisplayContent); + + mTransaction.finishActivity(activity.token); + assertApplyTransactionAllowed(mTransaction); + + assertTrue(activity.finishing); + } + + @Test public void testApplyTransaction_skipTransactionForUnregisterOrganizer() { mController.unregisterOrganizer(mIOrganizer); final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); |