diff options
Diffstat (limited to 'services')
183 files changed, 4867 insertions, 2665 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java index 070626be9f80..df4a52e308b6 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java @@ -256,6 +256,7 @@ class EventDispatcher { return actionMasked; } } + /** * Sends down events to the view hierarchy for all pointers which are not already being * delivered i.e. pointers that are not yet injected. @@ -285,6 +286,79 @@ class EventDispatcher { } /** + * Sends down events to the view hierarchy for all pointers which are not already being + * delivered with original down location. i.e. pointers that are not yet injected. The down time + * is also replaced by the original one. + * + * + * @param prototype The prototype from which to create the injected events. + * @param policyFlags The policy flags associated with the event. + */ + void sendDownForAllNotInjectedPointersWithOriginalDown(MotionEvent prototype, int policyFlags) { + // Inject the injected pointers. + int pointerIdBits = 0; + final int pointerCount = prototype.getPointerCount(); + final MotionEvent event = computeInjectionDownEvent(prototype); + for (int i = 0; i < pointerCount; i++) { + final int pointerId = prototype.getPointerId(i); + // Do not send event for already delivered pointers. + if (!mState.isInjectedPointerDown(pointerId)) { + pointerIdBits |= (1 << pointerId); + final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); + sendMotionEvent( + event, + action, + mState.getLastReceivedEvent(), + pointerIdBits, + policyFlags); + } + } + } + + private MotionEvent computeInjectionDownEvent(MotionEvent prototype) { + final int pointerCount = prototype.getPointerCount(); + if (pointerCount != mState.getReceivedPointerTracker().getReceivedPointerDownCount()) { + Slog.w(LOG_TAG, "The pointer count doesn't match the received count."); + return MotionEvent.obtain(prototype); + } + MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount]; + MotionEvent.PointerProperties[] properties = + new MotionEvent.PointerProperties[pointerCount]; + for (int i = 0; i < pointerCount; ++i) { + final int pointerId = prototype.getPointerId(i); + final float x = mState.getReceivedPointerTracker().getReceivedPointerDownX(pointerId); + final float y = mState.getReceivedPointerTracker().getReceivedPointerDownY(pointerId); + coords[i] = new MotionEvent.PointerCoords(); + coords[i].x = x; + coords[i].y = y; + properties[i] = new MotionEvent.PointerProperties(); + properties[i].id = pointerId; + properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER; + } + MotionEvent event = + MotionEvent.obtain( + prototype.getDownTime(), + // The event time is used for downTime while sending ACTION_DOWN. We adjust + // it to avoid the motion velocity is too fast in the beginning after + // Delegating. + prototype.getDownTime(), + prototype.getAction(), + pointerCount, + properties, + coords, + prototype.getMetaState(), + prototype.getButtonState(), + prototype.getXPrecision(), + prototype.getYPrecision(), + prototype.getDeviceId(), + prototype.getEdgeFlags(), + prototype.getSource(), + prototype.getFlags()); + return event; + } + + /** + * * Sends up events to the view hierarchy for all pointers which are already being delivered i.e. * pointers that are injected. * diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java index 07e82111d4e5..5b46cb4ab378 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java @@ -294,7 +294,7 @@ class MultiFingerSwipe extends GestureMatcher { + Float.toString(mGestureDetectionThresholdPixels)); } if (getState() == STATE_CLEAR) { - if (moveDelta < mTouchSlop) { + if (moveDelta < (mTargetFingerCount * mTouchSlop)) { // This still counts as a touch not a swipe. continue; } else if (mStrokeBuffers[pointerIndex].size() == 0) { diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index d127172c3aa6..8305be393ab1 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -608,7 +608,7 @@ public class TouchExplorer extends BaseEventStreamTransformation mReceivedPointerTracker.getReceivedPointerDownY(id) - rawEvent.getY(index); final double moveDelta = Math.hypot(deltaX, deltaY); - if (moveDelta < mTouchSlop) { + if (moveDelta < (2 * mTouchSlop)) { return; } } @@ -644,23 +644,20 @@ public class TouchExplorer extends BaseEventStreamTransformation if (mGestureDetector.isMultiFingerGesturesEnabled()) { if (mGestureDetector.isTwoFingerPassthroughEnabled()) { if (event.getPointerCount() == 3) { - boolean isOnBottomEdge = true; // If three fingers went down on the bottom edge of the screen, delegate // immediately. - final long screenHeight = - mContext.getResources().getDisplayMetrics().heightPixels; - for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { - if (mReceivedPointerTracker.getReceivedPointerDownY(i) - < (screenHeight - mEdgeSwipeHeightPixels)) { - isOnBottomEdge = false; - } - } - if (isOnBottomEdge) { + if (allPointersDownOnBottomEdge(event)) { if (DEBUG) { Slog.d(LOG_TAG, "Three-finger edge swipe detected."); } mState.startDelegating(); - mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); + if (mState.isTouchExploring()) { + mDispatcher.sendDownForAllNotInjectedPointers(event, + policyFlags); + } else { + mDispatcher.sendDownForAllNotInjectedPointersWithOriginalDown( + event, policyFlags); + } } } } @@ -1063,6 +1060,22 @@ public class TouchExplorer extends BaseEventStreamTransformation return downEvent; } + private boolean allPointersDownOnBottomEdge(MotionEvent event) { + final long screenHeight = + mContext.getResources().getDisplayMetrics().heightPixels; + for (int i = 0; i < event.getPointerCount(); ++i) { + final int pointerId = event.getPointerId(i); + final float pointerDownY = mReceivedPointerTracker.getReceivedPointerDownY(pointerId); + if (pointerDownY < (screenHeight - mEdgeSwipeHeightPixels)) { + if (DEBUG) { + Slog.d(LOG_TAG, "The pointer is not on the bottom edge" + pointerDownY); + } + return false; + } + } + return true; + } + public TouchState getState() { return mState; } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java index 49a85864407b..a401bcd3eabd 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java @@ -19,6 +19,8 @@ package com.android.server.accessibility.magnification; import static android.os.IBinder.DeathRecipient; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.RemoteCallback; import android.os.RemoteException; import android.util.Slog; import android.view.accessibility.IWindowMagnificationConnection; @@ -47,9 +49,10 @@ class WindowMagnificationConnectionWrapper { mConnection.asBinder().linkToDeath(deathRecipient, 0); } - boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY) { + boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY, + @Nullable RemoteCallback endCallback) { try { - mConnection.enableWindowMagnification(displayId, scale, centerX, centerY); + mConnection.enableWindowMagnification(displayId, scale, centerX, centerY, endCallback); } catch (RemoteException e) { if (DBG) { Slog.e(TAG, "Error calling enableWindowMagnification()", e); @@ -71,9 +74,9 @@ class WindowMagnificationConnectionWrapper { return true; } - boolean disableWindowMagnification(int displayId) { + boolean disableWindowMagnification(int displayId, @Nullable RemoteCallback endCallback) { try { - mConnection.disableWindowMagnification(displayId); + mConnection.disableWindowMagnification(displayId, endCallback); } catch (RemoteException e) { if (DBG) { Slog.e(TAG, "Error calling disableWindowMagnification()", e); diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java index 3ee5b28ee338..7d6067c8e1da 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java @@ -176,7 +176,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl Slog.i(LOG_TAG, "onDestroy(); delayed = " + mDetectingState.toString()); } - mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, true); + mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true); resetToDetectState(); } @@ -211,14 +211,14 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl final float scale = MathUtils.constrain( mWindowMagnificationMgr.getPersistedScale(), MIN_SCALE, MAX_SCALE); - mWindowMagnificationMgr.enableWindowMagnifier(mDisplayId, scale, centerX, centerY); + mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY); } private void disableWindowMagnifier() { if (DEBUG_ALL) { Slog.i(LOG_TAG, "disableWindowMagnifier()"); } - mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, false); + mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false); } private void toggleMagnification(float centerX, float centerY) { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index ed2b26f24478..d3d56d7f857d 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; +import android.os.RemoteCallback; import android.os.RemoteException; import android.provider.Settings; import android.util.MathUtils; @@ -73,7 +74,7 @@ public class WindowMagnificationManager implements public void onReceive(Context context, Intent intent) { final int displayId = context.getDisplayId(); removeMagnificationButton(displayId); - disableWindowMagnification(displayId); + disableWindowMagnification(displayId, false); } }; @@ -84,6 +85,7 @@ public class WindowMagnificationManager implements /** * Sets {@link IWindowMagnificationConnection}. + * * @param connection {@link IWindowMagnificationConnection} */ public void setConnection(@Nullable IWindowMagnificationConnection connection) { @@ -124,7 +126,6 @@ public class WindowMagnificationManager implements } /** - * * @return {@code true} if {@link IWindowMagnificationConnection} is available */ public boolean isConnected() { @@ -136,10 +137,10 @@ public class WindowMagnificationManager implements /** * Requests {@link IWindowMagnificationConnection} through * {@link StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)} and - * destroys all window magnifiers if necessary. + * destroys all window magnifications if necessary. * * @param connect {@code true} if needs connection, otherwise set the connection to null and - * destroy all window magnifiers. + * destroy all window magnifications. * @return {@code true} if {@link IWindowMagnificationConnection} state is going to change. */ public boolean requestConnection(boolean connect) { @@ -171,7 +172,7 @@ public class WindowMagnificationManager implements private void disableAllWindowMagnifiers() { for (int i = 0; i < mWindowMagnifiers.size(); i++) { final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); - magnifier.disable(); + magnifier.disableWindowMagnificationInternal(null); } mWindowMagnifiers.clear(); } @@ -187,12 +188,12 @@ public class WindowMagnificationManager implements @Override public boolean processScroll(int displayId, float distanceX, float distanceY) { - moveWindowMagnifier(displayId, -distanceX, -distanceY); + moveWindowMagnification(displayId, -distanceX, -distanceY); return /* event consumed: */ true; } /** - * Scales the magnified region on the specified display if the window magnifier is enabled. + * Scales the magnified region on the specified display if window magnification is initiated. * * @param displayId The logical display id. * @param scale The target scale, must be >= 1 @@ -209,37 +210,69 @@ public class WindowMagnificationManager implements } /** - * Enables the window magnifier with specified center and scale on the specified display. - * @param displayId The logical display id. + * Enables window magnification with specified center and scale on the given display and + * animating the transition. + * + * @param displayId The logical display id. + * @param scale The target scale, must be >= 1. + * @param centerX The screen-relative X coordinate around which to center, + * or {@link Float#NaN} to leave unchanged. + * @param centerY The screen-relative Y coordinate around which to center, + * or {@link Float#NaN} to leave unchanged. + */ + void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) { + enableWindowMagnification(displayId, scale, centerX, centerY, null); + } + + /** + * Enables window magnification with specified center and scale on the specified display and + * animating the transition. + * + * @param displayId The logical display id. * @param scale The target scale, must be >= 1. * @param centerX The screen-relative X coordinate around which to center, * or {@link Float#NaN} to leave unchanged. * @param centerY The screen-relative Y coordinate around which to center, * or {@link Float#NaN} to leave unchanged. + * @param endCallback Called when the animation is ended without any interruption or the + * window magnifier is disabled already. */ - void enableWindowMagnifier(int displayId, float scale, float centerX, float centerY) { + void enableWindowMagnification(int displayId, float scale, float centerX, float centerY, + @Nullable Runnable endCallback) { synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { magnifier = createWindowMagnifier(displayId); } - magnifier.enable(scale, centerX, centerY); + magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, endCallback); } } /** - * Disables the window magnifier on the specified display. + * Disables window magnification on the given display. + * + * @param displayId The logical display id. + * @param clear {@true} Clears the state of window magnification. + */ + void disableWindowMagnification(int displayId, boolean clear) { + disableWindowMagnification(displayId, clear, null); + } + + /** + * Disables window magnification on the specified display and animating the transition. * * @param displayId The logical display id. - * @param clear {@true} Clears the state of the window magnifier + * @param clear {@true} Clears the state of window magnification. + * @param endCallback Called when the animation is ended without any interruption or the + * window magnifier is disabled already. */ - void disableWindowMagnifier(int displayId, boolean clear) { + void disableWindowMagnification(int displayId, boolean clear, Runnable endCallback) { synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { return; } - magnifier.disable(); + magnifier.disableWindowMagnificationInternal(endCallback); if (clear) { mWindowMagnifiers.delete(displayId); } @@ -264,10 +297,10 @@ public class WindowMagnificationManager implements } /** - * Indicates whether this window magnifier is enabled on specified display. + * Indicates whether window magnification is enabled on specified display. * * @param displayId The logical display id. - * @return {@code true} if the window magnifier is enabled. + * @return {@code true} if the window magnification is enabled. */ boolean isWindowMagnifierEnabled(int displayId) { synchronized (mLock) { @@ -323,7 +356,7 @@ public class WindowMagnificationManager implements } /** - * Moves the window magnifier with specified offset. + * Moves window magnification on the specified display with the specified offset. * * @param displayId The logical display id. * @param offsetX the amount in pixels to offset the region in the X direction, in current @@ -331,7 +364,7 @@ public class WindowMagnificationManager implements * @param offsetY the amount in pixels to offset the region in the Y direction, in current * screen pixels. */ - void moveWindowMagnifier(int displayId, float offsetX, float offsetY) { + void moveWindowMagnification(int displayId, float offsetX, float offsetY) { synchronized (mLock) { WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); if (magnifier == null) { @@ -366,6 +399,7 @@ public class WindowMagnificationManager implements /** * Creates the windowMagnifier based on the specified display and stores it. + * * @param displayId logical display id. */ @GuardedBy("mLock") @@ -425,7 +459,8 @@ public class WindowMagnificationManager implements } /** - * A class to manipulate the window magnifier and contains the relevant information. + * A class manipulates window magnification per display and contains the magnification + * information. */ private static class WindowMagnifier { @@ -434,31 +469,34 @@ public class WindowMagnificationManager implements private boolean mEnabled; private final WindowMagnificationManager mWindowMagnificationManager; - //Records the bounds of window magnifier. + //Records the bounds of window magnification. private final Rect mBounds = new Rect(); //The magnified bounds on the screen. private final Rect mSourceBounds = new Rect(); + WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) { mDisplayId = displayId; mWindowMagnificationManager = windowMagnificationManager; } @GuardedBy("mLock") - void enable(float scale, float centerX, float centerY) { + void enableWindowMagnificationInternal(float scale, float centerX, float centerY, + @Nullable Runnable endCallback) { if (mEnabled) { return; } final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); - if (mWindowMagnificationManager.enableWindowMagnification(mDisplayId, normScale, - centerX, centerY)) { + if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale, + centerX, centerY, endCallback)) { mScale = normScale; mEnabled = true; } } @GuardedBy("mLock") - void disable() { - if (mEnabled && mWindowMagnificationManager.disableWindowMagnification(mDisplayId)) { + void disableWindowMagnificationInternal(@Nullable Runnable endCallback) { + if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal( + mDisplayId, endCallback)) { mEnabled = false; } } @@ -519,19 +557,21 @@ public class WindowMagnificationManager implements } } - private boolean enableWindowMagnification(int displayId, float scale, float centerX, - float centerY) { + private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX, + float centerY, Runnable endCallback) { return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification( - displayId, scale, centerX, centerY); + displayId, scale, centerX, centerY, + endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null); } private boolean setScaleInternal(int displayId, float scale) { return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale); } - private boolean disableWindowMagnification(int displayId) { + private boolean disableWindowMagnificationInternal(int displayId, Runnable endCallback) { return mConnectionWrapper != null && mConnectionWrapper.disableWindowMagnification( - displayId); + displayId, + endCallback != null ? new RemoteCallback(bundle -> endCallback.run()) : null); } private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) { diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index b9625397d237..16077cb6082f 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -228,7 +228,7 @@ public class FullRestoreEngine extends RestoreEngine { PackageManagerInternal.class); RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy( mBackupManagerService.getPackageManager(), allowApks, info, signatures, - pmi, mUserId); + pmi, mUserId, mBackupEligibilityRules); mManifestSignatures.put(info.packageName, signatures); mPackagePolicies.put(pkg, restorePolicy); mPackageInstallers.put(pkg, info.installerPackageName); diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java index 3789fa14e87b..6963248734a3 100644 --- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java +++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java @@ -389,13 +389,29 @@ public class TarBackupReader { public RestorePolicy chooseRestorePolicy(PackageManager packageManager, boolean allowApks, FileMetadata info, Signature[] signatures, PackageManagerInternal pmi, int userId) { + return chooseRestorePolicy(packageManager, allowApks, info, signatures, pmi, userId, + BackupEligibilityRules.forBackup(packageManager, pmi, userId)); + } + + /** + * Chooses restore policy. + * + * @param packageManager - PackageManager instance. + * @param allowApks - allow restore set to include apks. + * @param info - file metadata. + * @param signatures - array of signatures parsed from backup file. + * @param userId - ID of the user for which restore is performed. + * @param eligibilityRules - {@link BackupEligibilityRules} for this operation. + * @return a restore policy constant. + */ + public RestorePolicy chooseRestorePolicy(PackageManager packageManager, + boolean allowApks, FileMetadata info, Signature[] signatures, + PackageManagerInternal pmi, int userId, BackupEligibilityRules eligibilityRules) { if (signatures == null) { return RestorePolicy.IGNORE; } RestorePolicy policy = RestorePolicy.IGNORE; - BackupEligibilityRules eligibilityRules = BackupEligibilityRules.forBackup(packageManager, - pmi, userId); // Okay, got the manifest info we need... try { PackageInfo pkgInfo = packageManager.getPackageInfoAsUser( diff --git a/services/companion/TEST_MAPPING b/services/companion/TEST_MAPPING new file mode 100644 index 000000000000..63f54fa35158 --- /dev/null +++ b/services/companion/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "CtsOsTestCases", + "options": [ + { + "include-filter": "android.os.cts.CompanionDeviceManagerTest" + } + ] + } + ] +} diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index eb38f5199ce5..7b9728cb4f08 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -393,12 +393,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind .toString()); long identity = Binder.clearCallingIdentity(); try { - return PendingIntent.getActivity(getContext(), + return PendingIntent.getActivityAsUser(getContext(), 0 /* request code */, NotificationAccessConfirmationActivityContract.launcherIntent( userId, component, packageTitle), PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT - | PendingIntent.FLAG_CANCEL_CURRENT); + | PendingIntent.FLAG_CANCEL_CURRENT, + null /* options */, + new UserHandle(userId)); } finally { Binder.restoreCallingIdentity(identity); } @@ -612,8 +614,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } - private AtomicFile getStorageFileForUser(int uid) { - return mUidToStorage.computeIfAbsent(uid, (u) -> + private AtomicFile getStorageFileForUser(int userId) { + return mUidToStorage.computeIfAbsent(userId, (u) -> new AtomicFile(new File( //TODO deprecated method - what's the right replacement? Environment.getUserSystemDirectory(u), diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index f372c6f85ec6..0d79240a4b59 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -166,6 +166,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private String mAddress; private String mName; private final ContentResolver mContentResolver; + private final int mUserId; private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks; private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks; private IBinder mBluetoothBinder; @@ -481,6 +482,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mName = null; mErrorRecoveryRetryCounter = 0; mContentResolver = context.getContentResolver(); + mUserId = mContentResolver.getUserId(); // Observe BLE scan only mode settings change. registerForBleScanModeChange(); mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>(); @@ -625,7 +627,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (mContext.getResources() .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation) - && Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) + && Settings.Secure.getIntForUser(mContentResolver, + SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0, mUserId) == 0) { // if the valid flag is not set, don't load the address and name if (DBG) { @@ -633,8 +636,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } return; } - mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME); - mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS); + mName = Settings.Secure.getStringForUser( + mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, mUserId); + mAddress = Settings.Secure.getStringForUser( + mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, mUserId); if (DBG) { Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress); } @@ -648,26 +653,31 @@ class BluetoothManagerService extends IBluetoothManager.Stub { */ private void storeNameAndAddress(String name, String address) { if (name != null) { - Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name); + Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name, + mUserId); mName = name; if (DBG) { - Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getString(mContentResolver, - SECURE_SETTINGS_BLUETOOTH_NAME)); + Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getStringForUser( + mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, + mUserId)); } } if (address != null) { - Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address); + Settings.Secure.putStringForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, + address, mUserId); mAddress = address; if (DBG) { Slog.d(TAG, - "Stored Bluetoothaddress: " + Settings.Secure.getString(mContentResolver, - SECURE_SETTINGS_BLUETOOTH_ADDRESS)); + "Stored Bluetoothaddress: " + Settings.Secure.getStringForUser( + mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, + mUserId)); } } if ((name != null) && (address != null)) { - Settings.Secure.putInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1); + Settings.Secure.putIntForUser(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 1, + mUserId); } } diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index b09b2605a791..500e768372f5 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -126,6 +126,16 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { } @Override + public boolean closePartition() throws RemoteException { + IGsiService service = getGsiService(); + if (service.closePartition() != 0) { + Slog.i(TAG, "Partition installation completes with error"); + return false; + } + return true; + } + + @Override public boolean finishInstallation() throws RemoteException { IGsiService service = getGsiService(); if (service.closeInstall() != 0) { diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index eb8308b56f2e..e433fbd94d4f 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -64,6 +64,7 @@ import android.telephony.DisconnectCause; import android.telephony.LocationAccessPolicy; import android.telephony.PhoneCapability; import android.telephony.PhoneStateListener; +import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; import android.telephony.PreciseDisconnectCause; @@ -142,13 +143,13 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { int callerUid; int callerPid; - int events; + long events; int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - boolean matchPhoneStateListenerEvent(int events) { + boolean matchPhoneStateListenerEvent(long events) { return (callback != null) && ((events & this.events) != 0); } @@ -177,7 +178,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + onSubscriptionsChangedListenerCallback + " onOpportunisticSubscriptionsChangedListenererCallback=" + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId - + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + "}"; + + " phoneId=" + phoneId + " events=" + Long.toHexString(events) + "}"; } } @@ -306,6 +307,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private final LocalLog mListenLog = new LocalLog(00); + private List<PhysicalChannelConfig> mPhysicalChannelConfigs; + /** * Per-phone map of precise data connection state. The key of the map is the pair of transport * type and APN setting. This is the cache to prevent redundant callbacks to the listeners. @@ -318,19 +321,19 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // Starting in Q, almost all cellular location requires FINE location enforcement. // Prior to Q, cellular was available with COARSE location enforcement. Bits in this // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later. - static final int ENFORCE_LOCATION_PERMISSION_MASK = + static final long ENFORCE_LOCATION_PERMISSION_MASK = PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_CELL_INFO | PhoneStateListener.LISTEN_REGISTRATION_FAILURE | PhoneStateListener.LISTEN_BARRING_INFO; - static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = + static final long ENFORCE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED; - static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK = + static final long ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES @@ -339,11 +342,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { | PhoneStateListener.LISTEN_REGISTRATION_FAILURE | PhoneStateListener.LISTEN_BARRING_INFO; - static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK = + static final long READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK = PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS; - static final int READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK = + static final long READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT | PhoneStateListener.LISTEN_SRVCC_STATE_CHANGED | PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED @@ -495,6 +498,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { cutListToSize(mImsReasonInfo, mNumPhones); cutListToSize(mPreciseDataConnectionStates, mNumPhones); cutListToSize(mBarringInfo, mNumPhones); + cutListToSize(mPhysicalChannelConfigs, mNumPhones); return; } @@ -528,6 +532,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; + mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig( + PhysicalChannelConfig.CONNECTION_UNKNOWN,0)); } } @@ -588,6 +594,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones]; mBarringInfo = new ArrayList<>(); mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones]; + mPhysicalChannelConfigs = new ArrayList<>(); for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; @@ -617,6 +624,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mPreciseDataConnectionStates.add(new ArrayMap<>()); mBarringInfo.add(i, new BarringInfo()); mTelephonyDisplayInfos[i] = null; + mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig( + PhysicalChannelConfig.CONNECTION_UNKNOWN,0)); } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -795,23 +804,23 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { @Override public void listenWithFeature(String callingPackage, String callingFeatureId, - IPhoneStateListener callback, int events, boolean notifyNow) { + IPhoneStateListener callback, long events, boolean notifyNow) { listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callingPackage, callingFeatureId, callback, events, notifyNow); } @Override public void listenForSubscriber(int subId, String callingPackage, String callingFeatureId, - IPhoneStateListener callback, int events, boolean notifyNow) { + IPhoneStateListener callback, long events, boolean notifyNow) { listen(callingPackage, callingFeatureId, callback, events, notifyNow, subId); } private void listen(String callingPackage, @Nullable String callingFeatureId, - IPhoneStateListener callback, int events, boolean notifyNow, int subId) { + IPhoneStateListener callback, long events, boolean notifyNow, int subId) { int callerUserId = UserHandle.getCallingUserId(); mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); String str = "listen: E pkg=" + pii(callingPackage) + " uid=" + Binder.getCallingUid() - + " events=0x" + Integer.toHexString(events) + " notifyNow=" + notifyNow + " subId=" + + " events=0x" + Long.toHexString(events) + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId; mListenLog.log(str); if (VDBG) { @@ -1098,6 +1107,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) { + try { + r.callback.onPhysicalChannelConfigurationChanged( + mPhysicalChannelConfigs); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } else { @@ -2258,6 +2275,47 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Send a notification to registrants that the configs of physical channel has changed for + * a particular subscription. + * + * @param subId the subId + * @param configs a list of {@link PhysicalChannelConfig}, the configs of physical channel. + */ + public void notifyPhysicalChannelConfigurationForSubscriber( + int subId, List<PhysicalChannelConfig> configs) { + if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) { + return; + } + + if (VDBG) { + log("notifyPhysicalChannelConfiguration: subId=" + subId + " configs=" + configs); + } + + synchronized (mRecords) { + int phoneId = SubscriptionManager.getPhoneId(subId); + if (validatePhoneId(phoneId)) { + mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId)); + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) + && idMatch(r.subId, subId, phoneId)) { + try { + if (DBG_LOC) { + log("notifyPhysicalChannelConfiguration: " + + "mPhysicalChannelConfigs=" + + configs + " r=" + r); + } + r.callback.onPhysicalChannelConfigurationChanged(configs); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + } + handleRemoveListLocked(); + } + } @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @@ -2310,6 +2368,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mEmergencyNumberList=" + mEmergencyNumberList); pw.println("mDefaultPhoneId=" + mDefaultPhoneId); pw.println("mDefaultSubId=" + mDefaultSubId); + pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs); pw.decreaseIndent(); @@ -2536,7 +2595,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { == PackageManager.PERMISSION_GRANTED; } - private boolean checkListenerPermission(int events, int subId, String callingPackage, + private boolean checkListenerPermission(long events, int subId, String callingPackage, @Nullable String callingFeatureId, String message) { LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder = new LocationAccessPolicy.LocationPermissionQuery.Builder() @@ -2742,7 +2801,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } private void checkPossibleMissNotify(Record r, int phoneId) { - int events = r.events; + long events = r.events; if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { try { diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 6dbb1e922f60..bcb7bfacfaf3 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -489,6 +489,9 @@ final class UiModeManagerService extends SystemService { * @return True if the new value is different from the old value. False otherwise. */ private boolean updateNightModeFromSettingsLocked(Context context, Resources res, int userId) { + if (mCarModeEnabled || mCar) { + return false; + } int oldNightMode = mNightMode; if (mSetupWizardComplete) { mNightMode = Secure.getIntForUser(context.getContentResolver(), @@ -1033,7 +1036,7 @@ final class UiModeManagerService extends SystemService { private void persistNightMode(int user) { // Only persist setting if not in car mode - if (mCarModeEnabled) return; + if (mCarModeEnabled || mCar) return; Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, mNightMode, user); Secure.putLongForUser(getContext().getContentResolver(), @@ -1046,7 +1049,7 @@ final class UiModeManagerService extends SystemService { private void persistNightModeOverrides(int user) { // Only persist setting if not in car mode - if (mCarModeEnabled) return; + if (mCarModeEnabled || mCar) return; Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE_OVERRIDE_ON, mOverrideNightModeOn ? 1 : 0, user); Secure.putIntForUser(getContext().getContentResolver(), @@ -1097,7 +1100,7 @@ final class UiModeManagerService extends SystemService { } // Override night mode in power save mode if not in car mode - if (mPowerSave && !mCarModeEnabled) { + if (mPowerSave && !mCarModeEnabled && !mCar) { uiMode &= ~Configuration.UI_MODE_NIGHT_NO; uiMode |= Configuration.UI_MODE_NIGHT_YES; } else { diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 35e88eb804cb..7d81d412e369 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -2114,7 +2114,7 @@ public class AccountManagerService * Owner or system user account was renamed, rename the account for * those users with which the account was shared. */ - List<UserInfo> users = getUserManager().getUsers(true); + List<UserInfo> users = getUserManager().getAliveUsers(); for (UserInfo user : users) { if (user.isRestricted() && (user.restrictedProfileParentId == parentUserId)) { @@ -2373,7 +2373,7 @@ public class AccountManagerService int parentUserId = accounts.userId; if (canHaveProfile(parentUserId)) { // Remove from any restricted profiles that are sharing this account. - List<UserInfo> users = getUserManager().getUsers(true); + List<UserInfo> users = getUserManager().getAliveUsers(); for (UserInfo user : users) { if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) { removeSharedAccountAsUser(account, user.id, callingUid); @@ -4267,7 +4267,7 @@ public class AccountManagerService */ @NonNull public AccountAndUser[] getAllAccounts() { - final List<UserInfo> users = getUserManager().getUsers(true); + final List<UserInfo> users = getUserManager().getAliveUsers(); final int[] userIds = new int[users.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = users.get(i).id; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c31d73246ff6..343e05d982fd 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2268,21 +2268,23 @@ public final class ActiveServices { clist.remove(0); } - if (r.binding.service.app != null) { - if (r.binding.service.app.whitelistManager) { - updateWhitelistManagerLocked(r.binding.service.app); + final ProcessRecord app = r.binding.service.app; + if (app != null) { + if (app.whitelistManager) { + updateWhitelistManagerLocked(app); } // This could have made the service less important. if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { - r.binding.service.app.treatLikeActivity = true; - mAm.updateLruProcessLocked(r.binding.service.app, - r.binding.service.app.hasClientActivities() - || r.binding.service.app.treatLikeActivity, null); + app.treatLikeActivity = true; + mAm.updateLruProcessLocked(app, + app.hasClientActivities() + || app.treatLikeActivity, null); } + mAm.enqueueOomAdjTargetLocked(app); } } - mAm.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); + mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE); } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2d803437beb9..a5d2011483bc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2103,17 +2103,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { try { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "meminfo", pw)) return; PriorityDump.dump(mPriorityDumper, fd, pw, args); } finally { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } } } @@ -2127,17 +2123,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { try { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "gfxinfo", pw)) return; mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args); } finally { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } } } @@ -2151,17 +2143,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { try { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "dbinfo", pw)) return; mActivityManagerService.dumpDbInfo(fd, pw, args); } finally { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } } } @@ -2207,9 +2195,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { try { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(false); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "cacheinfo", pw)) { @@ -2218,9 +2204,7 @@ public class ActivityManagerService extends IActivityManager.Stub mActivityManagerService.dumpBinderCacheContents(fd, pw, args); } finally { - if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) { - Process.enableFreezer(true); - } + mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } } } @@ -5686,8 +5670,8 @@ public class ActivityManagerService extends IActivityManager.Stub "setProcessLimit()"); synchronized (this) { mConstants.setOverrideMaxCachedProcesses(max); + trimApplicationsLocked(true, OomAdjuster.OOM_ADJ_REASON_PROCESS_END); } - trimApplications(OomAdjuster.OOM_ADJ_REASON_PROCESS_END); } @Override @@ -5778,9 +5762,7 @@ public class ActivityManagerService extends IActivityManager.Stub } private boolean isAppBad(final String processName, final int uid) { - synchronized (this) { - return mAppErrors.isBadProcessLocked(processName, uid); - } + return mAppErrors.isBadProcess(processName, uid); } // NOTE: this is an internal method used by the OnShellCommand implementation only and should @@ -13995,7 +13977,8 @@ public class ActivityManagerService extends IActivityManager.Stub r.resultAbort, false); if (doNext) { doTrim = true; - r.queue.processNextBroadcast(false); + r.queue.processNextBroadcastLocked(/* frommsg */ false, + /* skipOomAdj */ true); } } @@ -14008,13 +13991,13 @@ public class ActivityManagerService extends IActivityManager.Stub rl.receiver.asBinder().unlinkToDeath(rl, 0); } } - } - // If we actually concluded any broadcasts, we might now be able - // to trim the recipients' apps from our working set - if (doTrim) { - trimApplications(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); - return; + // If we actually concluded any broadcasts, we might now be able + // to trim the recipients' apps from our working set + if (doTrim) { + trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); + return; + } } } finally { @@ -15145,7 +15128,7 @@ public class ActivityManagerService extends IActivityManager.Stub r.queue.processNextBroadcastLocked(/*fromMsg=*/ false, /*skipOomAdj=*/ true); } // updateOomAdjLocked() will be done here - trimApplicationsLocked(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); + trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); } } finally { @@ -16155,6 +16138,32 @@ public class ActivityManagerService extends IActivityManager.Stub return mOomAdjuster.updateOomAdjLocked(app, oomAdjAll, oomAdjReason); } + /** + * Enqueue the given process into a todo list, and the caller should + * call {@link #updateOomAdjPendingTargetsLocked} to kick off a pass of the oom adj update. + */ + @GuardedBy("this") + void enqueueOomAdjTargetLocked(ProcessRecord app) { + mOomAdjuster.enqueueOomAdjTargetLocked(app); + } + + /** + * Remove the given process into a todo list. + */ + @GuardedBy("this") + void removeOomAdjTargetLocked(ProcessRecord app, boolean procDied) { + mOomAdjuster.removeOomAdjTargetLocked(app, procDied); + } + + /** + * Kick off an oom adj update pass for the pending targets which are enqueued via + * {@link #enqueueOomAdjTargetLocked}. + */ + @GuardedBy("this") + void updateOomAdjPendingTargetsLocked(String oomAdjReason) { + mOomAdjuster.updateOomAdjPendingTargetsLocked(oomAdjReason); + } + static final class ProcStatsRunnable implements Runnable { private final ActivityManagerService mService; private final ProcessStatsService mProcessStats; @@ -16563,16 +16572,17 @@ public class ActivityManagerService extends IActivityManager.Stub mOomAdjuster.setUidTempWhitelistStateLocked(uid, onWhitelist); } - final void trimApplications(String oomAdjReason) { + private void trimApplications(boolean forceFullOomAdj, String oomAdjReason) { synchronized (this) { - trimApplicationsLocked(oomAdjReason); + trimApplicationsLocked(forceFullOomAdj, oomAdjReason); } } @GuardedBy("this") - final void trimApplicationsLocked(String oomAdjReason) { + private void trimApplicationsLocked(boolean forceFullOomAdj, String oomAdjReason) { // First remove any unused application processes whose package // has been removed. + boolean didSomething = false; for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) { final ProcessRecord app = mProcessList.mRemovedProcesses.get(i); if (!app.hasActivitiesOrRecentTasks() @@ -16594,6 +16604,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Ignore exceptions. } } + didSomething = true; cleanUpApplicationRecordLocked(app, false, true, -1, false /*replacingPid*/); mProcessList.mRemovedProcesses.remove(i); @@ -16606,7 +16617,12 @@ public class ActivityManagerService extends IActivityManager.Stub // Now update the oom adj for all processes. Don't skip this, since other callers // might be depending on it. - updateOomAdjLocked(oomAdjReason); + if (didSomething || forceFullOomAdj) { + updateOomAdjLocked(oomAdjReason); + } else { + // Process any pending oomAdj targets, it'll be a no-op if nothing is pending. + updateOomAdjPendingTargetsLocked(oomAdjReason); + } } /** This method sends the specified signal to each of the persistent apps */ @@ -16800,14 +16816,14 @@ public class ActivityManagerService extends IActivityManager.Stub } } - Process.enableFreezer(false); + mOomAdjuster.mCachedAppOptimizer.enableFreezer(false); final RemoteCallback intermediateCallback = new RemoteCallback( new RemoteCallback.OnResultListener() { @Override public void onResult(Bundle result) { finishCallback.sendResult(result); - Process.enableFreezer(true); + mOomAdjuster.mCachedAppOptimizer.enableFreezer(true); } }, null); @@ -17144,8 +17160,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) { - return mCpHelper.checkContentProviderUriPermission(uri, - userId, callingUid, modeFlags); + return mCpHelper.checkContentProviderUriPermission(uri, userId, callingUid, modeFlags); } @Override @@ -17412,7 +17427,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void trimApplications() { - ActivityManagerService.this.trimApplications(OomAdjuster.OOM_ADJ_REASON_ACTIVITY); + ActivityManagerService.this.trimApplications(true, OomAdjuster.OOM_ADJ_REASON_ACTIVITY); } public void killProcessesForRemovedTask(ArrayList<Object> procsToKill) { @@ -18738,4 +18753,16 @@ public class ActivityManagerService extends IActivityManager.Stub mAppErrors.resetStateLocked(); } } + + @Override + public boolean enableAppFreezer(boolean enable) { + int callerUid = Binder.getCallingUid(); + + // Only system can toggle the freezer state + if (callerUid == SYSTEM_UID) { + return mOomAdjuster.mCachedAppOptimizer.enableFreezer(enable); + } else { + throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state "); + } + } } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 5268359df327..5d429d3065f3 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -50,6 +50,7 @@ import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.GuardedBy; import com.android.internal.app.ProcessMap; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; @@ -96,7 +97,11 @@ class AppErrors { * a minimum amount of time; they are removed from it when they are * later restarted (hopefully due to some user action). The value is the * time it was added to the list. + * + * Access is synchronized on the container object itself, and no other + * locks may be acquired while holding that one. */ + @GuardedBy("mBadProcesses") private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>(); @@ -114,76 +119,81 @@ class AppErrors { mProcessCrashTimes.clear(); mProcessCrashTimesPersistent.clear(); mProcessCrashShowDialogTimes.clear(); - mBadProcesses.clear(); + synchronized (mBadProcesses) { + mBadProcesses.clear(); + } } void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) { - if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) { - return; - } - - final long token = proto.start(fieldId); - final long now = SystemClock.uptimeMillis(); - proto.write(AppErrorsProto.NOW_UPTIME_MS, now); - - if (!mProcessCrashTimes.getMap().isEmpty()) { - final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); - final int procCount = pmap.size(); - for (int ip = 0; ip < procCount; ip++) { - final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES); - final String pname = pmap.keyAt(ip); - final SparseArray<Long> uids = pmap.valueAt(ip); - final int uidCount = uids.size(); + synchronized (mBadProcesses) { + if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) { + return; + } - proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname); - for (int i = 0; i < uidCount; i++) { - final int puid = uids.keyAt(i); - final ProcessRecord r = mService.getProcessNames().get(pname, puid); - if (dumpPackage != null && (r == null || !r.pkgList.containsKey(dumpPackage))) { - continue; + final long token = proto.start(fieldId); + final long now = SystemClock.uptimeMillis(); + proto.write(AppErrorsProto.NOW_UPTIME_MS, now); + + if (!mProcessCrashTimes.getMap().isEmpty()) { + final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); + final int procCount = pmap.size(); + for (int ip = 0; ip < procCount; ip++) { + final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES); + final String pname = pmap.keyAt(ip); + final SparseArray<Long> uids = pmap.valueAt(ip); + final int uidCount = uids.size(); + + proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname); + for (int i = 0; i < uidCount; i++) { + final int puid = uids.keyAt(i); + final ProcessRecord r = mService.getProcessNames().get(pname, puid); + if (dumpPackage != null + && (r == null || !r.pkgList.containsKey(dumpPackage))) { + continue; + } + final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES); + proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid); + proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS, + uids.valueAt(i)); + proto.end(etoken); } - final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES); - proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid); - proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS, - uids.valueAt(i)); - proto.end(etoken); + proto.end(ctoken); } - proto.end(ctoken); - } - } + } - if (!mBadProcesses.getMap().isEmpty()) { - final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap(); - final int processCount = pmap.size(); - for (int ip = 0; ip < processCount; ip++) { - final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES); - final String pname = pmap.keyAt(ip); - final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip); - final int uidCount = uids.size(); + if (!mBadProcesses.getMap().isEmpty()) { + final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap(); + final int processCount = pmap.size(); + for (int ip = 0; ip < processCount; ip++) { + final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES); + final String pname = pmap.keyAt(ip); + final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip); + final int uidCount = uids.size(); - proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname); - for (int i = 0; i < uidCount; i++) { - final int puid = uids.keyAt(i); - final ProcessRecord r = mService.getProcessNames().get(pname, puid); - if (dumpPackage != null && (r == null - || !r.pkgList.containsKey(dumpPackage))) { - continue; + proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname); + for (int i = 0; i < uidCount; i++) { + final int puid = uids.keyAt(i); + final ProcessRecord r = mService.getProcessNames().get(pname, puid); + if (dumpPackage != null && (r == null + || !r.pkgList.containsKey(dumpPackage))) { + continue; + } + final BadProcessInfo info = uids.valueAt(i); + final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES); + proto.write(AppErrorsProto.BadProcess.Entry.UID, puid); + proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time); + proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg); + proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg); + proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack); + proto.end(etoken); } - final BadProcessInfo info = uids.valueAt(i); - final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES); - proto.write(AppErrorsProto.BadProcess.Entry.UID, puid); - proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time); - proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg); - proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg); - proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack); - proto.end(etoken); + proto.end(btoken); } - proto.end(btoken); } - } - proto.end(token); + proto.end(token); + } } boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) { @@ -272,12 +282,16 @@ class AppErrors { return needSep; } - boolean isBadProcessLocked(final String processName, final int uid) { - return mBadProcesses.get(processName, uid) != null; + boolean isBadProcess(final String processName, final int uid) { + synchronized (mBadProcesses) { + return mBadProcesses.get(processName, uid) != null; + } } - void clearBadProcessLocked(final String processName, final int uid) { - mBadProcesses.remove(processName, uid); + void clearBadProcess(final String processName, final int uid) { + synchronized (mBadProcesses) { + mBadProcesses.remove(processName, uid); + } } void resetProcessCrashTimeLocked(final String processName, final int uid) { @@ -747,8 +761,10 @@ class AppErrors { if (!app.isolated) { // XXX We don't have a way to mark isolated processes // as bad, since they don't have a peristent identity. - mBadProcesses.put(app.processName, app.uid, - new BadProcessInfo(now, shortMsg, longMsg, stackTrace)); + synchronized (mBadProcesses) { + mBadProcesses.put(app.processName, app.uid, + new BadProcessInfo(now, shortMsg, longMsg, stackTrace)); + } mProcessCrashTimes.remove(app.processName, app.uid); } app.bad = true; diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 960d26b5da34..6948f9061038 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -281,7 +281,7 @@ public final class BroadcastQueue { } private final void processCurBroadcastLocked(BroadcastRecord r, - ProcessRecord app, boolean skipOomAdj) throws RemoteException { + ProcessRecord app) throws RemoteException { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Process cur broadcast " + r + " for app " + app); if (app.thread == null) { @@ -297,9 +297,11 @@ public final class BroadcastQueue { app.curReceivers.add(r); app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER); mService.mProcessList.updateLruProcessLocked(app, false, null); - if (!skipOomAdj) { - mService.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE); - } + // Make sure the oom adj score is updated before delivering the broadcast. + // Force an update, even if there are other pending requests, overall it still saves time, + // because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)). + mService.enqueueOomAdjTargetLocked(app); + mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); // Tell the application to launch this receiver. r.intent.setComponent(r.curComponent); @@ -340,7 +342,7 @@ public final class BroadcastQueue { } try { mPendingBroadcast = null; - processCurBroadcastLocked(br, app, false); + processCurBroadcastLocked(br, app); didSomething = true; } catch (Exception e) { Slog.w(TAG, "Exception in new application when starting receiver " @@ -501,6 +503,7 @@ public final class BroadcastQueue { r.intent.setComponent(null); if (r.curApp != null && r.curApp.curReceivers.contains(r)) { r.curApp.curReceivers.remove(r); + mService.enqueueOomAdjTargetLocked(r.curApp); } if (r.curFilter != null) { r.curFilter.receiverList.curBroadcast = null; @@ -562,7 +565,7 @@ public final class BroadcastQueue { Slog.i(TAG, "Resuming delayed broadcast"); br.curComponent = null; br.state = BroadcastRecord.IDLE; - processNextBroadcast(false); + processNextBroadcastLocked(false, false); } } } @@ -604,7 +607,7 @@ public final class BroadcastQueue { } private void deliverToRegisteredReceiverLocked(BroadcastRecord r, - BroadcastFilter filter, boolean ordered, int index, boolean skipOomAdj) { + BroadcastFilter filter, boolean ordered, int index) { boolean skip = false; if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid, filter.packageName, filter.owningUid)) { @@ -790,10 +793,9 @@ public final class BroadcastQueue { // are already core system stuff so don't matter for this. r.curApp = filter.receiverList.app; filter.receiverList.app.curReceivers.add(r); - if (!skipOomAdj) { - mService.updateOomAdjLocked(r.curApp, true, - OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); - } + mService.enqueueOomAdjTargetLocked(r.curApp); + mService.updateOomAdjPendingTargetsLocked( + OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); } } try { @@ -827,6 +829,8 @@ public final class BroadcastQueue { filter.receiverList.app.removeAllowBackgroundActivityStartsToken(r); if (ordered) { filter.receiverList.app.curReceivers.remove(r); + // Something wrong, its oom adj could be downgraded, but not in a hurry. + mService.enqueueOomAdjTargetLocked(r.curApp); } } // And BroadcastRecord state related to ordered delivery, if appropriate @@ -946,7 +950,7 @@ public final class BroadcastQueue { return true; } - final void processNextBroadcast(boolean fromMsg) { + private void processNextBroadcast(boolean fromMsg) { synchronized (mService) { processNextBroadcastLocked(fromMsg, false); } @@ -990,7 +994,7 @@ public final class BroadcastQueue { "Delivering non-ordered on [" + mQueueName + "] to registered " + target + ": " + r); deliverToRegisteredReceiverLocked(r, - (BroadcastFilter) target, false, i, skipOomAdj); + (BroadcastFilter) target, false, i); } addBroadcastToHistoryLocked(r); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast [" @@ -1046,7 +1050,8 @@ public final class BroadcastQueue { // If we had finished the last ordered broadcast, then // make sure all processes have correct oom and sched // adjustments. - mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); + mService.updateOomAdjPendingTargetsLocked( + OomAdjuster.OOM_ADJ_REASON_START_RECEIVER); } // when we have no more ordered broadcast on this queue, stop logging @@ -1288,7 +1293,7 @@ public final class BroadcastQueue { "Delivering ordered [" + mQueueName + "] to registered " + filter + ": " + r); - deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx, skipOomAdj); + deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx); if (r.receiver == null || !r.ordered) { // The receiver has already finished, so schedule to // process the next one. @@ -1616,7 +1621,7 @@ public final class BroadcastQueue { app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats); maybeAddAllowBackgroundActivityStartsToken(app, r); - processCurBroadcastLocked(r, app, skipOomAdj); + processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { Slog.w(TAG, "Exception when sending broadcast to " @@ -1750,7 +1755,7 @@ public final class BroadcastQueue { ? r.curComponent.flattenToShortString() : "(null)")); r.curComponent = null; r.state = BroadcastRecord.IDLE; - processNextBroadcast(false); + processNextBroadcastLocked(false, false); return; } diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index d9fde0f6728a..c5047e5eed03 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -208,6 +208,8 @@ public final class CachedAppOptimizer { @GuardedBy("mPhenotypeFlagLock") private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION; private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER; + @GuardedBy("this") + private int mFreezerDisableCount = 1; // Freezer is initially disabled, until enabled private final Random mRandom = new Random(); @GuardedBy("mPhenotypeFlagLock") @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE; @@ -420,25 +422,82 @@ public final class CachedAppOptimizer { } /** - * Determines whether the freezer is correctly supported by this system + * Enables or disabled the app freezer. + * @param enable Enables the freezer if true, disables it if false. + * @return true if the operation completed successfully, false otherwise. + */ + public synchronized boolean enableFreezer(boolean enable) { + if (!mUseFreezer) { + return false; + } + + if (enable) { + mFreezerDisableCount--; + + if (mFreezerDisableCount > 0) { + return true; + } else if (mFreezerDisableCount < 0) { + Slog.e(TAG_AM, "unbalanced call to enableFreezer, ignoring"); + mFreezerDisableCount = 0; + return false; + } + } else { + mFreezerDisableCount++; + + if (mFreezerDisableCount > 1) { + return true; + } + } + + try { + enableFreezerInternal(enable); + return true; + } catch (java.lang.RuntimeException e) { + if (enable) { + mFreezerDisableCount = 0; + } else { + mFreezerDisableCount = 1; + } + + Slog.e(TAG_AM, "Exception handling freezer state (enable: " + enable + "): " + + e.toString()); + } + + return false; + } + + /** + * Enable or disable the freezer. When enable == false all frozen processes are unfrozen, + * but aren't removed from the freezer. While in this state, processes can be added or removed + * by using Process.setProcessFrozen(), but they wouldn't be actually frozen until the freezer + * is enabled. If enable == true all processes in the freezer are frozen. + * + * @param enable Specify whether to enable (true) or disable (false) the freezer. + * + * @hide + */ + private static native void enableFreezerInternal(boolean enable); + + /** + * Determines whether the freezer is supported by this system */ public static boolean isFreezerSupported() { boolean supported = false; FileReader fr = null; try { - fr = new FileReader("/dev/freezer/frozen/freezer.killable"); - int i = fr.read(); + fr = new FileReader("/sys/fs/cgroup/freezer/cgroup.freeze"); + char state = (char) fr.read(); - if ((char) i == '1') { + if (state == '1' || state == '0') { supported = true; } else { - Slog.w(TAG_AM, "Freezer killability is turned off, disabling freezer"); + Slog.e(TAG_AM, "unexpected value in cgroup.freeze"); } } catch (java.io.FileNotFoundException e) { - Slog.d(TAG_AM, "Freezer.killable not present, disabling freezer"); + Slog.d(TAG_AM, "cgroup.freeze not present"); } catch (Exception e) { - Slog.d(TAG_AM, "Unable to read freezer.killable, disabling freezer: " + e.toString()); + Slog.d(TAG_AM, "unable to read cgroup.freeze: " + e.toString()); } if (fr != null) { @@ -471,6 +530,8 @@ public final class CachedAppOptimizer { if (mUseFreezer && mFreezeHandler == null) { Slog.d(TAG_AM, "Freezer enabled"); + enableFreezer(true); + if (!mCachedAppOptimizerThread.isAlive()) { mCachedAppOptimizerThread.start(); } @@ -479,6 +540,8 @@ public final class CachedAppOptimizer { Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), Process.THREAD_GROUP_SYSTEM); + } else { + enableFreezer(false); } } diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index bfba4afcd4e4..1155569c6b36 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -198,22 +198,10 @@ public class ContentProviderHelper { if (providerRunning) { cpi = cpr.info; - String msg; if (r != null && cpr.canRunHere(r)) { - if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) { - throw new SecurityException("Content provider lookup " - + cpr.name.flattenToShortString() - + " failed: association not allowed with package " + msg); - } - checkTime(startTime, - "getContentProviderImpl: before checkContentProviderPermission"); - if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) - != null) { - throw new SecurityException(msg); - } - checkTime(startTime, - "getContentProviderImpl: after checkContentProviderPermission"); + checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser, + cpr.name.flattenToShortString(), startTime); // This provider has been published or is in the process // of being published... but it is also allowed to run @@ -234,26 +222,14 @@ public class ContentProviderHelper { } catch (RemoteException e) { } - if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) { - throw new SecurityException( - "Content provider lookup " + cpr.name.flattenToShortString() - + " failed: association not allowed with package " + msg); - } - checkTime(startTime, - "getContentProviderImpl: before checkContentProviderPermission"); - if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) - != null) { - throw new SecurityException(msg); - } - checkTime(startTime, - "getContentProviderImpl: after checkContentProviderPermission"); + checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser, + cpr.name.flattenToShortString(), startTime); final long origId = Binder.clearCallingIdentity(); checkTime(startTime, "getContentProviderImpl: incProviderCountLocked"); - // In this case the provider instance already exists, so we can - // return it right away. + // In this case the provider instance already exists so we can return it right away. conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag, stable, true, startTime, mService.mProcessList); @@ -328,19 +304,8 @@ public class ContentProviderHelper { cpi.applicationInfo = mService.getAppInfoForUser(cpi.applicationInfo, userId); checkTime(startTime, "getContentProviderImpl: got app info for user"); - String msg; - if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) { - throw new SecurityException("Content provider lookup " + name - + " failed: association not allowed with package " + msg); - } - checkTime(startTime, - "getContentProviderImpl: before checkContentProviderPermission"); - if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton)) - != null) { - throw new SecurityException(msg); - } - checkTime(startTime, - "getContentProviderImpl: after checkContentProviderPermission"); + checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, !singleton, + name, startTime); if (!mService.mProcessesReady && !cpi.processName.equals("system")) { // If this content provider does not run in the system @@ -352,10 +317,12 @@ public class ContentProviderHelper { // If system providers are not installed yet we aggressively crash to avoid // creating multiple instance of these providers and then bad things happen! - if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp() - && "system".equals(cpi.processName)) { - throw new IllegalStateException("Cannot access system provider: '" - + cpi.authority + "' before system providers are installed!"); + synchronized (this) { + if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp() + && "system".equals(cpi.processName)) { + throw new IllegalStateException("Cannot access system provider: '" + + cpi.authority + "' before system providers are installed!"); + } } // Make sure that the user who owns this provider is running. If not, @@ -605,6 +572,23 @@ public class ContentProviderHelper { return cpr.newHolder(conn, false); } + private void checkAssociationAndPermissionLocked(ProcessRecord callingApp, ProviderInfo cpi, + int callingUid, int userId, boolean checkUser, String cprName, long startTime) { + String msg; + if ((msg = checkContentProviderAssociation(callingApp, callingUid, cpi)) != null) { + throw new SecurityException("Content provider lookup " + cprName + + " failed: association not allowed with package " + msg); + } + checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); + if ((msg = checkContentProviderPermission( + cpi, Binder.getCallingPid(), Binder.getCallingUid(), userId, checkUser, + callingApp != null ? callingApp.toString() : null)) + != null) { + throw new SecurityException(msg); + } + checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission"); + } + void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) { if (providers == null) { return; @@ -623,7 +607,7 @@ public class ContentProviderHelper { } final long origId = Binder.clearCallingIdentity(); - + boolean providersPublished = false; for (int i = 0, size = providers.size(); i < size; i++) { ContentProviderHolder src = providers.get(i); if (src == null || src.info == null || src.provider == null) { @@ -636,6 +620,7 @@ public class ContentProviderHelper { if (DEBUG_MU) { Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid); } + providersPublished = true; ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name); mProviderMap.putProviderByClass(comp, dst); @@ -673,8 +658,19 @@ public class ContentProviderHelper { dst.onProviderPublishStatusLocked(true); } dst.mRestartCount = 0; + } + + // update the app's oom adj value and each provider's usage stats + if (providersPublished) { mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); - maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); + for (int i = 0, size = providers.size(); i < size; i++) { + ContentProviderHolder src = providers.get(i); + if (src == null || src.info == null || src.provider == null) { + continue; + } + maybeUpdateProviderUsageStatsLocked(r, + src.info.packageName, src.info.authority); + } } Binder.restoreCallingIdentity(origId); @@ -702,7 +698,8 @@ public class ContentProviderHelper { throw new NullPointerException("connection is null"); } if (decProviderCountLocked(conn, null, null, stable)) { - mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER); + mService.updateOomAdjLocked(conn.provider.proc, + OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER); } } } finally { @@ -738,7 +735,8 @@ public class ContentProviderHelper { ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId); if (localCpr.hasExternalProcessHandles()) { if (localCpr.removeExternalProcessHandleLocked(token)) { - mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER); + mService.updateOomAdjLocked(localCpr.proc, + OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER); } else { Slog.e(TAG, "Attempt to remove content provider " + localCpr + " with no external reference for token: " + token + "."); @@ -997,17 +995,19 @@ public class ContentProviderHelper { + "; expected to find a valid ContentProvider for this authority"; } + final int callingPid = Binder.getCallingPid(); ProcessRecord r; + final String appName; synchronized (mService.mPidsSelfLocked) { - r = mService.mPidsSelfLocked.get(Binder.getCallingPid()); - } - if (r == null) { - return "Failed to find PID " + Binder.getCallingPid(); + r = mService.mPidsSelfLocked.get(callingPid); + if (r == null) { + return "Failed to find PID " + callingPid; + } + appName = r.toString(); } - synchronized (mService) { - return checkContentProviderPermissionLocked(cpi, r, userId, true); - } + return checkContentProviderPermission(cpi, callingPid, Binder.getCallingUid(), + userId, true, appName); } int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) { @@ -1163,13 +1163,14 @@ public class ContentProviderHelper { } } } - if (providers != null) { - mService.mSystemThread.installSystemProviders(providers); - } - synchronized (mService) { + synchronized (this) { + if (providers != null) { + mService.mSystemThread.installSystemProviders(providers); + } mSystemProvidersInstalled = true; } + mService.mConstants.start(mService.mContext.getContentResolver()); mService.mCoreSettingsObserver = new CoreSettingsObserver(mService); mService.mActivityTaskManager.installSystemProviders(); @@ -1305,10 +1306,8 @@ public class ContentProviderHelper { * given {@link ProviderInfo}. Final permission checking is always done * in {@link ContentProvider}. */ - private String checkContentProviderPermissionLocked(ProviderInfo cpi, ProcessRecord r, - int userId, boolean checkUser) { - final int callingPid = (r != null) ? r.pid : Binder.getCallingPid(); - final int callingUid = (r != null) ? r.uid : Binder.getCallingUid(); + private String checkContentProviderPermission(ProviderInfo cpi, int callingPid, int callingUid, + int userId, boolean checkUser, String appName) { boolean checkedGrants = false; if (checkUser) { // Looking for cross-user grants before enforcing the typical cross-users permissions @@ -1376,8 +1375,8 @@ public class ContentProviderHelper { suffix = " requires " + cpi.readPermission + " or " + cpi.writePermission; } final String msg = "Permission Denial: opening provider " + cpi.name - + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid - + ", uid=" + callingUid + ")" + suffix; + + " from " + (appName != null ? appName : "(null)") + + " (pid=" + callingPid + ", uid=" + callingUid + ")" + suffix; Slog.w(TAG, msg); return msg; } @@ -1398,18 +1397,17 @@ public class ContentProviderHelper { } ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId, int pmFlags) { - ProviderInfo pi = null; ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId); if (cpr != null) { - pi = cpr.info; + return cpr.info; } else { try { - pi = AppGlobals.getPackageManager().resolveContentProvider( + return AppGlobals.getPackageManager().resolveContentProvider( authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userId); } catch (RemoteException ex) { + return null; } } - return pi; } private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName, @@ -1419,7 +1417,6 @@ public class ContentProviderHelper { return; } - UserState userState = mService.mUserController.getStartedUserState(app.userId); if (userState == null) return; final long now = SystemClock.elapsedRealtime(); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index bad042cd3a68..fc330313373e 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -81,6 +81,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.pm.ServiceInfo; import android.os.Debug; import android.os.Handler; @@ -94,6 +95,8 @@ import android.os.Trace; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.LongSparseArray; +import android.util.Pair; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -103,10 +106,13 @@ import com.android.internal.app.procstats.ProcessStats; import com.android.internal.compat.IPlatformCompat; import com.android.server.LocalServices; import com.android.server.ServiceThread; +import com.android.server.compat.CompatChange; +import com.android.server.compat.PlatformCompat; import com.android.server.wm.ActivityServiceConnectionsHolder; import com.android.server.wm.WindowProcessController; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -209,12 +215,99 @@ public final class OomAdjuster { private final ProcessList mProcessList; private final int mNumSlots; - private ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>(); - private ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>(); - private ActiveUids mTmpUidRecords; - private ArrayDeque<ProcessRecord> mTmpQueue; + private final ArrayList<ProcessRecord> mTmpProcessList = new ArrayList<ProcessRecord>(); + private final ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>(); + private final ActiveUids mTmpUidRecords; + private final ArrayDeque<ProcessRecord> mTmpQueue; + private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>(); + + private final PlatformCompatCache mPlatformCompatCache; + + private static class PlatformCompatCache { + private final PlatformCompat mPlatformCompat; + private final IPlatformCompat mIPlatformCompatProxy; + private final LongSparseArray<CacheItem> mCaches = new LongSparseArray<>(); + private final boolean mCacheEnabled; + + PlatformCompatCache(long[] compatChanges) { + IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); + if (b instanceof PlatformCompat) { + mPlatformCompat = (PlatformCompat) ServiceManager.getService( + Context.PLATFORM_COMPAT_SERVICE); + for (long changeId: compatChanges) { + mCaches.put(changeId, new CacheItem(mPlatformCompat, changeId)); + } + mIPlatformCompatProxy = null; + mCacheEnabled = true; + } else { + // we are in UT where the platform_compat is not running within the same process + mIPlatformCompatProxy = IPlatformCompat.Stub.asInterface(b); + mPlatformCompat = null; + mCacheEnabled = false; + } + } + + boolean isChangeEnabled(long changeId, ApplicationInfo app) throws RemoteException { + return mCacheEnabled ? mCaches.get(changeId).isChangeEnabled(app) + : mIPlatformCompatProxy.isChangeEnabled(changeId, app); + } + + void invalidate(ApplicationInfo app) { + for (int i = mCaches.size() - 1; i >= 0; i--) { + mCaches.valueAt(i).invalidate(app); + } + } + + static class CacheItem implements CompatChange.ChangeListener { + private final PlatformCompat mPlatformCompat; + private final long mChangeId; + private final Object mLock = new Object(); + + private final ArrayMap<String, Pair<Boolean, WeakReference<ApplicationInfo>>> mCache = + new ArrayMap<>(); + + CacheItem(PlatformCompat platformCompat, long changeId) { + mPlatformCompat = platformCompat; + mChangeId = changeId; + mPlatformCompat.registerListener(changeId, this); + } + + boolean isChangeEnabled(ApplicationInfo app) { + synchronized (mLock) { + final int index = mCache.indexOfKey(app.packageName); + Pair<Boolean, WeakReference<ApplicationInfo>> p; + if (index < 0) { + p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app), + new WeakReference<>(app)); + mCache.put(app.packageName, p); + return p.first; + } + p = mCache.valueAt(index); + if (p.second.get() == app) { + return p.first; + } + // Cache is invalid, regenerate it + p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app), + new WeakReference<>(app)); + mCache.setValueAt(index, p); + return p.first; + } + } - private final IPlatformCompat mPlatformCompat; + void invalidate(ApplicationInfo app) { + synchronized (mLock) { + mCache.remove(app.packageName); + } + } + + @Override + public void onCompatChange(String packageName) { + synchronized (mLock) { + mCache.remove(packageName); + } + } + } + } OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) { this(service, processList, activeUids, createAdjusterThread()); @@ -263,8 +356,9 @@ public final class OomAdjuster { mTmpQueue = new ArrayDeque<ProcessRecord>(mConstants.CUR_MAX_CACHED_PROCESSES << 1); mNumSlots = ((ProcessList.CACHED_APP_MAX_ADJ - ProcessList.CACHED_APP_MIN_ADJ + 1) >> 1) / ProcessList.CACHED_APP_IMPORTANCE_LEVELS; - IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); - mPlatformCompat = IPlatformCompat.Stub.asInterface(b); + mPlatformCompatCache = new PlatformCompatCache(new long[] { + PROCESS_CAPABILITY_CHANGE_ID, CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID + }); } void initSettings() { @@ -333,6 +427,8 @@ public final class OomAdjuster { // need to do a complete oom adj. final int cachedAdj = app.getCurRawAdj() >= ProcessList.CACHED_APP_MIN_ADJ ? app.getCurRawAdj() : ProcessList.UNKNOWN_ADJ; + // Check if this process is in the pending list too, remove from pending list if so. + mPendingProcessSet.remove(app); boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false, SystemClock.uptimeMillis()); if (oomAdjAll @@ -360,6 +456,9 @@ public final class OomAdjuster { uidRec.reset(); } + // Check if this process is in the pending list too, remove from pending list if so. + mPendingProcessSet.remove(app); + computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now, false, true); boolean success = applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime()); @@ -401,6 +500,8 @@ public final class OomAdjuster { @GuardedBy("mService") void updateOomAdjLocked(String oomAdjReason) { final ProcessRecord topApp = mService.getTopAppLocked(); + // Clear any pending ones because we are doing a full update now. + mPendingProcessSet.clear(); updateOomAdjLockedInner(oomAdjReason, topApp , null, null, true, true); } @@ -434,6 +535,8 @@ public final class OomAdjuster { app.containsCycle = false; app.procStateChanged = false; app.resetCachedInfo(); + // Check if this process is in the pending list too, remove from pending list if so. + mPendingProcessSet.remove(app); boolean success = updateOomAdjLocked(app, cachedAdj, topApp, false, SystemClock.uptimeMillis()); if (!success || (wasCached == app.isCached() && oldAdj != ProcessList.INVALID_ADJ @@ -486,6 +589,10 @@ public final class OomAdjuster { } queue.offer(service); service.mReachable = true; + // During scanning the reachable dependants, remove them from the pending oomadj + // targets list if it's possible, as they've been added into the immediate + // oomadj targets list 'processes' above. + mPendingProcessSet.remove(service); } for (int i = pr.conProviders.size() - 1; i >= 0; i--) { ContentProviderConnection cpc = pr.conProviders.get(i); @@ -499,6 +606,10 @@ public final class OomAdjuster { } queue.offer(provider); provider.mReachable = true; + // During scanning the reachable dependants, remove them from the pending oomadj + // targets list if it's possible, as they've been added into the immediate + // oomadj targets list 'processes' above. + mPendingProcessSet.remove(provider); } } @@ -523,12 +634,67 @@ public final class OomAdjuster { applyOomAdjLocked(app, false, SystemClock.uptimeMillis(), SystemClock.elapsedRealtime()); } + mTmpProcessList.clear(); mService.mOomAdjProfiler.oomAdjEnded(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return true; } /** + * Enqueue the given process for a later oom adj update + */ + @GuardedBy("mService") + void enqueueOomAdjTargetLocked(ProcessRecord app) { + if (app != null) { + mPendingProcessSet.add(app); + } + } + + @GuardedBy("mService") + void removeOomAdjTargetLocked(ProcessRecord app, boolean procDied) { + if (app != null) { + mPendingProcessSet.remove(app); + if (procDied) { + mPlatformCompatCache.invalidate(app.info); + } + } + } + + /** + * Kick off an oom adj update pass for the pending targets which are enqueued via + * {@link #enqueueOomAdjTargetLocked}. + */ + @GuardedBy("mService") + void updateOomAdjPendingTargetsLocked(String oomAdjReason) { + if (mPendingProcessSet.isEmpty()) { + return; + } + final ProcessRecord topApp = mService.getTopAppLocked(); + + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReason); + mService.mOomAdjProfiler.oomAdjStarted(); + + final ArrayList<ProcessRecord> processes = mTmpProcessList; + final ActiveUids uids = mTmpUidRecords; + uids.clear(); + processes.clear(); + for (int i = mPendingProcessSet.size() - 1; i >= 0; i--) { + final ProcessRecord app = mPendingProcessSet.valueAt(i); + if (app.uidRecord != null) { + uids.put(app.uidRecord.uid, app.uidRecord); + } + processes.add(app); + } + + updateOomAdjLockedInner(oomAdjReason, topApp, processes, uids, true, false); + processes.clear(); + mPendingProcessSet.clear(); + + mService.mOomAdjProfiler.oomAdjEnded(); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + /** * Update OomAdj for all processes within the given list (could be partial), or the whole LRU * list if the given list is null; when it's partial update, each process's client proc won't * get evaluated recursively here. @@ -1555,7 +1721,7 @@ public final class OomAdjuster { boolean enabled = false; try { - enabled = mPlatformCompat.isChangeEnabled( + enabled = mPlatformCompatCache.isChangeEnabled( CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID, s.appInfo); } catch (RemoteException e) { } @@ -1742,7 +1908,7 @@ public final class OomAdjuster { clientProcState = PROCESS_STATE_BOUND_TOP; boolean enabled = false; try { - enabled = mPlatformCompat.isChangeEnabled( + enabled = mPlatformCompatCache.isChangeEnabled( PROCESS_CAPABILITY_CHANGE_ID, client.info); } catch (RemoteException e) { } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 76089f8fba01..87898d80bd46 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1528,15 +1528,18 @@ public final class ProcessList { && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY && proc.lastCachedPss >= 4000) { // Turn this condition on to cause killing to happen regularly, for testing. - if (proc.baseProcessTracker != null) { - proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, proc.lastCachedPss); - for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); - FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, - proc.info.uid, - holder.state.getName(), - holder.state.getPackage(), - proc.lastCachedPss, holder.appVersion); + synchronized (mService.mProcessStats.mLock) { + if (proc.baseProcessTracker != null) { + proc.baseProcessTracker.reportCachedKill( + proc.pkgList.mPkgList, proc.lastCachedPss); + for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { + ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); + FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, + proc.info.uid, + holder.state.getName(), + holder.state.getPackage(), + proc.lastCachedPss, holder.appVersion); + } } } proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", @@ -1549,16 +1552,18 @@ public final class ProcessList { if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc .lastCachedPss); if (proc.lastCachedPss >= getCachedRestoreThresholdKb()) { - if (proc.baseProcessTracker != null) { - proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, - proc.lastCachedPss); - for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { - ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); - FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, - proc.info.uid, - holder.state.getName(), - holder.state.getPackage(), - proc.lastCachedPss, holder.appVersion); + synchronized (mService.mProcessStats.mLock) { + if (proc.baseProcessTracker != null) { + proc.baseProcessTracker.reportCachedKill(proc.pkgList.mPkgList, + proc.lastCachedPss); + for (int ipkg = proc.pkgList.size() - 1; ipkg >= 0; ipkg--) { + ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg); + FrameworkStatsLog.write(FrameworkStatsLog.CACHED_KILL_REPORTED, + proc.info.uid, + holder.state.getName(), + holder.state.getPackage(), + proc.lastCachedPss, holder.appVersion); + } } } proc.kill(Long.toString(proc.lastCachedPss) + "k from cached", @@ -2360,7 +2365,7 @@ public final class ProcessList { if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) { // If we are in the background, then check to see if this process // is bad. If so, we will just silently fail. - if (mService.mAppErrors.isBadProcessLocked(processName, info.uid)) { + if (mService.mAppErrors.isBadProcess(processName, info.uid)) { if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid + "/" + processName); return null; @@ -2373,11 +2378,11 @@ public final class ProcessList { if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid + "/" + processName); mService.mAppErrors.resetProcessCrashTimeLocked(processName, info.uid); - if (mService.mAppErrors.isBadProcessLocked(processName, info.uid)) { + if (mService.mAppErrors.isBadProcess(processName, info.uid)) { EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, UserHandle.getUserId(info.uid), info.uid, info.processName); - mService.mAppErrors.clearBadProcessLocked(processName, info.uid); + mService.mAppErrors.clearBadProcess(processName, info.uid); if (app != null) { app.bad = false; } @@ -2636,6 +2641,7 @@ public final class ProcessList { mLruProcessServiceStart--; } mLruProcesses.remove(lrui); + mService.removeOomAdjTargetLocked(app, true); } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 06ef58f8cc61..5447605a36d1 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -500,8 +500,8 @@ import java.util.concurrent.atomic.AtomicBoolean; sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); } - /*package*/ void postSetModeOwnerPid(int pid) { - sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid); + /*package*/ void postSetModeOwnerPid(int pid, int mode) { + sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode); } /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { @@ -977,7 +977,9 @@ import java.util.concurrent.atomic.AtomicBoolean; synchronized (mDeviceStateLock) { if (mModeOwnerPid != msg.arg1) { mModeOwnerPid = msg.arg1; - updateSpeakerphoneOn("setNewModeOwner"); + if (msg.arg2 != AudioSystem.MODE_RINGTONE) { + updateSpeakerphoneOn("setNewModeOwner"); + } if (mModeOwnerPid != 0) { mBtHelper.disconnectBluetoothSco(mModeOwnerPid); } diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java index 9ebd75bd0f64..af0e978726e3 100644 --- a/services/core/java/com/android/server/audio/AudioEventLogger.java +++ b/services/core/java/com/android/server/audio/AudioEventLogger.java @@ -60,7 +60,36 @@ public class AudioEventLogger { * @return the same instance of the event */ public Event printLog(String tag) { - Log.i(tag, eventToString()); + return printLog(ALOGI, tag); + } + + public static final int ALOGI = 0; + public static final int ALOGE = 1; + public static final int ALOGW = 2; + public static final int ALOGV = 3; + + /** + * Same as {@link #printLog(String)} with a log type + * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV} + * @param tag + * @return + */ + public Event printLog(int type, String tag) { + switch (type) { + case ALOGI: + Log.i(tag, eventToString()); + break; + case ALOGE: + Log.e(tag, eventToString()); + break; + case ALOGW: + Log.w(tag, eventToString()); + break; + case ALOGV: + default: + Log.v(tag, eventToString()); + break; + } return this; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 673ca1f3da86..4378490d19c5 100755..100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -26,6 +26,10 @@ import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE; +import static com.android.server.audio.AudioEventLogger.Event.ALOGE; +import static com.android.server.audio.AudioEventLogger.Event.ALOGI; +import static com.android.server.audio.AudioEventLogger.Event.ALOGW; + import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; @@ -287,6 +291,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_CHECK_MODE_FOR_UID = 31; private static final int MSG_STREAM_DEVICES_CHANGED = 32; private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33; + private static final int MSG_REINIT_VOLUMES = 34; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -669,6 +674,7 @@ public class AudioService extends IAudioService.Stub public AudioService(Context context, AudioSystemAdapter audioSystem, SystemServerAdapter systemServer) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent("AudioService()")); mContext = context; mContentResolver = context.getContentResolver(); mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); @@ -888,6 +894,9 @@ public class AudioService extends IAudioService.Stub mPrescaleAbsoluteVolume[i] = preScale[i]; } } + + // check on volume initialization + checkVolumeRangeInitialization("AudioService()"); } public void systemReady() { @@ -1015,11 +1024,15 @@ public class AudioService extends IAudioService.Stub if (!mSystemReady || (AudioSystem.checkAudioFlinger() != AudioSystem.AUDIO_STATUS_OK)) { Log.e(TAG, "Audioserver died."); + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "onAudioServerDied() audioserver died")); sendMsg(mAudioHandler, MSG_AUDIO_SERVER_DIED, SENDMSG_NOOP, 0, 0, null, 500); return; } - Log.e(TAG, "Audioserver started."); + Log.i(TAG, "Audioserver started."); + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "onAudioServerDied() audioserver started")); updateAudioHalPids(); @@ -1054,14 +1067,7 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.setForceUse_Async(AudioSystem.FOR_SYSTEM, forSys, "onAudioServerDied"); // Restore stream volumes - int numStreamTypes = AudioSystem.getNumStreamTypes(); - for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { - VolumeStreamState streamState = mStreamStates[streamType]; - AudioSystem.initStreamVolume( - streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10); - - streamState.applyAllVolumes(); - } + onReinitVolumes("after audioserver restart"); // Restore audio volume groups restoreVolumeGroups(); @@ -1159,6 +1165,72 @@ public class AudioService extends IAudioService.Stub setMicMuteFromSwitchInput(); } + private void onReinitVolumes(@NonNull String caller) { + final int numStreamTypes = AudioSystem.getNumStreamTypes(); + // keep track of any error during stream volume initialization + int status = AudioSystem.AUDIO_STATUS_OK; + for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { + VolumeStreamState streamState = mStreamStates[streamType]; + final int res = AudioSystem.initStreamVolume( + streamType, streamState.mIndexMin / 10, streamState.mIndexMax / 10); + if (res != AudioSystem.AUDIO_STATUS_OK) { + status = res; + Log.e(TAG, "Failed to initStreamVolume (" + res + ") for stream " + streamType); + // stream volume initialization failed, no need to try the others, it will be + // attempted again when MSG_REINIT_VOLUMES is handled + break; + } + streamState.applyAllVolumes(); + } + + // did it work? check based on status + if (status != AudioSystem.AUDIO_STATUS_OK) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume failed with " + status + " will retry") + .printLog(ALOGE, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + caller /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + return; + } + + // did it work? check based on min/max values of some basic streams + if (!checkVolumeRangeInitialization(caller)) { + return; + } + + // success + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume succeeded").printLog(ALOGI, TAG)); + } + + /** + * Check volume ranges were properly initialized + * @return true if volume ranges were successfully initialized + */ + private boolean checkVolumeRangeInitialization(String caller) { + boolean success = true; + final int[] basicStreams = { AudioSystem.STREAM_ALARM, AudioSystem.STREAM_RING, + AudioSystem.STREAM_MUSIC, AudioSystem.STREAM_VOICE_CALL, + AudioSystem.STREAM_ACCESSIBILITY }; + for (int streamType : basicStreams) { + final AudioAttributes aa = new AudioAttributes.Builder() + .setInternalLegacyStreamType(streamType).build(); + if (AudioSystem.getMaxVolumeIndexForAttributes(aa) < 0 + || AudioSystem.getMinVolumeIndexForAttributes(aa) < 0) { + success = false; + break; + } + } + if (!success) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + caller + ": initStreamVolume succeeded but invalid mix/max levels, will retry") + .printLog(ALOGW, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + caller /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + } + return success; + } + private void onDispatchAudioServerStateChange(boolean state) { synchronized (mAudioServerStateListeners) { for (AsdProxy asdp : mAudioServerStateListeners.values()) { @@ -3747,13 +3819,15 @@ public class AudioService extends IAudioService.Stub private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; - private String mPackage; + private final boolean mIsPrivileged; + private final String mPackage; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client - SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) { + SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { mCb = cb; mPid = pid; mUid = uid; + mIsPrivileged = isPrivileged; mPackage = caller; } @@ -3765,12 +3839,13 @@ public class AudioService extends IAudioService.Stub if (index < 0) { Log.w(TAG, "unregistered setMode() client died"); } else { - newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG); + newModeOwnerPid = setModeInt( + AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG); } } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); + mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode()); } public int getPid() { @@ -3796,6 +3871,10 @@ public class AudioService extends IAudioService.Stub public String getPackage() { return mPackage; } + + public boolean isPrivileged() { + return mIsPrivileged; + } } /** @see AudioManager#setMode(int) */ @@ -3847,18 +3926,19 @@ public class AudioService extends IAudioService.Stub + " without permission or being mode owner"); return; } - newModeOwnerPid = setModeInt( - mode, cb, callingPid, Binder.getCallingUid(), callingPackage); + newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(), + hasModifyPhoneStatePermission, callingPackage); } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes - mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); + mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode()); } // setModeInt() returns a valid PID if the audio mode was successfully set to // any mode other than NORMAL. @GuardedBy("mDeviceBroker.mSetModeLock") - private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) { + private int setModeInt( + int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) { if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", uid=" + uid + ", caller=" + caller + ")"); @@ -3910,7 +3990,7 @@ public class AudioService extends IAudioService.Stub } } else { if (hdlr == null) { - hdlr = new SetModeDeathHandler(cb, pid, uid, caller); + hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller); } // Register for client death notification try { @@ -3969,7 +4049,8 @@ public class AudioService extends IAudioService.Stub // change of mode may require volume to be re-applied on some devices updateAbsVolumeMultiModeDevices(oldMode, actualMode); - if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) { + if (actualMode == AudioSystem.MODE_IN_COMMUNICATION + && !hdlr.isPrivileged()) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, SENDMSG_QUEUE, @@ -5663,7 +5744,15 @@ public class AudioService extends IAudioService.Stub mIndexMin = MIN_STREAM_VOLUME[streamType] * 10; mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex() mIndexMax = MAX_STREAM_VOLUME[streamType] * 10; - AudioSystem.initStreamVolume(streamType, mIndexMin / 10, mIndexMax / 10); + final int status = AudioSystem.initStreamVolume( + streamType, mIndexMin / 10, mIndexMax / 10); + if (status != AudioSystem.AUDIO_STATUS_OK) { + sLifecycleLogger.log(new AudioEventLogger.StringEvent( + "VSS() stream:" + streamType + " initStreamVolume=" + status) + .printLog(ALOGE, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + "VSS()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + } readSettings(); mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION); @@ -6519,8 +6608,8 @@ public class AudioService extends IAudioService.Stub CHECK_MODE_FOR_UID_PERIOD_MS); break; } - // For now just log the fact that an app is hogging the audio mode. - // TODO(b/160260850): remove abusive app from audio mode stack. + setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(), + h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID"); mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; @@ -6534,6 +6623,10 @@ public class AudioService extends IAudioService.Stub case MSG_UPDATE_VOLUME_STATES_FOR_DEVICE: onUpdateVolumeStatesForAudioDevice(msg.arg1, (String) msg.obj); break; + + case MSG_REINIT_VOLUMES: + onReinitVolumes((String) msg.obj); + break; } } } @@ -7463,12 +7556,16 @@ public class AudioService extends IAudioService.Stub //========================================================================================== // AudioService logging and dumpsys //========================================================================================== + static final int LOG_NB_EVENTS_LIFECYCLE = 20; static final int LOG_NB_EVENTS_PHONE_STATE = 20; static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30; static final int LOG_NB_EVENTS_FORCE_USE = 20; static final int LOG_NB_EVENTS_VOLUME = 40; static final int LOG_NB_EVENTS_DYN_POLICY = 10; + static final AudioEventLogger sLifecycleLogger = new AudioEventLogger(LOG_NB_EVENTS_LIFECYCLE, + "audio services lifecycle"); + final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE, "phone state (logged after successful call to AudioSystem.setPhoneState(int, int))"); @@ -7545,6 +7642,7 @@ public class AudioService extends IAudioService.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + sLifecycleLogger.dump(pw); if (mAudioHandler != null) { pw.println("\nMessage handler (watch for unhandled messages):"); mAudioHandler.dump(new PrintWriterPrinter(pw), " "); diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index bbc29b0bf89b..bbc29b0bf89b 100755..100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 9128359250df..4be596de3af1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -195,6 +195,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> if (listener != null) { listener.onAuthenticationFailed(getSensorId()); } + } else { + mAlreadyDone = true; } } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java index dec40e39fb29..e585b48d49b6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java @@ -176,6 +176,11 @@ public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinde // TODO(b/157790417): Move this to the scheduler void binderDiedInternal(boolean clearListener) { + if (isAlreadyDone()) { + Slog.w(TAG, "Binder died but client is finished, ignoring"); + return; + } + // If the current client dies we should cancel the current operation. if (this instanceof Interruptable) { Slog.e(TAG, "Binder died, cancelling client"); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java index 32bb2db77ddc..c134a3faca4f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java @@ -25,13 +25,14 @@ import android.app.UserSwitchObserver; import android.content.Context; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricConstants; +import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; import android.hardware.face.Face; -import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorProperties; import android.hardware.face.IFaceServiceReceiver; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -110,6 +111,9 @@ class Face10 implements IHwBinder.DeathRecipient { @Override public void onUserSwitching(int newUserId) { scheduleInternalCleanup(newUserId); + scheduleGetFeature(new Binder(), newUserId, + BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, + null, mContext.getOpPackageName()); } }; @@ -361,6 +365,9 @@ class Face10 implements IHwBinder.DeathRecipient { if (halId != 0) { scheduleLoadAuthenticatorIds(); scheduleInternalCleanup(ActivityManager.getCurrentUser()); + scheduleGetFeature(new Binder(), ActivityManager.getCurrentUser(), + BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, + null, mContext.getOpPackageName()); } else { Slog.e(TAG, "Unable to set callback"); mDaemon = null; @@ -383,7 +390,7 @@ class Face10 implements IHwBinder.DeathRecipient { // is safe because authenticatorIds only change when A) new template has been enrolled, // or B) all templates are removed. mHandler.post(() -> { - for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) { + for (UserInfo user : UserManager.get(mContext).getAliveUsers()) { final int targetUserId = user.id; if (!mAuthenticatorIds.containsKey(targetUserId)) { scheduleUpdateActiveUserWithoutHandler(targetUserId); @@ -451,7 +458,7 @@ class Face10 implements IHwBinder.DeathRecipient { } void scheduleGetFeature(@NonNull IBinder token, int userId, int feature, - @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { + @Nullable ClientMonitorCallbackConverter listener, @NonNull String opPackageName) { mHandler.post(() -> { final List<Face> faces = getEnrolledFaces(userId); if (faces.isEmpty()) { @@ -462,10 +469,22 @@ class Face10 implements IHwBinder.DeathRecipient { scheduleUpdateActiveUserWithoutHandler(userId); final int faceId = faces.get(0).getBiometricId(); - final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, - mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, - opPackageName, mSensorId, feature, faceId); - mScheduler.scheduleClientMonitor(client); + final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon, + token, listener, userId, opPackageName, mSensorId, feature, faceId); + mScheduler.scheduleClientMonitor(client, new ClientMonitor.Callback() { + @Override + public void onClientFinished( + @NonNull ClientMonitor<?> clientMonitor, boolean success) { + if (success && feature == BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION) { + final int settingsValue = client.getValue() ? 1 : 0; + Slog.d(TAG, "Updating attention value for user: " + userId + + " to value: " + settingsValue); + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, + settingsValue, userId); + } + } + }); }); } @@ -480,7 +499,8 @@ class Face10 implements IHwBinder.DeathRecipient { * notifying the previous caller that the interrupting operation is complete (e.g. the * interrupting client's challenge has been revoked, so that the interrupted client can * start retry logic if necessary). See - * {@link FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)} + * {@link + *android.hardware.face.FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)} * The only case of conflicting challenges is currently resetLockout --> enroll. So, the second * option seems better as it prioritizes the new operation, which is user-facing. */ diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java index 8c7b99d6eb52..33b2b6ada24c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.sensors.face; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; @@ -39,9 +40,10 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> { private final int mFeature; private final int mFaceId; + private boolean mValue; FaceGetFeatureClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, - @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, + @NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int sensorId, int feature, int faceId) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, @@ -54,7 +56,9 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> { @Override public void unableToStart() { try { - getListener().onFeatureGet(false /* success */, mFeature, false /* value */); + if (getListener() != null) { + getListener().onFeatureGet(false /* success */, mFeature, false /* value */); + } } catch (RemoteException e) { Slog.e(TAG, "Unable to send error", e); } @@ -70,11 +74,18 @@ public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> { protected void startHalOperation() { try { final OptionalBool result = getFreshDaemon().getFeature(mFeature, mFaceId); - getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value); + mValue = result.value; + if (getListener() != null) { + getListener().onFeatureGet(result.status == Status.OK, mFeature, mValue); + } mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Unable to getFeature", e); mCallback.onClientFinished(this, false /* success */); } } + + boolean getValue() { + return mValue; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index b689106bbc44..c6664f4d96ff 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -25,9 +25,9 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.face.Face; +import android.hardware.face.FaceSensorProperties; import android.hardware.face.IFaceService; import android.hardware.face.IFaceServiceReceiver; -import android.hardware.face.FaceSensorProperties; import android.os.Binder; import android.os.IBinder; import android.os.NativeHandle; @@ -303,7 +303,8 @@ public class FaceService extends SystemService { public void getFeature(final IBinder token, int userId, int feature, IFaceServiceReceiver receiver, final String opPackageName) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); - mFace10.scheduleGetFeature(token, userId, feature, receiver, opPackageName); + mFace10.scheduleGetFeature(token, userId, feature, + new ClientMonitorCallbackConverter(receiver), opPackageName); } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java index c5c28227fd24..3754bd748781 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java @@ -440,7 +440,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { // is safe because authenticatorIds only change when A) new template has been enrolled, // or B) all templates are removed. mHandler.post(() -> { - for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) { + for (UserInfo user : UserManager.get(mContext).getAliveUsers()) { final int targetUserId = user.id; if (!mAuthenticatorIds.containsKey(targetUserId)) { scheduleUpdateActiveUserWithoutHandler(targetUserId); diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index a0bc7d8954fb..5d2f51230c12 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -57,10 +57,12 @@ import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.view.autofill.AutofillManagerInternal; +import android.widget.Toast; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.SystemService.TargetUser; +import com.android.server.UiThread; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -391,7 +393,9 @@ public class ClipboardService extends SystemService { return null; } addActiveOwnerLocked(intendingUid, pkg); - return getClipboard(intendingUserId).primaryClip; + PerUserClipboard clipboard = getClipboard(intendingUserId); + maybeNotify(pkg, intendingUid, intendingUserId, clipboard); + return clipboard.primaryClip; } } @@ -821,4 +825,65 @@ public class ClipboardService extends SystemService { return appOpsResult == AppOpsManager.MODE_ALLOWED; } + + /** + * Potentially notifies the user (via a toast) about an app accessing the clipboard. + * TODO(b/167676460): STOPSHIP as we don't want this code as-is to launch. Just an experiment. + */ + private void maybeNotify(String callingPackage, int uid, @UserIdInt int userId, + PerUserClipboard clipboard) { + if (clipboard.primaryClip == null) { + return; + } + if (Settings.Global.getInt(getContext().getContentResolver(), + "clipboard_access_toast_enabled", 0) == 0) { + return; + } + // Don't notify if the app accessing the clipboard is the same as the current owner. + if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) { + return; + } + // Exclude some special cases. It's a bit wasteful to check these again here, but for now + // beneficial to have all the logic contained in this single (probably temporary) method. + String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, userId); + if (!TextUtils.isEmpty(defaultIme)) { + final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName(); + if (imePkg.equals(callingPackage)) { + return; + } + } + if (mContentCaptureInternal != null + && mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId)) { + return; + } + if (mAutofillInternal != null + && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) { + return; + } + // Load the labels for the calling app and the app that set the clipboard content. + final long ident = Binder.clearCallingIdentity(); + try { + final IPackageManager pm = AppGlobals.getPackageManager(); + String message; + final CharSequence callingLabel = mPm.getApplicationLabel( + pm.getApplicationInfo(callingPackage, 0, userId)); + final String[] packagesForUid = pm.getPackagesForUid(clipboard.primaryClipUid); + if (packagesForUid != null && packagesForUid.length > 0) { + final CharSequence clipLabel = mPm.getApplicationLabel( + pm.getApplicationInfo(packagesForUid[0], 0, + UserHandle.getUserId(clipboard.primaryClipUid))); + message = callingLabel + " pasted from " + clipLabel; + } else { + message = callingLabel + " pasted from clipboard"; + } + Slog.i(TAG, message); + Toast.makeText(getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT) + .show(); + } catch (RemoteException e) { + /* ignore */ + } finally { + Binder.restoreCallingIdentity(ident); + } + } } diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java index 7c8fb5aefd1e..1f0066a43538 100644 --- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java +++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java @@ -29,6 +29,7 @@ import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL; import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK; import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET; +import static android.net.SocketKeepalive.ERROR_STOP_REASON_UNINITIALIZED; import static android.net.SocketKeepalive.ERROR_UNSUPPORTED; import static android.net.SocketKeepalive.MAX_INTERVAL_SEC; import static android.net.SocketKeepalive.MIN_INTERVAL_SEC; @@ -152,6 +153,7 @@ public class KeepaliveTracker { private static final int STARTED = 3; private static final int STOPPING = 4; private int mStartedState = NOT_STARTED; + private int mStopReason = ERROR_STOP_REASON_UNINITIALIZED; KeepaliveInfo(@NonNull ISocketKeepaliveCallback callback, @NonNull NetworkAgentInfo nai, @@ -365,6 +367,11 @@ public class KeepaliveTracker { Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network); } } + // Store the reason of stopping, and report it after the keepalive is fully stopped. + if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) { + throw new IllegalStateException("Unexpected stop reason: " + mStopReason); + } + mStopReason = reason; Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString() + ": " + reason); switch (mStartedState) { @@ -403,24 +410,6 @@ public class KeepaliveTracker { Log.wtf(TAG, "Error closing fd for keepalive " + mSlot + ": " + e); } } - - if (reason == SUCCESS) { - try { - mCallback.onStopped(); - } catch (RemoteException e) { - Log.w(TAG, "Discarded onStop callback: " + reason); - } - } else if (reason == DATA_RECEIVED) { - try { - mCallback.onDataReceived(); - } catch (RemoteException e) { - Log.w(TAG, "Discarded onDataReceived callback: " + reason); - } - } else { - notifyErrorCallback(mCallback, reason); - } - - unlinkDeathRecipient(); } void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) { @@ -505,12 +494,37 @@ public class KeepaliveTracker { Log.e(TAG, "Attempt to remove nonexistent keepalive " + slot + " on " + networkName); return; } + + // Remove the keepalive from hash table so the slot can be considered available when reusing + // it. networkKeepalives.remove(slot); Log.d(TAG, "Remove keepalive " + slot + " on " + networkName + ", " + networkKeepalives.size() + " remains."); if (networkKeepalives.isEmpty()) { mKeepalives.remove(nai); } + + // Notify app that the keepalive is stopped. + final int reason = ki.mStopReason; + if (reason == SUCCESS) { + try { + ki.mCallback.onStopped(); + } catch (RemoteException e) { + Log.w(TAG, "Discarded onStop callback: " + reason); + } + } else if (reason == DATA_RECEIVED) { + try { + ki.mCallback.onDataReceived(); + } catch (RemoteException e) { + Log.w(TAG, "Discarded onDataReceived callback: " + reason); + } + } else if (reason == ERROR_STOP_REASON_UNINITIALIZED) { + throw new IllegalStateException("Unexpected stop reason: " + reason); + } else { + notifyErrorCallback(ki.mCallback, reason); + } + + ki.unlinkDeathRecipient(); } public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) { diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index a75a80a606eb..4c63eb488118 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -174,7 +174,7 @@ public class PermissionMonitor implements PackageManagerInternal.PackageListObse netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms); } - List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users + List<UserInfo> users = mUserManager.getAliveUsers(); if (users != null) { for (UserInfo user : users) { mUsers.add(user.id); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 5484bfca5851..7175489614ea 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1463,7 +1463,7 @@ public class Vpn { final long token = Binder.clearCallingIdentity(); List<UserInfo> users; try { - users = UserManager.get(mContext).getUsers(true); + users = UserManager.get(mContext).getAliveUsers(); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index ec12a971e445..b33aa0a6fad3 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -367,7 +367,7 @@ public class SyncManager { } private void removeStaleAccounts() { - for (UserInfo user : mUserManager.getUsers(true)) { + for (UserInfo user : mUserManager.getAliveUsers()) { // Skip any partially created/removed users if (user.partial) continue; Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts( @@ -777,7 +777,7 @@ public class SyncManager { if (!mSyncStorageEngine.shouldGrantSyncAdaptersAccountAccess()) { return; } - List<UserInfo> users = mUserManager.getUsers(true); + List<UserInfo> users = mUserManager.getAliveUsers(); final int userCount = users.size(); for (int i = 0; i < userCount; i++) { UserHandle userHandle = users.get(i).getUserHandle(); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 2c632d96e738..4a12ee71adbe 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -83,6 +83,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; +import android.util.EventLog; import android.util.IntArray; import android.util.Pair; import android.util.Slog; @@ -283,6 +284,7 @@ public final class DisplayManagerService extends SystemService { // Temporary display info, used for comparing display configurations. private final DisplayInfo mTempDisplayInfo = new DisplayInfo(); + private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo(); // Temporary viewports, used when sending new viewport information to the // input system. May be used outside of the lock but only on the handler thread. @@ -507,7 +509,8 @@ public final class DisplayManagerService extends SystemService { mDisplayTransactionListeners.remove(listener); } - private void setDisplayInfoOverrideFromWindowManagerInternal( + @VisibleForTesting + void setDisplayInfoOverrideFromWindowManagerInternal( int displayId, DisplayInfo info) { synchronized (mSyncRoot) { LogicalDisplay display = mLogicalDisplays.get(displayId); @@ -935,7 +938,8 @@ public final class DisplayManagerService extends SystemService { adapter.registerLocked(); } - private void handleDisplayDeviceAdded(DisplayDevice device) { + @VisibleForTesting + void handleDisplayDeviceAdded(DisplayDevice device) { synchronized (mSyncRoot) { handleDisplayDeviceAddedLocked(device); } @@ -947,7 +951,6 @@ public final class DisplayManagerService extends SystemService { Slog.w(TAG, "Attempted to add already added display device: " + info); return; } - Slog.i(TAG, "Display device added: " + info); device.mDebugLastLoggedDeviceInfo = info; @@ -960,7 +963,8 @@ public final class DisplayManagerService extends SystemService { scheduleTraversalLocked(false); } - private void handleDisplayDeviceChanged(DisplayDevice device) { + @VisibleForTesting + void handleDisplayDeviceChanged(DisplayDevice device) { synchronized (mSyncRoot) { DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); if (!mDisplayDevices.contains(device)) { @@ -1234,6 +1238,7 @@ public final class DisplayManagerService extends SystemService { LogicalDisplay display = mLogicalDisplays.valueAt(i); mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked()); + display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo); display.updateLocked(mDisplayDevices); if (!display.isValidLocked()) { mLogicalDisplays.removeAt(i); @@ -1242,6 +1247,15 @@ public final class DisplayManagerService extends SystemService { } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) { handleLogicalDisplayChanged(displayId, display); changed = true; + } else { + // While applications shouldn't know nor care about the non-overridden info, we + // still need to let WindowManager know so it can update its own internal state for + // things like display cutouts. + display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo); + if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) { + handleLogicalDisplayChanged(displayId, display); + changed = true; + } } } return changed; @@ -2176,6 +2190,8 @@ public final class DisplayManagerService extends SystemService { if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { + EventLog.writeEvent(0x534e4554, "162627132", callingUid, + "Attempt to create a trusted display without holding permission!"); throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to " + "create a trusted virtual display."); } diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceAction.java b/services/core/java/com/android/server/hdmi/ActiveSourceAction.java index c90f297e1485..179602737985 100644 --- a/services/core/java/com/android/server/hdmi/ActiveSourceAction.java +++ b/services/core/java/com/android/server/hdmi/ActiveSourceAction.java @@ -51,7 +51,7 @@ public class ActiveSourceAction extends HdmiCecFeatureAction { Constants.MENU_STATE_ACTIVATED)); } - source().setActiveSource(logicalAddress, physicalAddress); + source().setActiveSource(logicalAddress, physicalAddress, "ActiveSourceAction"); mState = STATE_FINISHED; finish(); return true; diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java index 01547c16e9b5..8405bbe38b12 100644 --- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java +++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java @@ -17,9 +17,9 @@ package com.android.server.hdmi; import android.annotation.Nullable; -import android.hardware.hdmi.IHdmiControlCallback; -import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.HdmiControlManager; +import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.hdmi.IHdmiControlCallback; import android.os.RemoteException; import android.util.Slog; @@ -69,7 +69,7 @@ final class ActiveSourceHandler { if (!tv.isProhibitMode()) { ActiveSource old = ActiveSource.of(tv.getActiveSource()); - tv.updateActiveSource(newActive); + tv.updateActiveSource(newActive, "ActiveSourceHandler"); boolean notifyInputChange = (mCallback == null); if (!old.equals(newActive)) { tv.setPrevPortId(tv.getActivePortId()); @@ -85,7 +85,7 @@ final class ActiveSourceHandler { HdmiCecMessage activeSourceCommand = HdmiCecMessageBuilder.buildActiveSource( current.logicalAddress, current.physicalAddress); mService.sendCecCommand(activeSourceCommand); - tv.updateActiveSource(current); + tv.updateActiveSource(current, "ActiveSourceHandler"); invokeCallback(HdmiControlManager.RESULT_SUCCESS); } else { tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress, true, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 66652ca26e54..7dc4d6e8efcf 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -688,16 +688,24 @@ final class HdmiCecController { } void dump(final IndentingPrintWriter pw) { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + for (int i = 0; i < mLocalDevices.size(); ++i) { pw.println("HdmiCecLocalDevice #" + mLocalDevices.keyAt(i) + ":"); pw.increaseIndent(); mLocalDevices.valueAt(i).dump(pw); + + pw.println("Active Source history:"); + pw.increaseIndent(); + for (Dumpable activeSourceEvent : mLocalDevices.valueAt(i).getActiveSourceHistory()) { + activeSourceEvent.dump(pw, sdf); + } + pw.decreaseIndent(); pw.decreaseIndent(); } pw.println("CEC message history:"); pw.increaseIndent(); - final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (Dumpable record : mMessageHistory) { record.dump(pw, sdf); } @@ -885,7 +893,7 @@ final class HdmiCecController { @Override public void serviceDied(long cookie) { if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) { - HdmiLogger.error(TAG, "Service died cokkie : " + cookie + "; reconnecting"); + HdmiLogger.error("Service died cookie : " + cookie + "; reconnecting"); connectToHal(); } } @@ -917,7 +925,7 @@ final class HdmiCecController { } } - private abstract static class Dumpable { + public abstract static class Dumpable { protected final long mTime; Dumpable() { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 86e6a3220507..b88a37e7b8b4 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -38,10 +38,13 @@ import com.android.server.hdmi.Constants.LocalActivePort; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiControlService.SendMessageCallback; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; /** * Class that models a logical CEC device hosted in this system. Handles initialization, CEC @@ -50,6 +53,7 @@ import java.util.List; abstract class HdmiCecLocalDevice { private static final String TAG = "HdmiCecLocalDevice"; + private static final int MAX_HDMI_ACTIVE_SOURCE_HISTORY = 10; private static final int MSG_DISABLE_DEVICE_TIMEOUT = 1; private static final int MSG_USER_CONTROL_RELEASE_TIMEOUT = 2; // Timeout in millisecond for device clean up (5s). @@ -68,6 +72,10 @@ abstract class HdmiCecLocalDevice { protected int mLastKeycode = HdmiCecKeycode.UNSUPPORTED_KEYCODE; protected int mLastKeyRepeatCount = 0; + // Stores recent changes to the active source in the CEC network. + private final ArrayBlockingQueue<HdmiCecController.Dumpable> mActiveSourceHistory = + new ArrayBlockingQueue<>(MAX_HDMI_ACTIVE_SOURCE_HISTORY); + static class ActiveSource { int logicalAddress; int physicalAddress; @@ -893,16 +901,16 @@ abstract class HdmiCecLocalDevice { return mService.getLocalActiveSource(); } - void setActiveSource(ActiveSource newActive) { - setActiveSource(newActive.logicalAddress, newActive.physicalAddress); + void setActiveSource(ActiveSource newActive, String caller) { + setActiveSource(newActive.logicalAddress, newActive.physicalAddress, caller); } - void setActiveSource(HdmiDeviceInfo info) { - setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress()); + void setActiveSource(HdmiDeviceInfo info, String caller) { + setActiveSource(info.getLogicalAddress(), info.getPhysicalAddress(), caller); } - void setActiveSource(int logicalAddress, int physicalAddress) { - mService.setActiveSource(logicalAddress, physicalAddress); + void setActiveSource(int logicalAddress, int physicalAddress, String caller) { + mService.setActiveSource(logicalAddress, physicalAddress, caller); mService.setLastInputForMhl(Constants.INVALID_PORT_ID); } @@ -1120,6 +1128,20 @@ abstract class HdmiCecLocalDevice { HdmiCecMessageBuilder.buildUserControlReleased(mAddress, targetAddress)); } + void addActiveSourceHistoryItem(ActiveSource activeSource, boolean isActiveSource, + String caller) { + ActiveSourceHistoryRecord record = new ActiveSourceHistoryRecord(activeSource, + isActiveSource, caller); + if (!mActiveSourceHistory.offer(record)) { + mActiveSourceHistory.poll(); + mActiveSourceHistory.offer(record); + } + } + + public ArrayBlockingQueue<HdmiCecController.Dumpable> getActiveSourceHistory() { + return this.mActiveSourceHistory; + } + /** Dump internal status of HdmiCecLocalDevice object. */ protected void dump(final IndentingPrintWriter pw) { pw.println("mDeviceType: " + mDeviceType); @@ -1152,4 +1174,29 @@ abstract class HdmiCecLocalDevice { } return finalMask | myPhysicalAddress; } + + private static final class ActiveSourceHistoryRecord extends HdmiCecController.Dumpable { + private final ActiveSource mActiveSource; + private final boolean mIsActiveSource; + private final String mCaller; + + private ActiveSourceHistoryRecord(ActiveSource mActiveSource, boolean mIsActiveSource, + String caller) { + this.mActiveSource = mActiveSource; + this.mIsActiveSource = mIsActiveSource; + this.mCaller = caller; + } + + @Override + void dump(final IndentingPrintWriter pw, SimpleDateFormat sdf) { + pw.print("time="); + pw.print(sdf.format(new Date(mTime))); + pw.print(" active source="); + pw.print(mActiveSource); + pw.print(" isActiveSource="); + pw.print(mIsActiveSource); + pw.print(" from="); + pw.println(mCaller); + } + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index af815973d3c3..68473c1830ae 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -361,7 +361,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { assertRunOnServiceThread(); // Invalidate the internal active source record when goes to standby // This set will also update mIsActiveSource - mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS); + mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS, + "HdmiCecLocalDeviceAudioSystem#onStandby()"); mTvSystemAudioModeSupport = null; // Record the last state of System Audio Control before going to standby synchronized (mLock) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index d675b81629a4..f2f6dbe9bde5 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -190,7 +190,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { boolean wasActiveSource = mIsActiveSource; // Invalidate the internal active source record when goes to standby // This set will also update mIsActiveSource - mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS); + mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS, + "HdmiCecLocalDevicePlayback#onStandby()"); if (initiatedByCec || !mAutoTvOff || !wasActiveSource) { return; } @@ -399,7 +400,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) { assertRunOnServiceThread(); if (physicalAddress != mService.getPhysicalAddress()) { - setActiveSource(physicalAddress); + setActiveSource(physicalAddress, + "HdmiCecLocalDevicePlayback#handleRoutingChangeAndInformation()"); return; } switch (mPlaybackDeviceActionOnRoutingControl) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index 44ad8eea65ca..4ff36c4b65db 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -119,11 +119,11 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { } @ServiceThreadOnly - protected void setActiveSource(int physicalAddress) { + protected void setActiveSource(int physicalAddress, String caller) { assertRunOnServiceThread(); // Invalidate the internal active source record. This will also update mIsActiveSource. ActiveSource activeSource = ActiveSource.of(Constants.ADDR_INVALID, physicalAddress); - setActiveSource(activeSource); + setActiveSource(activeSource, caller); } @ServiceThreadOnly @@ -133,7 +133,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); if (!getActiveSource().equals(activeSource)) { - setActiveSource(activeSource); + setActiveSource(activeSource, "HdmiCecLocalDeviceSource#handleActiveSource()"); } setIsActiveSource(physicalAddress == mService.getPhysicalAddress()); updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON); @@ -162,7 +162,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { setAndBroadcastActiveSource(message, physicalAddress); } if (physicalAddress != mService.getPhysicalAddress()) { - setActiveSource(physicalAddress); + setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleSetStreamPath()"); } switchInputOnReceivingNewActivePath(physicalAddress); return true; @@ -174,7 +174,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { assertRunOnServiceThread(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams(), 2); if (physicalAddress != mService.getPhysicalAddress()) { - setActiveSource(physicalAddress); + setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleRoutingChange()"); } if (!isRoutingControlFeatureEnabled()) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); @@ -196,7 +196,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { assertRunOnServiceThread(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); if (physicalAddress != mService.getPhysicalAddress()) { - setActiveSource(physicalAddress); + setActiveSource(physicalAddress, "HdmiCecLocalDeviceSource#handleRoutingInformation()"); } if (!isRoutingControlFeatureEnabled()) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 804cc92cca08..0325ad929849 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -288,13 +288,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (targetAddress == Constants.ADDR_INTERNAL) { handleSelectInternalSource(); // Switching to internal source is always successful even when CEC control is disabled. - setActiveSource(targetAddress, mService.getPhysicalAddress()); + setActiveSource(targetAddress, mService.getPhysicalAddress(), + "HdmiCecLocalDeviceTv#deviceSelect()"); setActivePath(mService.getPhysicalAddress()); invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS); return; } if (!mService.isControlEnabled()) { - setActiveSource(targetDevice); + setActiveSource(targetDevice, "HdmiCecLocalDeviceTv#deviceSelect()"); invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE); return; } @@ -307,7 +308,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { assertRunOnServiceThread(); // Seq #18 if (mService.isControlEnabled() && getActiveSource().logicalAddress != mAddress) { - updateActiveSource(mAddress, mService.getPhysicalAddress()); + updateActiveSource(mAddress, mService.getPhysicalAddress(), + "HdmiCecLocalDeviceTv#handleSelectInternalSource()"); if (mSkipRoutingControl) { mSkipRoutingControl = false; return; @@ -319,19 +321,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } @ServiceThreadOnly - void updateActiveSource(int logicalAddress, int physicalAddress) { + void updateActiveSource(int logicalAddress, int physicalAddress, String caller) { assertRunOnServiceThread(); - updateActiveSource(ActiveSource.of(logicalAddress, physicalAddress)); + updateActiveSource(ActiveSource.of(logicalAddress, physicalAddress), caller); } @ServiceThreadOnly - void updateActiveSource(ActiveSource newActive) { + void updateActiveSource(ActiveSource newActive, String caller) { assertRunOnServiceThread(); // Seq #14 if (getActiveSource().equals(newActive)) { return; } - setActiveSource(newActive); + setActiveSource(newActive, caller); int logicalAddress = newActive.logicalAddress; if (getCecDeviceInfo(logicalAddress) != null && logicalAddress != mAddress) { if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) { diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index 4ad51de2e25b..0b4f31d365d3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -293,6 +293,11 @@ public class HdmiCecMessageValidator { return success ? OK : ERROR_PARAMETER; } + private boolean isWithinRange(int value, int min, int max) { + value = value & 0xFF; + return (value >= min && value <= max); + } + private class PhysicalAddressValidator implements ParameterValidator { @Override public int isValid(byte[] params) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 0576e91b79eb..44b6a63faea1 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1970,7 +1970,21 @@ public class HdmiControlService extends SystemService { @Override public void powerOnRemoteDevice(int logicalAddress, int powerStatus) { - // TODO(amyjojo): implement the method + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + Slog.i(TAG, "Device " + + logicalAddress + " power status is " + powerStatus + + " before power on command sent out"); + if (getSwitchDevice() != null) { + getSwitchDevice().sendUserControlPressedAndReleased( + logicalAddress, HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION); + } else { + Slog.e(TAG, "Can't get the correct local device to handle routing."); + } + } + }); } @Override @@ -3214,7 +3228,7 @@ public class HdmiControlService extends SystemService { } } - void setActiveSource(int logicalAddress, int physicalAddress) { + void setActiveSource(int logicalAddress, int physicalAddress, String caller) { synchronized (mLock) { mActiveSource.logicalAddress = logicalAddress; mActiveSource.physicalAddress = physicalAddress; @@ -3225,14 +3239,17 @@ public class HdmiControlService extends SystemService { // mIsActiveSource only exists in source device, ignore this setting if the current // device is not an HdmiCecLocalDeviceSource. if (!(device instanceof HdmiCecLocalDeviceSource)) { + device.addActiveSourceHistoryItem(new ActiveSource(logicalAddress, physicalAddress), + false, caller); continue; } - if (logicalAddress == device.getDeviceInfo().getLogicalAddress() - && physicalAddress == getPhysicalAddress()) { - ((HdmiCecLocalDeviceSource) device).setIsActiveSource(true); - } else { - ((HdmiCecLocalDeviceSource) device).setIsActiveSource(false); - } + boolean deviceIsActiveSource = + logicalAddress == device.getDeviceInfo().getLogicalAddress() + && physicalAddress == getPhysicalAddress(); + + ((HdmiCecLocalDeviceSource) device).setIsActiveSource(deviceIsActiveSource); + device.addActiveSourceHistoryItem(new ActiveSource(logicalAddress, physicalAddress), + deviceIsActiveSource, caller); } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 3fddd5ab91ee..3235b20fb75e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3274,6 +3274,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) { + if (mCurClient == null || mCurClient.curSession == null) { + return false; + } if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mShowExplicitlyRequested || mShowForced)) { if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); @@ -3458,7 +3461,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // pre-rendering not supported on low-ram devices. cs.shouldPreRenderIme = DebugFlags.FLAG_PRE_RENDER_IME_VIEWS.value() && !mIsLowRam; - if (mCurFocusedWindow == windowToken) { + final boolean sameWindowFocused = mCurFocusedWindow == windowToken; + final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; + if (sameWindowFocused && isTextEditor) { if (DEBUG) { Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client + " attribute=" + attribute + ", token = " + windowToken @@ -3473,6 +3478,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY, null, null, null, -1, null); } + mCurFocusedWindow = windowToken; mCurFocusedWindowSoftInputMode = softInputMode; mCurFocusedWindowClient = cs; @@ -3490,7 +3496,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub == LayoutParams.SOFT_INPUT_ADJUST_RESIZE || mRes.getConfiguration().isLayoutSizeAtLeast( Configuration.SCREENLAYOUT_SIZE_LARGE); - final boolean isTextEditor = (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0; // We want to start input before showing the IME, but after closing // it. We want to do this after closing it to help the IME disappear @@ -3550,9 +3555,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } break; case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: - if (DEBUG) Slog.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(mCurFocusedWindow, 0, null, - SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE); + if (isImeVisible()) { + if (DEBUG) Slog.v(TAG, "Window asks to hide input"); + hideCurrentInputLocked(mCurFocusedWindow, 0, null, + SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE); + } break; case LayoutParams.SOFT_INPUT_STATE_VISIBLE: if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { @@ -3577,13 +3584,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.v(TAG, "Window asks to always show input"); if (InputMethodUtils.isSoftInputModeStateVisibleAllowed( unverifiedTargetSdkVersion, startInputFlags)) { - if (attribute != null) { - res = startInputUncheckedLocked(cs, inputContext, missingMethods, - attribute, startInputFlags, startInputReason); - didStart = true; + if (!isImeVisible()) { + if (attribute != null) { + res = startInputUncheckedLocked(cs, inputContext, missingMethods, + attribute, startInputFlags, startInputReason); + didStart = true; + } + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE); } - showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, - SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE); } else { Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3601,6 +3610,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else { res = InputBindResult.NO_EDITOR; } + } else if (sameWindowFocused) { + return new InputBindResult( + InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY, + null, null, null, -1, null); } else { res = InputBindResult.NULL_EDITOR_INFO; } @@ -3608,6 +3621,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return res; } + private boolean isImeVisible() { + return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0; + } + private boolean canShowInputMethodPickerLocked(IInputMethodClient client) { // TODO(yukawa): multi-display support. final int uid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/inputmethod/TEST_MAPPING b/services/core/java/com/android/server/inputmethod/TEST_MAPPING new file mode 100644 index 000000000000..0ccd75dcbdce --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/base/core/java/android/view/inputmethod" + } + ] +} diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 817902d9d566..b61c6a7ca569 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -205,8 +205,13 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format("Successfully pushed rule set: %s", version)); + Slog.i( + TAG, + String.format( + "Successfully pushed rule set to version '%s' from '%s'", + version, ruleProvider)); } + FrameworkStatsLog.write( FrameworkStatsLog.INTEGRITY_RULES_PUSHED, success, @@ -324,13 +329,12 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { + getAllowedInstallers(packageInfo)); } IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata); - if (DEBUG_INTEGRITY_COMPONENT) { + if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) { Slog.i( TAG, - "Integrity check result: " - + result.getEffect() - + " due to " - + result.getMatchedRules()); + String.format( + "Integrity check of %s result: %s due to %s", + packageName, result.getEffect(), result.getMatchedRules())); } FrameworkStatsLog.write( @@ -673,8 +677,10 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages. List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps(); if (DEBUG_INTEGRITY_COMPONENT) { - Slog.i(TAG, String.format( - "Rule provider system app list contains: %s", allowedRuleProviders)); + Slog.i( + TAG, + String.format( + "Rule provider system app list contains: %s", allowedRuleProviders)); } // Identify the package names in the caller list. @@ -730,9 +736,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private boolean integrityCheckIncludesRuleProvider() { return Settings.Global.getInt( - mContext.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, - 0) + mContext.getContentResolver(), + Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, + 0) == 1; } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 71f1833854ce..f4d0a6254318 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -697,7 +697,8 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - Location location = manager.getLastLocation(request, identity, permissionLevel); + Location location = manager.getLastLocation(identity, permissionLevel, + request.isLocationSettingsIgnored()); // lastly - note app ops if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), @@ -740,13 +741,9 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - // create a location request that works in almost all circumstances - LocationRequest request = LocationRequest.createFromDeprecatedProvider(GPS_PROVIDER, 0, - 0, true); - - // use our own identity rather than the caller - CallerIdentity identity = CallerIdentity.fromContext(mContext); - Location location = gpsManager.getLastLocation(request, identity, PERMISSION_FINE); + // use fine permission level to avoid creating unnecessary coarse locations + Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL, + PERMISSION_FINE, false); if (location == null) { return null; } @@ -1133,9 +1130,7 @@ public class LocationManagerService extends ILocationManager.Stub { return; } - String dumpFilter = args.length == 0 ? null : args[0]; - - ipw.println("Location Manager State:"); + ipw.print("Location Manager State:"); ipw.increaseIndent(); ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime())); @@ -1166,34 +1161,28 @@ public class LocationManagerService extends ILocationManager.Stub { ipw.println( "Location Controller Extra Package: " + mExtraLocationControllerPackage + (mExtraLocationControllerPackageEnabled ? " [enabled]" - : "[disabled]")); + : " [disabled]")); } } ipw.println("Location Providers:"); ipw.increaseIndent(); for (LocationProviderManager manager : mProviderManagers) { - if (dumpFilter == null || manager.getName().equals(dumpFilter)) { - manager.dump(fd, ipw, args); - } + manager.dump(fd, ipw, args); } ipw.decreaseIndent(); - if (dumpFilter == null || GPS_PROVIDER.equals(dumpFilter)) { - if (mGnssManagerService != null) { - ipw.println("GNSS Manager:"); - ipw.increaseIndent(); - mGnssManagerService.dump(fd, ipw, args); - ipw.decreaseIndent(); - } - } - - if (dumpFilter == null || "geofence".equals(dumpFilter)) { - ipw.println("Geofence Manager:"); + if (mGnssManagerService != null) { + ipw.println("GNSS Manager:"); ipw.increaseIndent(); - mGeofenceManager.dump(fd, ipw, args); + mGnssManagerService.dump(fd, ipw, args); ipw.decreaseIndent(); } + + ipw.println("Geofence Manager:"); + ipw.increaseIndent(); + mGeofenceManager.dump(fd, ipw, args); + ipw.decreaseIndent(); } private class LocalService extends LocationManagerInternal { diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index 06105bfcc0f3..1815a8554705 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -84,7 +84,7 @@ import com.android.server.LocalServices; import com.android.server.PendingIntentUtils; import com.android.server.location.LocationPermissions.PermissionLevel; import com.android.server.location.listeners.ListenerMultiplexer; -import com.android.server.location.listeners.RemovableListenerRegistration; +import com.android.server.location.listeners.RemoteListenerRegistration; import com.android.server.location.util.AppForegroundHelper; import com.android.server.location.util.AppForegroundHelper.AppForegroundListener; import com.android.server.location.util.AppOpsHelper; @@ -154,15 +154,8 @@ class LocationProviderManager extends @Override public void deliverOnLocationChanged(Location location, - @Nullable Runnable onCompleteCallback) - throws RemoteException { - mListener.onLocationChanged(location, - onCompleteCallback == null ? null : new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) { - onCompleteCallback.run(); - } - }); + @Nullable Runnable onCompleteCallback) throws RemoteException { + mListener.onLocationChanged(location, SingleUseCallback.wrap(onCompleteCallback)); } @Override @@ -221,7 +214,7 @@ class LocationProviderManager extends } protected abstract class Registration extends - RemovableListenerRegistration<LocationRequest, LocationTransport> { + RemoteListenerRegistration<LocationRequest, LocationTransport> { @PermissionLevel protected final int mPermissionLevel; private final WorkSource mWorkSource; @@ -306,11 +299,12 @@ class LocationProviderManager extends } @Override - protected final void onInactive() { + protected final ListenerOperation<LocationTransport> onInactive() { onHighPowerUsageChanged(); if (!getRequest().getHideFromAppOps()) { mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey()); } + return null; } @Override @@ -440,18 +434,17 @@ class LocationProviderManager extends } LocationRequest newRequest = calculateProviderLocationRequest(); - if (!mProviderLocationRequest.equals(newRequest)) { - LocationRequest oldRequest = mProviderLocationRequest; - mProviderLocationRequest = newRequest; - onHighPowerUsageChanged(); - updateService(); - - // if location settings ignored has changed then the active state may have changed - return oldRequest.isLocationSettingsIgnored() - != newRequest.isLocationSettingsIgnored(); + if (mProviderLocationRequest.equals(newRequest)) { + return false; } - return false; + LocationRequest oldRequest = mProviderLocationRequest; + mProviderLocationRequest = newRequest; + onHighPowerUsageChanged(); + updateService(); + + // if location settings ignored has changed then the active state may have changed + return oldRequest.isLocationSettingsIgnored() != newRequest.isLocationSettingsIgnored(); } private LocationRequest calculateProviderLocationRequest() { @@ -826,6 +819,12 @@ class LocationProviderManager extends @GuardedBy("mLock") @Override protected void onProviderListenerRegister() { + try { + ((IBinder) getKey()).linkToDeath(this, 0); + } catch (RemoteException e) { + remove(); + } + mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs( SystemClock.elapsedRealtime()); @@ -837,12 +836,6 @@ class LocationProviderManager extends 0, this, FgThread.getHandler(), getWorkSource()); } - try { - ((IBinder) getKey()).linkToDeath(this, 0); - } catch (RemoteException e) { - remove(); - } - // start listening for provider enabled/disabled events addEnabledListener(this); @@ -1066,8 +1059,13 @@ class LocationProviderManager extends mUserInfoHelper.addListener(mUserChangedListener); mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener); - // initialize enabled state - onUserStarted(UserHandle.USER_ALL); + long identity = Binder.clearCallingIdentity(); + try { + // initialize enabled state + onUserStarted(UserHandle.USER_ALL); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1077,10 +1075,15 @@ class LocationProviderManager extends mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); // notify and remove all listeners - onUserStopped(UserHandle.USER_ALL); - removeRegistrationIf(key -> true); - mEnabledListeners.clear(); + long identity = Binder.clearCallingIdentity(); + try { + onUserStopped(UserHandle.USER_ALL); + removeRegistrationIf(key -> true); + } finally { + Binder.restoreCallingIdentity(identity); + } + mEnabledListeners.clear(); mStarted = false; } } @@ -1141,14 +1144,26 @@ class LocationProviderManager extends public void setRealProvider(AbstractLocationProvider provider) { synchronized (mLock) { Preconditions.checkState(mStarted); - mProvider.setRealProvider(provider); + + long identity = Binder.clearCallingIdentity(); + try { + mProvider.setRealProvider(provider); + } finally { + Binder.restoreCallingIdentity(identity); + } } } public void setMockProvider(@Nullable MockProvider provider) { synchronized (mLock) { Preconditions.checkState(mStarted); - mProvider.setMockProvider(provider); + + long identity = Binder.clearCallingIdentity(); + try { + mProvider.setMockProvider(provider); + } finally { + Binder.restoreCallingIdentity(identity); + } // when removing a mock provider, also clear any mock last locations and reset the // location fudger. the mock provider could have been used to infer the current @@ -1170,7 +1185,12 @@ class LocationProviderManager extends throw new IllegalArgumentException(mName + " provider is not a test provider"); } - mProvider.setMockProviderAllowed(enabled); + long identity = Binder.clearCallingIdentity(); + try { + mProvider.setMockProviderAllowed(enabled); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1180,15 +1200,20 @@ class LocationProviderManager extends throw new IllegalArgumentException(mName + " provider is not a test provider"); } - String locationProvider = location.getProvider(); - if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) { - // The location has an explicit provider that is different from the mock - // provider name. The caller may be trying to fool us via b/33091107. - EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), - mName + "!=" + locationProvider); - } + long identity = Binder.clearCallingIdentity(); + try { + String locationProvider = location.getProvider(); + if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) { + // The location has an explicit provider that is different from the mock + // provider name. The caller may be trying to fool us via b/33091107. + EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(), + mName + "!=" + locationProvider); + } - mProvider.setMockProviderLocation(location); + mProvider.setMockProviderLocation(location); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1203,10 +1228,8 @@ class LocationProviderManager extends } @Nullable - public Location getLastLocation(LocationRequest request, CallerIdentity identity, - @PermissionLevel int permissionLevel) { - Preconditions.checkArgument(mName.equals(request.getProvider())); - + public Location getLastLocation(CallerIdentity identity, @PermissionLevel int permissionLevel, + boolean ignoreLocationSettings) { if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(), identity.getPackageName())) { return null; @@ -1214,12 +1237,12 @@ class LocationProviderManager extends if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) { return null; } - if (!request.isLocationSettingsIgnored() && !isEnabled(identity.getUserId())) { + if (!ignoreLocationSettings && !isEnabled(identity.getUserId())) { return null; } - Location location = getLastLocation(identity.getUserId(), permissionLevel, - request.isLocationSettingsIgnored()); + Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel, + ignoreLocationSettings); // we don't note op here because we don't know what the client intends to do with the // location, the client is responsible for noting if necessary @@ -1233,9 +1256,30 @@ class LocationProviderManager extends } } + /** + * This function does not perform any permissions or safety checks, by calling it you are + * committing to performing all applicable checks yourself. Prefer + * {@link #getLastLocation(CallerIdentity, int, boolean)} where possible. + */ @Nullable - private Location getLastLocation(int userId, @PermissionLevel int permissionLevel, + public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel, boolean ignoreLocationSettings) { + if (userId == UserHandle.USER_ALL) { + Location lastLocation = null; + final int[] runningUserIds = mUserInfoHelper.getRunningUserIds(); + for (int i = 0; i < runningUserIds.length; i++) { + Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel, + ignoreLocationSettings); + if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos() + > lastLocation.getElapsedRealtimeNanos())) { + lastLocation = next; + } + } + return lastLocation; + } + + Preconditions.checkArgument(userId >= 0); + synchronized (mLock) { LastLocation lastLocation = mLastLocations.get(userId); if (lastLocation == null) { @@ -1247,7 +1291,7 @@ class LocationProviderManager extends public void injectLastLocation(Location location, int userId) { synchronized (mLock) { - if (getLastLocation(userId, PERMISSION_FINE, false) == null) { + if (getLastLocationUnsafe(userId, PERMISSION_FINE, false) == null) { setLastLocation(location, userId); } } @@ -1279,7 +1323,7 @@ class LocationProviderManager extends } } - public void getCurrentLocation(LocationRequest request, CallerIdentity identity, + public void getCurrentLocation(LocationRequest request, CallerIdentity callerIdentity, int permissionLevel, ICancellationSignal cancellationTransport, ILocationCallback callback) { Preconditions.checkArgument(mName.equals(request.getProvider())); @@ -1291,12 +1335,27 @@ class LocationProviderManager extends GetCurrentLocationListenerRegistration registration = new GetCurrentLocationListenerRegistration( request, - identity, + callerIdentity, new GetCurrentLocationTransport(callback), permissionLevel); synchronized (mLock) { - Location lastLocation = getLastLocation(request, identity, permissionLevel); + if (mSettingsHelper.isLocationPackageBlacklisted(callerIdentity.getUserId(), + callerIdentity.getPackageName())) { + registration.deliverLocation(null); + return; + } + if (!mUserInfoHelper.isCurrentUserId(callerIdentity.getUserId())) { + registration.deliverLocation(null); + return; + } + if (!request.isLocationSettingsIgnored() && !isEnabled(callerIdentity.getUserId())) { + registration.deliverLocation(null); + return; + } + + Location lastLocation = getLastLocationUnsafe(callerIdentity.getUserId(), + permissionLevel, request.isLocationSettingsIgnored()); if (lastLocation != null) { long locationAgeMs = NANOSECONDS.toMillis( SystemClock.elapsedRealtimeNanos() @@ -1314,63 +1373,94 @@ class LocationProviderManager extends } // if last location isn't good enough then we add a location request - addRegistration(callback.asBinder(), registration); - CancellationSignal cancellationSignal = CancellationSignal.fromTransport( - cancellationTransport); - if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener( - () -> { - synchronized (mLock) { - removeRegistration(callback.asBinder(), registration); - } - }); + long identity = Binder.clearCallingIdentity(); + try { + addRegistration(callback.asBinder(), registration); + } finally { + Binder.restoreCallingIdentity(identity); } } + + CancellationSignal cancellationSignal = CancellationSignal.fromTransport( + cancellationTransport); + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener(SingleUseCallback.wrap( + () -> { + synchronized (mLock) { + removeRegistration(callback.asBinder(), registration); + } + })); + } } public void sendExtraCommand(int uid, int pid, String command, Bundle extras) { - mProvider.sendExtraCommand(uid, pid, command, extras); + long identity = Binder.clearCallingIdentity(); + try { + mProvider.sendExtraCommand(uid, pid, command, extras); + } finally { + Binder.restoreCallingIdentity(identity); + } } - public void registerLocationRequest(LocationRequest request, CallerIdentity identity, + public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, @PermissionLevel int permissionLevel, ILocationListener listener) { Preconditions.checkArgument(mName.equals(request.getProvider())); synchronized (mLock) { - addRegistration( - listener.asBinder(), - new LocationListenerRegistration( - request, - identity, - new LocationListenerTransport(listener), - permissionLevel)); + long identity = Binder.clearCallingIdentity(); + try { + addRegistration( + listener.asBinder(), + new LocationListenerRegistration( + request, + callerIdentity, + new LocationListenerTransport(listener), + permissionLevel)); + } finally { + Binder.restoreCallingIdentity(identity); + } } } - public void registerLocationRequest(LocationRequest request, CallerIdentity identity, + public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity, @PermissionLevel int permissionLevel, PendingIntent pendingIntent) { Preconditions.checkArgument(mName.equals(request.getProvider())); synchronized (mLock) { - addRegistration( - pendingIntent, - new LocationPendingIntentRegistration( - request, - identity, - new LocationPendingIntentTransport(mContext, pendingIntent), - permissionLevel)); + long identity = Binder.clearCallingIdentity(); + try { + addRegistration( + pendingIntent, + new LocationPendingIntentRegistration( + request, + callerIdentity, + new LocationPendingIntentTransport(mContext, pendingIntent), + permissionLevel)); + } finally { + Binder.restoreCallingIdentity(identity); + } } } public void unregisterLocationRequest(ILocationListener listener) { synchronized (mLock) { - removeRegistration(listener.asBinder()); + long identity = Binder.clearCallingIdentity(); + try { + removeRegistration(listener.asBinder()); + } finally { + Binder.restoreCallingIdentity(identity); + } } } public void unregisterLocationRequest(PendingIntent pendingIntent) { synchronized (mLock) { - removeRegistration(pendingIntent); + long identity = Binder.clearCallingIdentity(); + try { + removeRegistration(pendingIntent); + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -1877,7 +1967,8 @@ class LocationProviderManager extends ipw.println("user " + userId + ":"); ipw.increaseIndent(); } - ipw.println("last location=" + getLastLocation(userId, PERMISSION_FINE, false)); + ipw.println( + "last location=" + getLastLocationUnsafe(userId, PERMISSION_FINE, false)); ipw.println("enabled=" + isEnabled(userId)); if (userIds.length != 1) { ipw.decreaseIndent(); @@ -1950,7 +2041,7 @@ class LocationProviderManager extends } // update last coarse interval only if enough time has passed long timeDeltaMs = NANOSECONDS.toMillis(newCoarse.getElapsedRealtimeNanos()) - - NANOSECONDS.toMillis(oldCoarse.getElapsedRealtimeNanos()); + - NANOSECONDS.toMillis(oldCoarse.getElapsedRealtimeNanos()); if (timeDeltaMs > FASTEST_COARSE_INTERVAL_MS) { return newCoarse; } else { @@ -1958,4 +2049,61 @@ class LocationProviderManager extends } } } + + private static class SingleUseCallback extends IRemoteCallback.Stub implements Runnable, + CancellationSignal.OnCancelListener { + + @Nullable + public static SingleUseCallback wrap(@Nullable Runnable callback) { + return callback == null ? null : new SingleUseCallback(callback); + } + + @GuardedBy("this") + @Nullable private Runnable mCallback; + + private SingleUseCallback(Runnable callback) { + mCallback = Objects.requireNonNull(callback); + } + + @Override + public void sendResult(Bundle data) { + run(); + } + + @Override + public void onCancel() { + run(); + } + + @Override + public void run() { + Runnable callback; + synchronized (this) { + callback = mCallback; + mCallback = null; + } + + // prevent this callback from being run more than once - otherwise this could provide an + // attack vector for a malicious app to break assumptions on how many times a callback + // may be invoked, and thus crash system server. + if (callback == null) { + return; + } + + long identity = Binder.clearCallingIdentity(); + try { + callback.run(); + } catch (RuntimeException e) { + // since this is within a oneway binder transaction there is nowhere + // for exceptions to go - move onto another thread to crash system + // server so we find out about it + FgThread.getExecutor().execute(() -> { + throw new AssertionError(e); + }); + throw e; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } } diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java index 2d9734ef0553..2d7f02873b8f 100644 --- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java +++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java @@ -32,6 +32,7 @@ import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationRequest; import android.location.util.identity.CallerIdentity; +import android.os.Binder; import android.os.PowerManager; import android.os.SystemClock; import android.os.WorkSource; @@ -291,17 +292,28 @@ public class GeofenceManager extends @Nullable String attributionTag) { LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE); - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag, - AppOpsManager.toReceiverId(pendingIntent)); - addRegistration(new GeofenceKey(pendingIntent, geofence), - new GeofenceRegistration(geofence, identity, pendingIntent)); + CallerIdentity callerIdentity = CallerIdentity.fromBinder(mContext, packageName, + attributionTag, AppOpsManager.toReceiverId(pendingIntent)); + + long identity = Binder.clearCallingIdentity(); + try { + addRegistration(new GeofenceKey(pendingIntent, geofence), + new GeofenceRegistration(geofence, callerIdentity, pendingIntent)); + } finally { + Binder.restoreCallingIdentity(identity); + } } /** * Removes the geofence associated with the PendingIntent. */ public void removeGeofence(PendingIntent pendingIntent) { - removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent)); + long identity = Binder.clearCallingIdentity(); + try { + removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent)); + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index 1b599b026c38..a9fdacca9a06 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -25,6 +25,7 @@ import android.annotation.Nullable; import android.location.LocationManagerInternal; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.util.identity.CallerIdentity; +import android.os.Binder; import android.os.IBinder; import android.os.IInterface; import android.os.Process; @@ -218,16 +219,27 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter /** * Adds a listener with the given identity and request. */ - protected void addListener(TRequest request, CallerIdentity identity, TListener listener) { - addRegistration(listener.asBinder(), - new GnssListenerRegistration(request, identity, listener)); + protected void addListener(TRequest request, CallerIdentity callerIdentity, + TListener listener) { + long identity = Binder.clearCallingIdentity(); + try { + addRegistration(listener.asBinder(), + new GnssListenerRegistration(request, callerIdentity, listener)); + } finally { + Binder.restoreCallingIdentity(identity); + } } /** * Removes the given listener. */ public void removeListener(TListener listener) { - removeRegistration(listener.asBinder()); + long identity = Binder.clearCallingIdentity(); + try { + removeRegistration(listener.asBinder()); + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index 850cf7f4b7ce..a4486d7b5898 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -85,7 +85,9 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * A GNSS implementation of LocationProvider used by LocationManager. @@ -181,7 +183,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private static final int INJECT_NTP_TIME = 5; // PSDS stands for Predicted Satellite Data Service private static final int DOWNLOAD_PSDS_DATA = 6; - private static final int DOWNLOAD_PSDS_DATA_FINISHED = 11; private static final int INITIALIZE_HANDLER = 13; private static final int REQUEST_LOCATION = 16; private static final int REPORT_LOCATION = 17; // HAL reports location @@ -288,6 +289,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private static final long DOWNLOAD_PSDS_DATA_TIMEOUT_MS = 60 * 1000; private static final long WAKELOCK_TIMEOUT_MILLIS = 30 * 1000; + @GuardedBy("mLock") private final ExponentialBackOff mPsdsBackOff = new ExponentialBackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL); @@ -297,14 +299,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private boolean mShutdown; - // states for injecting ntp and downloading psds data - private static final int STATE_PENDING_NETWORK = 0; - private static final int STATE_DOWNLOADING = 1; - private static final int STATE_IDLE = 2; - - // flags to trigger NTP or PSDS data download when network becomes available - // initialized to true so we do NTP and PSDS when the network comes up after booting - private int mDownloadPsdsDataPending = STATE_PENDING_NETWORK; + @GuardedBy("mLock") + private Set<Integer> mPendingDownloadPsdsTypes = new HashSet<>(); // true if GPS is navigating private boolean mNavigating; @@ -610,6 +606,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mNIHandler = new GpsNetInitiatedHandler(context, mNetInitiatedListener, mSuplEsEnabled); + // Trigger PSDS data download when the network comes up after booting. + mPendingDownloadPsdsTypes.add(GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX); mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context, GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler); @@ -670,10 +668,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements */ private void onNetworkAvailable() { mNtpTimeHelper.onNetworkAvailable(); - if (mDownloadPsdsDataPending == STATE_PENDING_NETWORK) { - if (mSupportsPsds) { - // Download only if supported, (prevents an unnecessary on-boot download) - psdsDownloadRequest(/* psdsType= */ GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX); + // Download only if supported, (prevents an unnecessary on-boot download) + if (mSupportsPsds) { + synchronized (mLock) { + for (int psdsType : mPendingDownloadPsdsTypes) { + downloadPsdsData(psdsType); + } + mPendingDownloadPsdsTypes.clear(); } } } @@ -799,17 +800,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements Log.d(TAG, "handleDownloadPsdsData() called when PSDS not supported"); return; } - if (mDownloadPsdsDataPending == STATE_DOWNLOADING) { - // already downloading data - return; - } if (!mNetworkConnectivityHandler.isDataNetworkConnected()) { // try again when network is up - mDownloadPsdsDataPending = STATE_PENDING_NETWORK; + synchronized (mLock) { + mPendingDownloadPsdsTypes.add(psdsType); + } return; } - mDownloadPsdsDataPending = STATE_DOWNLOADING; - synchronized (mLock) { // hold wake lock while task runs mDownloadPsdsWakeLock.acquire(DOWNLOAD_PSDS_DATA_TIMEOUT_MS); @@ -820,20 +817,24 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssConfiguration.getProperties()); byte[] data = psdsDownloader.downloadPsdsData(psdsType); if (data != null) { - if (DEBUG) Log.d(TAG, "calling native_inject_psds_data"); - native_inject_psds_data(data, data.length); - mPsdsBackOff.reset(); - } - - sendMessage(DOWNLOAD_PSDS_DATA_FINISHED, 0, null); - - if (data == null) { - // try again later - // since this is delayed and not urgent we do not hold a wake lock here - // the arg2 below should not be 1 otherwise the wakelock will be under-locked. + mHandler.post(() -> { + if (DEBUG) Log.d(TAG, "calling native_inject_psds_data"); + native_inject_psds_data(data, data.length, psdsType); + synchronized (mLock) { + mPsdsBackOff.reset(); + } + }); + } else { + // Try download PSDS data again later according to backoff time. + // Since this is delayed and not urgent, we do not hold a wake lock here. + // The arg2 below should not be 1 otherwise the wakelock will be under-locked. + long backoffMillis; + synchronized (mLock) { + backoffMillis = mPsdsBackOff.nextBackoffMillis(); + } mHandler.sendMessageDelayed( mHandler.obtainMessage(DOWNLOAD_PSDS_DATA, psdsType, 0, null), - mPsdsBackOff.nextBackoffMillis()); + backoffMillis); } // Release wake lock held by task, synchronize on mLock in case multiple @@ -1128,7 +1129,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements requestUtcTime(); } else if ("force_psds_injection".equals(command)) { if (mSupportsPsds) { - psdsDownloadRequest(/* psdsType= */ + downloadPsdsData(/* psdsType= */ GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX); } } else { @@ -1581,8 +1582,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements reportLocation(locations); } - void psdsDownloadRequest(int psdsType) { - if (DEBUG) Log.d(TAG, "psdsDownloadRequest. psdsType: " + psdsType); + void downloadPsdsData(int psdsType) { + if (DEBUG) Log.d(TAG, "downloadPsdsData. psdsType: " + psdsType); sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null); } @@ -1896,9 +1897,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements case DOWNLOAD_PSDS_DATA: handleDownloadPsdsData(msg.arg1); break; - case DOWNLOAD_PSDS_DATA_FINISHED: - mDownloadPsdsDataPending = STATE_IDLE; - break; case INITIALIZE_HANDLER: handleInitialize(); break; @@ -2007,8 +2005,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements return "REQUEST_LOCATION"; case DOWNLOAD_PSDS_DATA: return "DOWNLOAD_PSDS_DATA"; - case DOWNLOAD_PSDS_DATA_FINISHED: - return "DOWNLOAD_PSDS_DATA_FINISHED"; case INITIALIZE_HANDLER: return "INITIALIZE_HANDLER"; case REPORT_LOCATION: @@ -2022,6 +2018,21 @@ public class GnssLocationProvider extends AbstractLocationProvider implements @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + boolean dumpAll = false; + + int opti = 0; + while (opti < args.length) { + String opt = args[opti]; + if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { + break; + } + opti++; + if ("-a".equals(opt)) { + dumpAll = true; + break; + } + } + StringBuilder s = new StringBuilder(); s.append("mStarted=").append(mStarted).append(" (changed "); TimeUtils.formatDuration(SystemClock.elapsedRealtime() @@ -2053,9 +2064,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements s.append("]\n"); } s.append(mGnssMetrics.dumpGnssMetricsAsText()); - s.append("native internal state: \n"); - s.append(" ").append(native_get_internal_state()); - s.append("\n"); + if (dumpAll) { + s.append("native internal state: \n"); + s.append(" ").append(native_get_internal_state()); + s.append("\n"); + } pw.append(s); } @@ -2094,7 +2107,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private native boolean native_supports_psds(); - private native void native_inject_psds_data(byte[] data, int length); + private native void native_inject_psds_data(byte[] data, int length, int psdsType); // DEBUG Support private native String native_get_internal_state(); diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java index 8e81f29550d6..2bf6af25422a 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -519,7 +519,7 @@ public class GnssManagerService implements GnssNative.Callbacks { @Override public void psdsDownloadRequest(int psdsType) { - mGnssLocationProvider.psdsDownloadRequest(psdsType); + mGnssLocationProvider.downloadPsdsData(psdsType); } @Override diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java index bd8bce8f6d52..58aabdad056f 100644 --- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java @@ -32,10 +32,10 @@ import android.util.Log; * @param <TListener> listener type */ public abstract class BinderListenerRegistration<TRequest, TListener> extends - RemovableListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient { + RemoteListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient { /** - * Interface to allowed binder retrieval when keys are not themselves IBinder. + * Interface to allow binder retrieval when keys are not themselves IBinders. */ public interface BinderKey { /** diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java index f94de9be0cfe..8a6b8aa1e463 100644 --- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java @@ -18,12 +18,9 @@ package com.android.server.location.listeners; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.Binder; import android.os.Build; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.IndentingPrintWriter; -import android.util.Pair; import com.android.internal.annotations.GuardedBy; import com.android.internal.listeners.ListenerExecutor.ListenerOperation; @@ -31,8 +28,10 @@ import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; +import java.util.Map.Entry; import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; @@ -42,7 +41,7 @@ import java.util.function.Predicate; * divided into two categories, active registrations and inactive registrations, as defined by * {@link #isActive(ListenerRegistration)}. If a registration's active state changes, * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration - * whose active state may have changed. + * whose active state may have changed. Listeners will only be invoked for active registrations. * * Callbacks invoked for various changes will always be ordered according to this lifecycle list: * @@ -64,14 +63,6 @@ import java.util.function.Predicate; * {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This * ensures re-entrant removal does not accidentally remove the incorrect registration. * - * All callbacks will be invoked with a cleared binder identity. - * - * Listeners owned by other processes will be run on a direct executor (and thus while holding a - * lock). Listeners owned by the same process this multiplexer is in will be run asynchronously (and - * thus without holding a lock). The underlying assumption is that listeners owned by other - * processes will simply be forwarding the call to those other processes and perhaps performing - * simple bookkeeping, with no potential for deadlock. - * * @param <TKey> key type * @param <TRequest> request type * @param <TListener> listener type @@ -149,46 +140,51 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } /** - * Invoked before the first registration occurs. This is a convenient entry point for - * registering listeners, etc, which only need to be present while there are any registrations. + * Invoked when the multiplexer goes from having no registrations to having some registrations. + * This is a convenient entry point for registering listeners, etc, which only need to be + * present while there are any registrations. Invoked while holding the multiplexer's internal + * lock. */ protected void onRegister() {} /** - * Invoked after the last unregistration occurs. This is a convenient entry point for - * unregistering listeners, etc, which only need to be present while there are any - * registrations. + * Invoked when the multiplexer goes from having some registrations to having no registrations. + * This is a convenient entry point for unregistering listeners, etc, which only need to be + * present while there are any registrations. Invoked while holding the multiplexer's internal + * lock. */ protected void onUnregister() {} /** - * Invoked when a registration is added. + * Invoked when a registration is added. Invoked while holding the multiplexer's internal lock. */ protected void onRegistrationAdded(@NonNull TKey key, @NonNull TRegistration registration) {} /** - * Invoked when a registration is removed. + * Invoked when a registration is removed. Invoked while holding the multiplexer's internal + * lock. */ protected void onRegistrationRemoved(@NonNull TKey key, @NonNull TRegistration registration) {} /** - * Invoked when the manager goes from having no active registrations to having some active + * Invoked when the multiplexer goes from having no active registrations to having some active * registrations. This is a convenient entry point for registering listeners, etc, which only - * need to be present while there are active registrations. + * need to be present while there are active registrations. Invoked while holding the + * multiplexer's internal lock. */ protected void onActive() {} /** - * Invoked when the manager goes from having some active registrations to having no active + * Invoked when the multiplexer goes from having some active registrations to having no active * registrations. This is a convenient entry point for unregistering listeners, etc, which only - * need to be present while there are active registrations. + * need to be present while there are active registrations. Invoked while holding the + * multiplexer's internal lock. */ protected void onInactive() {} /** - * Adds a new registration with the given key. Registration may fail if - * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the registration - * will not be added. This method cannot be called to add a registration re-entrantly. + * Adds a new registration with the given key. This method cannot be called to add a + * registration re-entrantly. */ protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) { Objects.requireNonNull(key); @@ -204,7 +200,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, // involve removing a prior registration. note that try-with-resources ordering is // meaningful here as well. we want to close the reentrancy guard first, as this may // generate additional service updates, then close the update service buffer. - long identity = Binder.clearCallingIdentity(); try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { @@ -224,16 +219,13 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, registration.onRegister(key); onRegistrationAdded(key, registration); onRegistrationActiveChanged(registration); - } finally { - Binder.restoreCallingIdentity(identity); } } } /** - * Removes the registration with the given key. If unregistration occurs, - * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method - * cannot be called to remove a registration re-entrantly. + * Removes the registration with the given key. This method cannot be called to remove a + * registration re-entrantly. */ protected final void removeRegistration(@NonNull Object key) { synchronized (mRegistrations) { @@ -250,9 +242,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } /** - * Removes all registrations with keys that satisfy the given predicate. If unregistration - * occurs, {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This - * method cannot be called to remove a registration re-entrantly. + * Removes all registrations with keys that satisfy the given predicate. This method cannot be + * called to remove a registration re-entrantly. */ protected final void removeRegistrationIf(@NonNull Predicate<TKey> predicate) { synchronized (mRegistrations) { @@ -281,11 +272,8 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, /** * Removes the given registration with the given key. If the given key has a different - * registration at the time this method is called, nothing happens. If unregistration occurs, - * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method - * allows for re-entrancy, and may be called to remove a registration re-entrantly. In this case - * the registration will immediately be marked inactive and unregistered, and will be removed - * completely at some later time. + * registration at the time this method is called, nothing happens. This method allows for + * re-entrancy, and may be called to remove a registration re-entrantly. */ protected final void removeRegistration(@NonNull Object key, @NonNull ListenerRegistration<?, ?> registration) { @@ -324,7 +312,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, // in multiple service updates. note that try-with-resources ordering is meaningful here as // well. we want to close the reentrancy guard first, as this may generate additional // service updates, then close the update service buffer. - long identity = Binder.clearCallingIdentity(); try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { @@ -337,8 +324,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, onUnregister(); } } - } finally { - Binder.restoreCallingIdentity(identity); } } @@ -362,38 +347,46 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } } - long identity = Binder.clearCallingIdentity(); - try { - if (actives.isEmpty()) { + if (actives.isEmpty()) { + mCurrentRequest = null; + if (mServiceRegistered) { + mServiceRegistered = false; mCurrentRequest = null; - if (mServiceRegistered) { - mServiceRegistered = false; - mCurrentRequest = null; - unregisterWithService(); - } - return; + unregisterWithService(); } + return; + } - TMergedRequest merged = mergeRequests(actives); - if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) { - if (mServiceRegistered) { - mServiceRegistered = reregisterWithService(mCurrentRequest, merged); - } else { - mServiceRegistered = registerWithService(merged); - } - if (mServiceRegistered) { - mCurrentRequest = merged; - } else { - mCurrentRequest = null; - } + TMergedRequest merged = mergeRequests(actives); + if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) { + if (mServiceRegistered) { + mServiceRegistered = reregisterWithService(mCurrentRequest, merged); + } else { + mServiceRegistered = registerWithService(merged); + } + if (mServiceRegistered) { + mCurrentRequest = merged; + } else { + mCurrentRequest = null; } - } finally { - Binder.restoreCallingIdentity(identity); } } } /** + * Clears currently stored service state, and invokes {@link #updateService()} to force a new + * call to {@link #registerWithService(Object)} if necessary. This is useful, for instance, if + * the backing service has crashed or otherwise lost state, and needs to be re-initialized. + */ + protected final void resetService() { + synchronized (mRegistrations) { + mServiceRegistered = false; + mCurrentRequest = null; + updateService(); + } + } + + /** * Begins buffering calls to {@link #updateService()} until {@link UpdateServiceLock#close()} * is called. This is useful to prevent extra work when combining multiple calls (for example, * buffering {@code updateService()} until after multiple adds/removes/updates occur. @@ -404,9 +397,9 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, /** * Evaluates the predicate on all registrations. The predicate should return true if the active - * state of the registration may have changed as a result. Any {@link #updateService()} - * invocations made while this method is executing will be deferred until after the method is - * complete so as to avoid redundant work. + * state of the registration may have changed as a result. If the active state of any + * registration has changed, {@link #updateService()} will automatically be invoked to handle + * the resulting changes. */ protected final void updateRegistrations(@NonNull Predicate<TRegistration> predicate) { synchronized (mRegistrations) { @@ -415,7 +408,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, // callbacks. note that try-with-resources ordering is meaningful here as well. we want // to close the reentrancy guard first, as this may generate additional service updates, // then close the update service buffer. - long identity = Binder.clearCallingIdentity(); try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire(); ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) { @@ -426,8 +418,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, onRegistrationActiveChanged(registration); } } - } finally { - Binder.restoreCallingIdentity(identity); } } } @@ -450,7 +440,10 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, execute(registration, operation); } } else { - registration.onInactive(); + ListenerOperation<TListener> operation = registration.onInactive(); + if (operation != null) { + execute(registration, operation); + } if (--mActiveRegistrationsCount == 0) { onInactive(); } @@ -468,7 +461,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, protected final void deliverToListeners( @NonNull Function<TRegistration, ListenerOperation<TListener>> function) { synchronized (mRegistrations) { - long identity = Binder.clearCallingIdentity(); try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) { final int size = mRegistrations.size(); for (int i = 0; i < size; i++) { @@ -480,8 +472,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, } } } - } finally { - Binder.restoreCallingIdentity(identity); } } } @@ -495,7 +485,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, */ protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) { synchronized (mRegistrations) { - long identity = Binder.clearCallingIdentity(); try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) { final int size = mRegistrations.size(); for (int i = 0; i < size; i++) { @@ -504,8 +493,6 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, execute(registration, operation); } } - } finally { - Binder.restoreCallingIdentity(identity); } } } @@ -522,27 +509,26 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, /** * Dumps debug information. */ - public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mRegistrations) { - ipw.print("service: "); - dumpServiceState(ipw); - ipw.println(); + pw.print("service: "); + dumpServiceState(pw); + pw.println(); if (!mRegistrations.isEmpty()) { - ipw.println("listeners:"); + pw.println("listeners:"); - ipw.increaseIndent(); final int size = mRegistrations.size(); for (int i = 0; i < size; i++) { TRegistration registration = mRegistrations.valueAt(i); - ipw.print(registration); + pw.print(" "); + pw.print(registration); if (!registration.isActive()) { - ipw.println(" (inactive)"); + pw.println(" (inactive)"); } else { - ipw.println(); + pw.println(); } } - ipw.decreaseIndent(); } } } @@ -577,7 +563,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, @GuardedBy("mRegistrations") private int mGuardCount; @GuardedBy("mRegistrations") - private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals; + private @Nullable ArraySet<Entry<Object, ListenerRegistration<?, ?>>> mScheduledRemovals; ReentrancyGuard() { mGuardCount = 0; @@ -602,7 +588,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, if (mScheduledRemovals == null) { mScheduledRemovals = new ArraySet<>(mRegistrations.size()); } - mScheduledRemovals.add(new Pair<>(key, registration)); + mScheduledRemovals.add(new AbstractMap.SimpleImmutableEntry<>(key, registration)); } ReentrancyGuard acquire() { @@ -612,7 +598,7 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, @Override public void close() { - ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null; + ArraySet<Entry<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null; Preconditions.checkState(mGuardCount > 0); if (--mGuardCount == 0) { @@ -620,14 +606,15 @@ public abstract class ListenerMultiplexer<TKey, TRequest, TListener, mScheduledRemovals = null; } - if (scheduledRemovals != null) { - try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) { - final int size = scheduledRemovals.size(); - for (int i = 0; i < size; i++) { - Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt( - i); - removeRegistration(pair.first, pair.second); - } + if (scheduledRemovals == null) { + return; + } + + try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) { + final int size = scheduledRemovals.size(); + for (int i = 0; i < size; i++) { + Entry<Object, ListenerRegistration<?, ?>> entry = scheduledRemovals.valueAt(i); + removeRegistration(entry.getKey(), entry.getValue()); } } } diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java index ac56c51568be..deb9660a1c82 100644 --- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java @@ -17,55 +17,34 @@ package com.android.server.location.listeners; -import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; - import android.annotation.NonNull; import android.annotation.Nullable; -import android.location.util.identity.CallerIdentity; -import android.os.Process; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.listeners.ListenerExecutor; -import com.android.server.FgThread; import java.util.Objects; import java.util.concurrent.Executor; /** * A listener registration object which holds data associated with the listener, such as an optional - * request, and the identity of the listener owner. + * request, and an executor responsible for listener invocations. * * @param <TRequest> request type * @param <TListener> listener type */ public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor { - @VisibleForTesting - public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor(); - private final Executor mExecutor; private final @Nullable TRequest mRequest; - private final CallerIdentity mIdentity; private boolean mActive; private volatile @Nullable TListener mListener; - protected ListenerRegistration(@Nullable TRequest request, CallerIdentity identity, + protected ListenerRegistration(Executor executor, @Nullable TRequest request, TListener listener) { - // if a client is in the same process as us, binder calls will execute synchronously and - // we shouldn't run callbacks directly since they might be run under lock and deadlock - if (identity.getPid() == Process.myPid()) { - // there's a slight loophole here for pending intents - pending intent callbacks can - // always be run on the direct executor since they're always asynchronous, but honestly - // you shouldn't be using pending intent callbacks within the same process anyways - mExecutor = IN_PROCESS_EXECUTOR; - } else { - mExecutor = DIRECT_EXECUTOR; - } - + mExecutor = Objects.requireNonNull(executor); mRequest = request; - mIdentity = Objects.requireNonNull(identity); mActive = false; mListener = Objects.requireNonNull(listener); } @@ -82,34 +61,34 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut } /** - * Returns the listener identity. - */ - public final CallerIdentity getIdentity() { - return mIdentity; - } - - /** - * May be overridden by subclasses. Invoked when registration occurs. + * May be overridden by subclasses. Invoked when registration occurs. Invoked while holding the + * owning multiplexer's internal lock. */ protected void onRegister(Object key) {} /** - * May be overridden by subclasses. Invoked when unregistration occurs. + * May be overridden by subclasses. Invoked when unregistration occurs. Invoked while holding + * the owning multiplexer's internal lock. */ protected void onUnregister() {} /** * May be overridden by subclasses. Invoked when this registration becomes active. If this - * returns a non-null operation, that operation will be invoked for the listener. + * returns a non-null operation, that operation will be invoked for the listener. Invoked + * while holding the owning multiplexer's internal lock. */ protected @Nullable ListenerOperation<TListener> onActive() { return null; } /** - * May be overridden by subclasses. Invoked when registration becomes inactive. + * May be overridden by subclasses. Invoked when registration becomes inactive. If this returns + * a non-null operation, that operation will be invoked for the listener. Invoked while holding + * the owning multiplexer's internal lock. */ - protected void onInactive() {} + protected @Nullable ListenerOperation<TListener> onInactive() { + return null; + } public final boolean isActive() { return mActive; @@ -136,8 +115,7 @@ public class ListenerRegistration<TRequest, TListener> implements ListenerExecut /** * May be overridden by subclasses, however should rarely be needed. Invoked when the listener * associated with this registration is unregistered, which may occur before the registration - * itself is unregistered. This immediately prevents the listener from being further invoked - * even if the various bookkeeping associated with unregistration has not occurred yet. + * itself is unregistered. This immediately prevents the listener from being further invoked. */ protected void onListenerUnregister() {}; diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java index b5d2ef6a72ec..7b6154eb0d00 100644 --- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java @@ -30,7 +30,7 @@ import android.util.Log; * @param <TListener> listener type */ public abstract class PendingIntentListenerRegistration<TRequest, TListener> extends - RemovableListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener { + RemoteListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener { /** * Interface to allowed pending intent retrieval when keys are not themselves PendingIntents. diff --git a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java new file mode 100644 index 000000000000..e4b0b190d34c --- /dev/null +++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.listeners; + + +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; + +import android.annotation.Nullable; +import android.location.util.identity.CallerIdentity; +import android.os.Process; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.FgThread; + +import java.util.Objects; +import java.util.concurrent.Executor; + +/** + * A listener registration representing a remote (possibly from a different process) listener. + * Listeners from a different process will be run on a direct executor, since the x-process listener + * invocation should already be asynchronous. Listeners from the same process will be run on a + * normal executor, since in-process listener invocation may be synchronous. + * + * @param <TRequest> request type + * @param <TListener> listener type + */ +public abstract class RemoteListenerRegistration<TRequest, TListener> extends + RemovableListenerRegistration<TRequest, TListener> { + + @VisibleForTesting + public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor(); + + private static Executor chooseExecutor(CallerIdentity identity) { + // if a client is in the same process as us, binder calls will execute synchronously and + // we shouldn't run callbacks directly since they might be run under lock and deadlock + if (identity.getPid() == Process.myPid()) { + // there's a slight loophole here for pending intents - pending intent callbacks can + // always be run on the direct executor since they're always asynchronous, but honestly + // you shouldn't be using pending intent callbacks within the same process anyways + return IN_PROCESS_EXECUTOR; + } else { + return DIRECT_EXECUTOR; + } + } + + private final CallerIdentity mIdentity; + + protected RemoteListenerRegistration(String tag, @Nullable TRequest request, + CallerIdentity identity, TListener listener) { + super(tag, chooseExecutor(identity), request, listener); + mIdentity = Objects.requireNonNull(identity); + } + + /** + * Returns the listener identity. + */ + public final CallerIdentity getIdentity() { + return mIdentity; + } +} + diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java index 0698cca903f0..2383bece4e0a 100644 --- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java @@ -17,10 +17,10 @@ package com.android.server.location.listeners; import android.annotation.Nullable; -import android.location.util.identity.CallerIdentity; import android.util.Log; import java.util.Objects; +import java.util.concurrent.Executor; /** * A listener registration that stores its own key, and thus can remove itself. By default it will @@ -36,9 +36,9 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends private volatile @Nullable Object mKey; - protected RemovableListenerRegistration(String tag, @Nullable TRequest request, - CallerIdentity callerIdentity, TListener listener) { - super(request, callerIdentity, listener); + protected RemovableListenerRegistration(String tag, Executor executor, + @Nullable TRequest request, TListener listener) { + super(executor, request, listener); mTag = Objects.requireNonNull(tag); } @@ -70,7 +70,7 @@ public abstract class RemovableListenerRegistration<TRequest, TListener> extends @Override public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) { - Log.w(mTag, "registration " + getIdentity() + " removed due to unexpected exception", e); + Log.w(mTag, "registration " + this + " removed due to unexpected exception", e); remove(); } diff --git a/services/core/java/com/android/server/location/util/SystemSettingsHelper.java b/services/core/java/com/android/server/location/util/SystemSettingsHelper.java index ff4ba914cb9c..39aeaba16579 100644 --- a/services/core/java/com/android/server/location/util/SystemSettingsHelper.java +++ b/services/core/java/com/android/server/location/util/SystemSettingsHelper.java @@ -29,6 +29,7 @@ import static com.android.server.location.LocationManagerService.D; import static com.android.server.location.LocationManagerService.TAG; import android.app.ActivityManager; +import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.net.Uri; @@ -330,11 +331,13 @@ public class SystemSettingsHelper extends SettingsHelper { @Override public float getCoarseLocationAccuracyM() { long identity = Binder.clearCallingIdentity(); + final ContentResolver cr = mContext.getContentResolver(); try { - return Settings.Secure.getFloat( - mContext.getContentResolver(), + return Settings.Secure.getFloatForUser( + cr, LOCATION_COARSE_ACCURACY_M, - DEFAULT_COARSE_LOCATION_ACCURACY_M); + DEFAULT_COARSE_LOCATION_ACCURACY_M, + cr.getUserId()); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 26c3132167d3..dbc725ea93e8 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -837,7 +837,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (getString("migrated", null, 0) == null) { final ContentResolver cr = mContext.getContentResolver(); for (String validSetting : VALID_SETTINGS) { - String value = Settings.Secure.getString(cr, validSetting); + String value = Settings.Secure.getStringForUser(cr, validSetting, cr.getUserId()); if (value != null) { setString(validSetting, value, 0); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index e9a05a8aa16c..715e41c62a05 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -746,7 +746,7 @@ class LockSettingsStorage { public void dump(IndentingPrintWriter pw) { final UserManager um = UserManager.get(mContext); - for (UserInfo user : um.getUsers(false)) { + for (UserInfo user : um.getUsers()) { File userPath = getSyntheticPasswordDirectoryForUser(user.id); pw.println(String.format("User %d [%s]:", user.id, userPath.getAbsolutePath())); pw.increaseIndent(); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 29ee8eb13564..ded77b394066 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -518,8 +518,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final SparseBooleanArray mRestrictBackgroundAllowlistRevokedUids = new SparseBooleanArray(); + final Object mMeteredIfacesLock = new Object(); /** Set of ifaces that are metered. */ - @GuardedBy("mNetworkPoliciesSecondLock") + @GuardedBy("mMeteredIfacesLock") private ArraySet<String> mMeteredIfaces = new ArraySet<>(); /** Set of over-limit templates that have been notified. */ @GuardedBy("mNetworkPoliciesSecondLock") @@ -1972,13 +1973,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } // Remove quota from any interfaces that are no longer metered. - for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) { - final String iface = mMeteredIfaces.valueAt(i); - if (!newMeteredIfaces.contains(iface)) { - removeInterfaceQuotaAsync(iface); + synchronized (mMeteredIfacesLock) { + for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) { + final String iface = mMeteredIfaces.valueAt(i); + if (!newMeteredIfaces.contains(iface)) { + removeInterfaceQuotaAsync(iface); + } } + mMeteredIfaces = newMeteredIfaces; } - mMeteredIfaces = newMeteredIfaces; final ContentResolver cr = mContext.getContentResolver(); final boolean quotaEnabled = Settings.Global.getInt(cr, @@ -2030,7 +2033,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mSubscriptionOpportunisticQuota.put(subId, quotaBytes); } - final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); + final String[] meteredIfaces; + synchronized (mMeteredIfacesLock) { + meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]); + } mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget(); mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget(); @@ -3436,7 +3442,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("Restrict background: "); fout.println(mRestrictBackground); fout.print("Restrict power: "); fout.println(mRestrictPower); fout.print("Device idle: "); fout.println(mDeviceIdleMode); - fout.print("Metered ifaces: "); fout.println(mMeteredIfaces); + synchronized (mMeteredIfacesLock) { + fout.print("Metered ifaces: "); + fout.println(mMeteredIfaces); + } fout.println(); fout.print("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode); @@ -4632,7 +4641,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } case MSG_LIMIT_REACHED: { final String iface = (String) msg.obj; - synchronized (mNetworkPoliciesSecondLock) { + synchronized (mMeteredIfacesLock) { // fast return if not needed. if (!mMeteredIfaces.contains(iface)) { return true; @@ -5274,7 +5283,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { isBackgroundRestricted = mRestrictBackground; } final boolean isNetworkMetered; - synchronized (mNetworkPoliciesSecondLock) { + synchronized (mMeteredIfacesLock) { isNetworkMetered = mMeteredIfaces.contains(ifname); } final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered, diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index a604625460a7..74b7bd76b047 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -1554,7 +1554,7 @@ abstract public class ManagedServices { if (!isEnabledForCurrentProfiles()) { return false; } - return this.userid == userId; + return userId == USER_ALL || userId == this.userid; } public boolean enabledAndUserMatches(int nid) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 04658555f22b..5bc6e237ef37 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -262,7 +262,6 @@ import com.android.server.EventLogTags; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.UiThread; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; @@ -966,8 +965,7 @@ public class NotificationManagerService extends SystemService { nv.recycle(); } reportUserInteraction(r); - mAssistants.notifyAssistantActionClicked( - r.getSbn(), actionIndex, action, generatedByAssistant); + mAssistants.notifyAssistantActionClicked(r.getSbn(), action, generatedByAssistant); } } @@ -6261,6 +6259,8 @@ public class NotificationManagerService extends SystemService { for (NotificationRecord r : enqueued) { if (r.mUpdateTimeMs > mWhen) { // At least one enqueue was posted after the cancel, so we're invalid + Slog.i(TAG, "notification cancel ignored due to newer enqueued entry" + + "key=" + r.getSbn().getKey()); return; } } @@ -8629,12 +8629,25 @@ public class NotificationManagerService extends SystemService { ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE)); } - private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { + @VisibleForTesting + boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { if (!listener.enabledAndUserMatches(sbn.getUserId())) { return false; } - // TODO: remove this for older listeners. - return true; + return isInteractionVisibleToListener(listener, sbn.getUserId()); + } + + /** + * Returns whether the given assistant should be informed about interactions on the given user. + * + * Normally an assistant would be able to see all interactions on the current user and any + * associated profiles because they are notification listeners, but since NASes have one + * instance per user, we want to filter out interactions that are not for the user that the + * given NAS is bound in. + */ + private boolean isInteractionVisibleToListener(ManagedServiceInfo info, int userId) { + boolean isAssistantService = mAssistants.isServiceTokenValidLocked(info.service); + return !isAssistantService || info.isSameUser(userId); } private boolean isPackageSuspendedForUser(String pkg, int uid) { @@ -8856,8 +8869,6 @@ public class NotificationManagerService extends SystemService { } protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) { - // There should be only one, but it's a list, so while we enforce - // singularity elsewhere, we keep it general here, to avoid surprises. for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { ArrayList<String> keys = new ArrayList<>(records.size()); for (NotificationRecord r : records) { @@ -8875,6 +8886,8 @@ public class NotificationManagerService extends SystemService { } protected void onPanelRevealed(int items) { + // send to all currently bounds NASes since notifications from both users will appear in + // the panel for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> { final INotificationListener assistant = (INotificationListener) info.service; @@ -8888,6 +8901,8 @@ public class NotificationManagerService extends SystemService { } protected void onPanelHidden() { + // send to all currently bounds NASes since notifications from both users will appear in + // the panel for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> { final INotificationListener assistant = (INotificationListener) info.service; @@ -8976,7 +8991,7 @@ public class NotificationManagerService extends SystemService { } notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onNotificationVisibilityChanged(key, isVisible); @@ -8994,7 +9009,7 @@ public class NotificationManagerService extends SystemService { final String key = sbn.getKey(); notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded); @@ -9010,7 +9025,7 @@ public class NotificationManagerService extends SystemService { final String key = sbn.getKey(); notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onNotificationDirectReply(key); @@ -9026,7 +9041,7 @@ public class NotificationManagerService extends SystemService { final String key = sbn.getKey(); notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onSuggestedReplySent( @@ -9043,12 +9058,12 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") void notifyAssistantActionClicked( - final StatusBarNotification sbn, int actionIndex, Notification.Action action, + final StatusBarNotification sbn, Notification.Action action, boolean generatedByAssistant) { final String key = sbn.getKey(); notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onActionClicked( @@ -9072,7 +9087,7 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification sbn, final String snoozeCriterionId) { notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onNotificationSnoozedUntilContext( @@ -9129,7 +9144,7 @@ public class NotificationManagerService extends SystemService { } protected void resetDefaultAssistantsIfNecessary() { - final List<UserInfo> activeUsers = mUm.getUsers(true); + final List<UserInfo> activeUsers = mUm.getAliveUsers(); for (UserInfo userInfo : activeUsers) { int userId = userInfo.getUserHandle().getIdentifier(); if (!hasUserSet(userId)) { @@ -9293,10 +9308,12 @@ public class NotificationManagerService extends SystemService { } public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { + // send to all currently bounds NASes since notifications from both users will appear in + // the status bar for (final ManagedServiceInfo info : getServices()) { mHandler.post(() -> { final INotificationListener listener = (INotificationListener) info.service; - try { + try { listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons); } catch (RemoteException ex) { Slog.e(TAG, "unable to notify listener " @@ -9470,7 +9487,8 @@ public class NotificationManagerService extends SystemService { && changedHiddenNotifications.size() > 0; for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.isEnabledForCurrentProfiles()) { + if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener( + serviceInfo, ActivityManager.getCurrentUser())) { continue; } @@ -9489,12 +9507,7 @@ public class NotificationManagerService extends SystemService { final NotificationRankingUpdate update = makeRankingUpdateLocked( serviceInfo); - mHandler.post(new Runnable() { - @Override - public void run() { - notifyRankingUpdate(serviceInfo, update); - } - }); + mHandler.post(() -> notifyRankingUpdate(serviceInfo, update)); } } } @@ -9502,15 +9515,11 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyListenerHintsChangedLocked(final int hints) { for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.isEnabledForCurrentProfiles()) { + if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener( + serviceInfo, ActivityManager.getCurrentUser())) { continue; } - mHandler.post(new Runnable() { - @Override - public void run() { - notifyListenerHintsChanged(serviceInfo, hints); - } - }); + mHandler.post(() -> notifyListenerHintsChanged(serviceInfo, hints)); } } @@ -9562,15 +9571,12 @@ public class NotificationManagerService extends SystemService { public void notifyInterruptionFilterChanged(final int interruptionFilter) { for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.isEnabledForCurrentProfiles()) { + if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener( + serviceInfo, ActivityManager.getCurrentUser())) { continue; } - mHandler.post(new Runnable() { - @Override - public void run() { - notifyInterruptionFilterChanged(serviceInfo, interruptionFilter); - } - }); + mHandler.post( + () -> notifyInterruptionFilterChanged(serviceInfo, interruptionFilter)); } } @@ -9579,15 +9585,16 @@ public class NotificationManagerService extends SystemService { if (channel == null) { return; } - for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) { + for (final ManagedServiceInfo info : getServices()) { + if (!info.enabledAndUserMatches(UserHandle.getCallingUserId()) + || !isInteractionVisibleToListener(info, UserHandle.getCallingUserId())) { continue; } BackgroundThread.getHandler().post(() -> { - if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) { + if (info.isSystem || hasCompanionDevice(info)) { notifyNotificationChannelChanged( - serviceInfo, pkg, user, channel, modificationType); + info, pkg, user, channel, modificationType); } }); } @@ -9599,15 +9606,16 @@ public class NotificationManagerService extends SystemService { if (group == null) { return; } - for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) { + for (final ManagedServiceInfo info : getServices()) { + if (!info.enabledAndUserMatches(UserHandle.getCallingUserId()) + || !isInteractionVisibleToListener(info, UserHandle.getCallingUserId())) { continue; } BackgroundThread.getHandler().post(() -> { - if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) { + if (info.isSystem || hasCompanionDevice(info)) { notifyNotificationChannelGroupChanged( - serviceInfo, pkg, user, group, modificationType); + info, pkg, user, group, modificationType); } }); } @@ -9626,9 +9634,6 @@ public class NotificationManagerService extends SystemService { private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) { - if (!info.enabledAndUserMatches(sbn.getUserId())) { - return; - } final INotificationListener listener = (INotificationListener) info.service; StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); try { diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index a4debc16493a..d7a1ba2a93d4 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -288,7 +288,7 @@ public final class OverlayManagerService extends SystemService { private void initIfNeeded() { final UserManager um = getContext().getSystemService(UserManager.class); - final List<UserInfo> users = um.getUsers(true /*excludeDying*/); + final List<UserInfo> users = um.getAliveUsers(); synchronized (mLock) { final int userCount = users.size(); for (int i = 0; i < userCount; i++) { diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java new file mode 100644 index 000000000000..0338ed802436 --- /dev/null +++ b/services/core/java/com/android/server/pm/ApkChecksums.java @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; +import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256; +import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512; +import static android.content.pm.PackageManager.WHOLE_MD5; +import static android.content.pm.PackageManager.WHOLE_MERKLE_ROOT_4K_SHA256; +import static android.content.pm.PackageManager.WHOLE_SHA1; +import static android.content.pm.PackageManager.WHOLE_SHA256; +import static android.content.pm.PackageManager.WHOLE_SHA512; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.FileChecksum; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.os.Handler; +import android.os.SystemClock; +import android.os.incremental.IncrementalManager; +import android.os.incremental.IncrementalStorage; +import android.util.ArrayMap; +import android.util.Pair; +import android.util.Slog; +import android.util.apk.ApkSignatureSchemeV2Verifier; +import android.util.apk.ApkSignatureSchemeV3Verifier; +import android.util.apk.ApkSignatureSchemeV4Verifier; +import android.util.apk.ApkSignatureVerifier; +import android.util.apk.ApkSigningBlockUtils; +import android.util.apk.ByteBufferFactory; +import android.util.apk.SignatureInfo; +import android.util.apk.SignatureNotFoundException; +import android.util.apk.VerityBuilder; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.security.VerityUtils; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Provides checksums for APK. + */ +public class ApkChecksums { + static final String TAG = "ApkChecksums"; + + // MessageDigest algorithms. + static final String ALGO_MD5 = "MD5"; + static final String ALGO_SHA1 = "SHA1"; + static final String ALGO_SHA256 = "SHA256"; + static final String ALGO_SHA512 = "SHA512"; + + /** + * Check back in 1 second after we detected we needed to wait for the APK to be fully available. + */ + private static final long PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS = 1000; + + /** + * 24 hours timeout to wait till all files are loaded. + */ + private static final long PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS = 1000 * 3600 * 24; + + /** + * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. + * + * NOTE: All getters should return the same instance for every call. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + static class Injector { + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + interface Producer<T> { + /** Produce an instance of type {@link T} */ + T produce(); + } + + private final Producer<Context> mContext; + private final Producer<Handler> mHandlerProducer; + private final Producer<IncrementalManager> mIncrementalManagerProducer; + + Injector(Producer<Context> context, Producer<Handler> handlerProducer, + Producer<IncrementalManager> incrementalManagerProducer) { + mContext = context; + mHandlerProducer = handlerProducer; + mIncrementalManagerProducer = incrementalManagerProducer; + } + + public Context getContext() { + return mContext.produce(); + } + + public Handler getHandler() { + return mHandlerProducer.produce(); + } + + public IncrementalManager getIncrementalManager() { + return mIncrementalManagerProducer.produce(); + } + } + + /** + * Fetch or calculate checksums for the collection of files. + * + * @param filesToChecksum split name, null for base and File to fetch checksums for + * @param optional mask to fetch readily available checksums + * @param required mask to forcefully calculate if not available + * @param trustedInstallers array of certificate to trust, two specific cases: + * null - trust anybody, + * [] - trust nobody. + * @param statusReceiver to receive the resulting checksums + */ + public static void getChecksums(List<Pair<String, File>> filesToChecksum, + @PackageManager.FileChecksumKind int optional, + @PackageManager.FileChecksumKind int required, + @Nullable Certificate[] trustedInstallers, + @NonNull IntentSender statusReceiver, + @NonNull Injector injector) { + List<Map<Integer, FileChecksum>> result = new ArrayList<>(filesToChecksum.size()); + for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { + final String split = filesToChecksum.get(i).first; + final File file = filesToChecksum.get(i).second; + Map<Integer, FileChecksum> checksums = new ArrayMap<>(); + result.add(checksums); + + try { + getAvailableFileChecksums(split, file, optional | required, trustedInstallers, + checksums); + } catch (Throwable e) { + Slog.e(TAG, "Preferred checksum calculation error", e); + } + } + + long startTime = SystemClock.uptimeMillis(); + processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector, + startTime); + } + + private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum, + List<Map<Integer, FileChecksum>> result, + @PackageManager.FileChecksumKind int required, + @NonNull IntentSender statusReceiver, + @NonNull Injector injector, + long startTime) { + final boolean timeout = + SystemClock.uptimeMillis() - startTime >= PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS; + List<FileChecksum> allChecksums = new ArrayList<>(); + for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { + final String split = filesToChecksum.get(i).first; + final File file = filesToChecksum.get(i).second; + Map<Integer, FileChecksum> checksums = result.get(i); + + try { + if (!timeout || required != 0) { + if (needToWait(file, required, checksums, injector)) { + // Not ready, come back later. + injector.getHandler().postDelayed(() -> { + processRequiredChecksums(filesToChecksum, result, required, + statusReceiver, injector, startTime); + }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS); + return; + } + + getRequiredFileChecksums(split, file, required, checksums); + } + allChecksums.addAll(checksums.values()); + } catch (Throwable e) { + Slog.e(TAG, "Required checksum calculation error", e); + } + } + + final Intent intent = new Intent(); + intent.putExtra(EXTRA_CHECKSUMS, + allChecksums.toArray(new FileChecksum[allChecksums.size()])); + + try { + statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null); + } catch (IntentSender.SendIntentException e) { + Slog.w(TAG, e); + } + } + + /** + * Fetch readily available checksums - enforced by kernel or provided by Installer. + * + * @param split split name, null for base + * @param file to fetch checksums for + * @param kinds mask to fetch checksums + * @param trustedInstallers array of certificate to trust, two specific cases: + * null - trust anybody, + * [] - trust nobody. + * @param checksums resulting checksums + */ + private static void getAvailableFileChecksums(String split, File file, + @PackageManager.FileChecksumKind int kinds, + @Nullable Certificate[] trustedInstallers, + Map<Integer, FileChecksum> checksums) { + final String filePath = file.getAbsolutePath(); + + // Always available: FSI or IncFs. + if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { + // Hashes in fs-verity and IncFS are always verified. + FileChecksum checksum = extractHashFromFS(split, filePath); + if (checksum != null) { + checksums.put(checksum.getKind(), checksum); + } + } + + // System enforced: v2/v3. + if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired( + PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { + Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature( + split, filePath, kinds); + if (v2v3checksums != null) { + checksums.putAll(v2v3checksums); + } + } + + // TODO(b/160605420): Installer provided. + } + + /** + * Whether the file is available for checksumming or we need to wait. + */ + private static boolean needToWait(File file, + @PackageManager.FileChecksumKind int kinds, + Map<Integer, FileChecksum> checksums, + @NonNull Injector injector) throws IOException { + if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums) + && !isRequired(WHOLE_MD5, kinds, checksums) + && !isRequired(WHOLE_SHA1, kinds, checksums) + && !isRequired(WHOLE_SHA256, kinds, checksums) + && !isRequired(WHOLE_SHA512, kinds, checksums) + && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) + && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { + return false; + } + + final String filePath = file.getAbsolutePath(); + if (!IncrementalManager.isIncrementalPath(filePath)) { + return false; + } + + IncrementalManager manager = injector.getIncrementalManager(); + if (manager == null) { + throw new IllegalStateException("IncrementalManager is missing."); + } + IncrementalStorage storage = manager.openStorage(filePath); + if (storage == null) { + throw new IllegalStateException( + "IncrementalStorage is missing for a path on IncFs: " + filePath); + } + + return !storage.isFileFullyLoaded(filePath); + } + + /** + * Fetch or calculate checksums for the specific file. + * + * @param split split name, null for base + * @param file to fetch checksums for + * @param kinds mask to forcefully calculate if not available + * @param checksums resulting checksums + */ + private static void getRequiredFileChecksums(String split, File file, + @PackageManager.FileChecksumKind int kinds, + Map<Integer, FileChecksum> checksums) { + final String filePath = file.getAbsolutePath(); + + // Manually calculating required checksums if not readily available. + if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { + try { + byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash( + filePath, /*salt=*/null, + new ByteBufferFactory() { + @Override + public ByteBuffer create(int capacity) { + return ByteBuffer.allocate(capacity); + } + }); + checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256, + new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash)); + } catch (IOException | NoSuchAlgorithmException | DigestException e) { + Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e); + } + } + + calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_MD5); + calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA1); + calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA256); + calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA512); + + calculatePartialChecksumsIfRequested(checksums, split, file, kinds); + } + + private static boolean isRequired(@PackageManager.FileChecksumKind int kind, + @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums) { + if ((kinds & kind) == 0) { + return false; + } + if (checksums.containsKey(kind)) { + return false; + } + return true; + } + + private static FileChecksum extractHashFromFS(String split, String filePath) { + // verity first + { + byte[] hash = VerityUtils.getFsverityRootHash(filePath); + if (hash != null) { + return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash); + } + } + // v4 next + try { + ApkSignatureSchemeV4Verifier.VerifiedSigner signer = + ApkSignatureSchemeV4Verifier.extractCertificates(filePath); + byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256, + null); + if (hash != null) { + return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash); + } + } catch (SignatureNotFoundException e) { + // Nothing + } catch (SecurityException e) { + Slog.e(TAG, "V4 signature error", e); + } + return null; + } + + private static Map<Integer, FileChecksum> extractHashFromV2V3Signature( + String split, String filePath, int kinds) { + Map<Integer, byte[]> contentDigests = null; + try { + contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath, + PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, + false).contentDigests; + } catch (PackageParser.PackageParserException e) { + if (!(e.getCause() instanceof SignatureNotFoundException)) { + Slog.e(TAG, "Signature verification error", e); + } + } + + if (contentDigests == null) { + return null; + } + + Map<Integer, FileChecksum> checksums = new ArrayMap<>(); + if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) { + byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null); + if (hash != null) { + checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256, + new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash)); + } + } + if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) { + byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null); + if (hash != null) { + checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512, + new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash)); + } + } + return checksums; + } + + private static String getMessageDigestAlgoForChecksumKind(int kind) + throws NoSuchAlgorithmException { + switch (kind) { + case WHOLE_MD5: + return ALGO_MD5; + case WHOLE_SHA1: + return ALGO_SHA1; + case WHOLE_SHA256: + return ALGO_SHA256; + case WHOLE_SHA512: + return ALGO_SHA512; + default: + throw new NoSuchAlgorithmException("Invalid checksum kind: " + kind); + } + } + + private static void calculateChecksumIfRequested(Map<Integer, FileChecksum> checksums, + String split, File file, int required, int kind) { + if ((required & kind) != 0 && !checksums.containsKey(kind)) { + final byte[] checksum = getFileChecksum(file, kind); + if (checksum != null) { + checksums.put(kind, new FileChecksum(split, kind, checksum)); + } + } + } + + private static byte[] getFileChecksum(File file, int kind) { + try (FileInputStream fis = new FileInputStream(file); + BufferedInputStream bis = new BufferedInputStream(fis)) { + byte[] dataBytes = new byte[512 * 1024]; + int nread = 0; + + final String algo = getMessageDigestAlgoForChecksumKind(kind); + MessageDigest md = MessageDigest.getInstance(algo); + while ((nread = bis.read(dataBytes)) != -1) { + md.update(dataBytes, 0, nread); + } + + return md.digest(); + } catch (IOException e) { + Slog.e(TAG, "Error reading " + file.getAbsolutePath() + " to compute hash.", e); + return null; + } catch (NoSuchAlgorithmException e) { + Slog.e(TAG, "Device does not support MessageDigest algorithm", e); + return null; + } + } + + private static int[] getContentDigestAlgos(boolean needSignatureSha256, + boolean needSignatureSha512) { + if (needSignatureSha256 && needSignatureSha512) { + // Signature block present, but no digests??? + return new int[]{CONTENT_DIGEST_CHUNKED_SHA256, CONTENT_DIGEST_CHUNKED_SHA512}; + } else if (needSignatureSha256) { + return new int[]{CONTENT_DIGEST_CHUNKED_SHA256}; + } else { + return new int[]{CONTENT_DIGEST_CHUNKED_SHA512}; + } + } + + private static int getChecksumKindForContentDigestAlgo(int contentDigestAlgo) { + switch (contentDigestAlgo) { + case CONTENT_DIGEST_CHUNKED_SHA256: + return PARTIAL_MERKLE_ROOT_1M_SHA256; + case CONTENT_DIGEST_CHUNKED_SHA512: + return PARTIAL_MERKLE_ROOT_1M_SHA512; + default: + return -1; + } + } + + private static void calculatePartialChecksumsIfRequested(Map<Integer, FileChecksum> checksums, + String split, File file, int required) { + boolean needSignatureSha256 = + (required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey( + PARTIAL_MERKLE_ROOT_1M_SHA256); + boolean needSignatureSha512 = + (required & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey( + PARTIAL_MERKLE_ROOT_1M_SHA512); + if (!needSignatureSha256 && !needSignatureSha512) { + return; + } + + try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { + SignatureInfo signatureInfo = null; + try { + signatureInfo = ApkSignatureSchemeV3Verifier.findSignature(raf); + } catch (SignatureNotFoundException e) { + try { + signatureInfo = ApkSignatureSchemeV2Verifier.findSignature(raf); + } catch (SignatureNotFoundException ee) { + } + } + if (signatureInfo == null) { + Slog.e(TAG, "V2/V3 signatures not found in " + file.getAbsolutePath()); + return; + } + + final int[] digestAlgos = getContentDigestAlgos(needSignatureSha256, + needSignatureSha512); + byte[][] digests = ApkSigningBlockUtils.computeContentDigestsPer1MbChunk(digestAlgos, + raf.getFD(), signatureInfo); + for (int i = 0, size = digestAlgos.length; i < size; ++i) { + int checksumKind = getChecksumKindForContentDigestAlgo(digestAlgos[i]); + if (checksumKind != -1) { + checksums.put(checksumKind, new FileChecksum(split, checksumKind, digests[i])); + } + } + } catch (IOException | DigestException e) { + Slog.e(TAG, "Error computing hash.", e); + } + } +} diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 3d7c978ca625..f168ac70dda8 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -560,9 +560,9 @@ public class AppsFilter { final boolean newIsForceQueryable = mForceQueryable.contains(newPkgSetting.appId) /* shared user that is already force queryable */ - || newPkg.isForceQueryable() - || newPkgSetting.forceQueryableOverride + || newPkgSetting.forceQueryableOverride /* adb override */ || (newPkgSetting.isSystem() && (mSystemAppsQueryable + || newPkg.isForceQueryable() || ArrayUtils.contains(mForceQueryableByDevicePackageNames, newPkg.getPackageName()))); if (newIsForceQueryable diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java index 3f6b8e92ef74..d25ddad174f9 100644 --- a/services/core/java/com/android/server/pm/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/ComponentResolver.java @@ -890,54 +890,55 @@ public class ComponentResolver { return; } - if (systemActivities == null) { - // the system package is not disabled; we're parsing the system partition - if (isProtectedAction(intent)) { - if (mDeferProtectedFilters) { - // We can't deal with these just yet. No component should ever obtain a - // >0 priority for a protected actions, with ONE exception -- the setup - // wizard. The setup wizard, however, cannot be known until we're able to - // query it for the category CATEGORY_SETUP_WIZARD. Which we can't do - // until all intent filters have been processed. Chicken, meet egg. - // Let the filter temporarily have a high priority and rectify the - // priorities after all system packages have been scanned. - if (mProtectedFilters == null) { - mProtectedFilters = new ArrayList<>(); - } - mProtectedFilters.add(Pair.create(activity, intent)); - if (DEBUG_FILTERS) { - Slog.i(TAG, "Protected action; save for later;" - + " package: " + packageName - + " activity: " + className - + " origPrio: " + intent.getPriority()); - } - return; - } else { - if (DEBUG_FILTERS && setupWizardPackage == null) { - Slog.i(TAG, "No setup wizard;" - + " All protected intents capped to priority 0"); - } - if (packageName.equals(setupWizardPackage)) { - if (DEBUG_FILTERS) { - Slog.i(TAG, "Found setup wizard;" - + " allow priority " + intent.getPriority() + ";" - + " package: " + packageName - + " activity: " + className - + " priority: " + intent.getPriority()); - } - // setup wizard gets whatever it wants - return; - } + if (isProtectedAction(intent)) { + if (mDeferProtectedFilters) { + // We can't deal with these just yet. No component should ever obtain a + // >0 priority for a protected actions, with ONE exception -- the setup + // wizard. The setup wizard, however, cannot be known until we're able to + // query it for the category CATEGORY_SETUP_WIZARD. Which we can't do + // until all intent filters have been processed. Chicken, meet egg. + // Let the filter temporarily have a high priority and rectify the + // priorities after all system packages have been scanned. + if (mProtectedFilters == null) { + mProtectedFilters = new ArrayList<>(); + } + mProtectedFilters.add(Pair.create(activity, intent)); + if (DEBUG_FILTERS) { + Slog.i(TAG, "Protected action; save for later;" + + " package: " + packageName + + " activity: " + className + + " origPrio: " + intent.getPriority()); + } + } else { + if (DEBUG_FILTERS && setupWizardPackage == null) { + Slog.i(TAG, "No setup wizard;" + + " All protected intents capped to priority 0"); + } + if (packageName.equals(setupWizardPackage)) { if (DEBUG_FILTERS) { - Slog.i(TAG, "Protected action; cap priority to 0;" + Slog.i(TAG, "Found setup wizard;" + + " allow priority " + intent.getPriority() + ";" + " package: " + packageName + " activity: " + className - + " origPrio: " + intent.getPriority()); + + " priority: " + intent.getPriority()); } - intent.setPriority(0); + // setup wizard gets whatever it wants return; } + if (DEBUG_FILTERS) { + Slog.i(TAG, "Protected action; cap priority to 0;" + + " package: " + packageName + + " activity: " + className + + " origPrio: " + intent.getPriority()); + } + intent.setPriority(0); } + return; + } + + if (systemActivities == null) { + // the system package is not disabled; we're parsing the system partition + // privileged apps on the system image get whatever priority they request return; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 4b246c3b330c..155af82289d4 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -656,7 +656,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new IllegalArgumentException("Invalid install mode: " + params.mode); } - // If caller requested explicit location, sanity check it, otherwise + // If caller requested explicit location, validity check it, otherwise // resolve the best internal or adopted location. if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { if (!PackageHelper.fitsOnInternal(mContext, params)) { @@ -688,7 +688,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements final int sessionId; final PackageInstallerSession session; synchronized (mSessions) { - // Sanity check that installer isn't going crazy + // Check that the installer does not have too many active sessions. final int activeCount = getSessionCount(mSessions, callingUid); if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) == PackageManager.PERMISSION_GRANTED) { @@ -743,9 +743,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mStagingManager.createSession(session); } - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionCreated(session.sessionId, session.userId); - } + mCallbacks.notifySessionCreated(session.sessionId, session.userId); + writeSessionsAsync(); return sessionId; } @@ -1355,25 +1354,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements class InternalCallback { public void onSessionBadgingChanged(PackageInstallerSession session) { - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId); - } - + mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId); writeSessionsAsync(); } public void onSessionActiveChanged(PackageInstallerSession session, boolean active) { - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, - active); - } + mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, + active); } public void onSessionProgressChanged(PackageInstallerSession session, float progress) { - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, - progress); - } + mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, + progress); } public void onStagedSessionChanged(PackageInstallerSession session) { @@ -1389,17 +1381,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } public void onSessionFinished(final PackageInstallerSession session, boolean success) { - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); - } + mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); mInstallHandler.post(new Runnable() { @Override public void run() { - if (session.isStaged()) { - if (!success) { - mStagingManager.abortSession(session); - } + if (session.isStaged() && !success) { + mStagingManager.abortSession(session); } synchronized (mSessions) { if (!session.isStaged() || !success) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index ed62362b04fb..ca125320bbf2 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -249,6 +249,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private final PackageManagerService mPm; private final Handler mHandler; private final PackageSessionProvider mSessionProvider; + /** + * Note all calls must be done outside {@link #mLock} to prevent lock inversion. + */ private final StagingManager mStagingManager; final int sessionId; @@ -389,6 +392,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private String mStagedSessionErrorMessage; /** + * The callback to run when pre-reboot verification has ended. Used by {@link #abandonStaged()} + * to delay session clean-up until it is safe to do so. + */ + @GuardedBy("mLock") + @Nullable + private Runnable mPendingAbandonCallback; + /** + * {@code true} if pre-reboot verification is ongoing which means it is not safe for + * {@link #abandon()} to clean up staging directories. + */ + @GuardedBy("mLock") + private boolean mInPreRebootVerification; + + /** * Path to the validated base APK for this session, which may point at an * APK inside the session (when the session defines the base), or it may * point at the existing base APK (when adding splits to an existing app). @@ -978,7 +995,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd) throws IOException { - // Quick sanity check of state, and allocate a pipe for ourselves. We + // Quick validity check of state, and allocate a pipe for ourselves. We // then do heavy disk allocation outside the lock, but this open pipe // will block any attempted install transitions. final RevocableFileDescriptor fd; @@ -1454,26 +1471,54 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // TODO(patb): since the work done here for a parent session in a multi-package install is // mostly superficial, consider splitting this method for the parent and // single / child sessions. - synchronized (mLock) { - if (mCommitted) { - return true; + try { + synchronized (mLock) { + if (mCommitted) { + return true; + } + // Read transfers from the original owner stay open, but as the session's data + // cannot be modified anymore, there is no leak of information. For staged sessions, + // further validation is performed by the staging manager. + if (!params.isMultiPackage) { + if (!prepareDataLoaderLocked()) { + return false; + } + + if (isApexInstallation()) { + validateApexInstallLocked(); + } else { + validateApkInstallLocked(); + } + } } - if (!streamAndValidateLocked()) { - return false; + if (params.isStaged) { + mStagingManager.checkNonOverlappingWithStagedSessions(this); } - // Client staging is fully done at this point - mClientProgress = 1f; - computeProgressLocked(true); + synchronized (mLock) { + if (mDestroyed) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session destroyed"); + } + // Client staging is fully done at this point + mClientProgress = 1f; + computeProgressLocked(true); - // This ongoing commit should keep session active, even though client - // will probably close their end. - mActiveCount.incrementAndGet(); + // This ongoing commit should keep session active, even though client + // will probably close their end. + mActiveCount.incrementAndGet(); - mCommitted = true; + mCommitted = true; + } + return true; + } catch (PackageManagerException e) { + throw onSessionValidationFailure(e); + } catch (Throwable e) { + // Convert all exceptions into package manager exceptions as only those are handled + // in the code above. + throw onSessionValidationFailure(new PackageManagerException(e)); } - return true; } @GuardedBy("mLock") @@ -1511,44 +1556,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - /** - * Prepare DataLoader and stream content for DataLoader sessions. - * Validate the contents of all session. - * - * @return false if the data loader could not be prepared. - * @throws PackageManagerException when an unrecoverable exception is encountered - */ - @GuardedBy("mLock") - private boolean streamAndValidateLocked() throws PackageManagerException { - try { - // Read transfers from the original owner stay open, but as the session's data cannot - // be modified anymore, there is no leak of information. For staged sessions, further - // validation is performed by the staging manager. - if (!params.isMultiPackage) { - if (!prepareDataLoaderLocked()) { - return false; - } - - if (isApexInstallation()) { - validateApexInstallLocked(); - } else { - validateApkInstallLocked(); - } - } - - if (params.isStaged) { - mStagingManager.checkNonOverlappingWithStagedSessions(this); - } - return true; - } catch (PackageManagerException e) { - throw onSessionValidationFailure(e); - } catch (Throwable e) { - // Convert all exceptions into package manager exceptions as only those are handled - // in the code above. - throw onSessionValidationFailure(new PackageManagerException(e)); - } - } - private PackageManagerException onSessionValidationFailure(PackageManagerException e) { onSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e)); return e; @@ -1561,19 +1568,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { dispatchSessionFinished(error, detailMessage, null); } - private void onSessionVerificationFailure(int error, String detailMessage) { - Slog.e(TAG, "Failed to verify session " + sessionId + " [" + detailMessage + "]"); + private void onSessionVerificationFailure(int error, String msg) { + final String msgWithErrorCode = PackageManager.installStatusToString(error, msg); + Slog.e(TAG, "Failed to verify session " + sessionId + " [" + msgWithErrorCode + "]"); // Session is sealed and committed but could not be verified, we need to destroy it. destroyInternal(); if (isStaged()) { setStagedSessionFailed( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, detailMessage); + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode); // TODO(b/136257624): Remove this once all verification logic has been transferred out // of StagingManager. - mStagingManager.notifyVerificationComplete(sessionId); + mStagingManager.notifyVerificationComplete(this); } else { // Dispatch message to remove session from PackageInstallerService. - dispatchSessionFinished(error, detailMessage, null); + dispatchSessionFinished(error, msg, null); } } @@ -1836,21 +1844,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throws PackageManagerException { assertNotLocked("makeSessionActive"); - synchronized (mLock) { - if (mRelinquished) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Session relinquished"); - } - if (mDestroyed) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Session destroyed"); - } - if (!mSealed) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Session not sealed"); - } - } - // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc if (!params.isMultiPackage && needToAskForPermissions()) { // User needs to confirm installation; @@ -1880,6 +1873,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private PackageManagerService.VerificationParams makeVerificationParamsLocked() throws PackageManagerException { + if (mRelinquished) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session relinquished"); + } + if (mDestroyed) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session destroyed"); + } + if (!mSealed) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Session not sealed"); + } + // TODO(b/136257624): Some logic in this if block probably belongs in // makeInstallParams(). if (!params.isMultiPackage && !isApexInstallation()) { @@ -2786,14 +2792,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private void abandonStaged() { + final Runnable r; synchronized (mLock) { - if (mDestroyed) { - // If a user abandons staged session in an unsafe state, then system will try to - // abandon the destroyed staged session when it is safe on behalf of the user. - assertCallerIsOwnerOrRootOrSystemLocked(); - } else { - assertCallerIsOwnerOrRootLocked(); - } + assertCallerIsOwnerOrRootLocked(); if (isStagedAndInTerminalState()) { // We keep the session in the database if it's in a finalized state. It will be // removed by PackageInstallerService when the last update time is old enough. @@ -2802,17 +2803,25 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } mDestroyed = true; - if (mCommitted) { - if (!mStagingManager.abortCommittedSessionLocked(this)) { - // Do not clean up the staged session from system. It is not safe yet. - mCallback.onStagedSessionChanged(this); - return; + boolean isCommitted = mCommitted; + List<PackageInstallerSession> childSessions = getChildSessionsLocked(); + r = () -> { + assertNotLocked("abandonStaged"); + if (isCommitted) { + mStagingManager.abortCommittedSession(this); } + cleanStageDir(childSessions); + destroyInternal(); + dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); + }; + if (mInPreRebootVerification) { + // Pre-reboot verification is ongoing. It is not safe to clean up the session yet. + mPendingAbandonCallback = r; + mCallback.onStagedSessionChanged(this); + return; } - cleanStageDir(getChildSessionsLocked()); - destroyInternal(); } - dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null); + r.run(); } @Override @@ -2829,6 +2838,50 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + /** + * Notified by the staging manager that pre-reboot verification is about to start. The return + * value should be checked to decide whether it is OK to start pre-reboot verification. In + * the case of a destroyed session, {@code false} is returned and there is no need to start + * pre-reboot verification. + */ + boolean notifyStagedStartPreRebootVerification() { + synchronized (mLock) { + if (mInPreRebootVerification) { + throw new IllegalStateException("Pre-reboot verification has started"); + } + if (mDestroyed) { + return false; + } + mInPreRebootVerification = true; + return true; + } + } + + private void dispatchPendingAbandonCallback() { + final Runnable callback; + synchronized (mLock) { + callback = mPendingAbandonCallback; + mPendingAbandonCallback = null; + } + if (callback != null) { + callback.run(); + } + } + + /** + * Notified by the staging manager that pre-reboot verification has ended. Now it is safe to + * clean up the session if {@link #abandon()} has been called previously. + */ + void notifyStagedEndPreRebootVerification() { + synchronized (mLock) { + if (!mInPreRebootVerification) { + throw new IllegalStateException("Pre-reboot verification not started"); + } + mInPreRebootVerification = false; + } + dispatchPendingAbandonCallback(); + } + @Override public boolean isMultiPackage() { return params.isMultiPackage; @@ -3306,8 +3359,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Send broadcast to default launcher only if it's a new install // TODO(b/144270665): Secure the usage of this broadcast. final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); - if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts() - && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { + if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); } @@ -3744,7 +3796,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { out.endTag(null, TAG_SESSION); } - // Sanity check to be performed when the session is restored from an external file. Only one + // Validity check to be performed when the session is restored from an external file. Only one // of the session states should be true, or none of them. private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied, boolean isFailed) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c05bc455887d..9f78f0f08fd1 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -254,6 +254,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; +import android.os.ParcelableException; import android.os.PatternMatcher; import android.os.PersistableBundle; import android.os.Process; @@ -394,6 +395,7 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -404,7 +406,10 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -926,6 +931,7 @@ public class PackageManagerService extends IPackageManager.Stub private final Object mLock; private final Installer mInstaller; private final Object mInstallLock; + private final Handler mBackgroundHandler; private final Executor mBackgroundExecutor; // ----- producers ----- @@ -948,7 +954,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, - Executor backgroundExecutor, + Handler backgroundHandler, Producer<ComponentResolver> componentResolverProducer, Producer<PermissionManagerServiceInternal> permissionManagerProducer, Producer<UserManagerService> userManagerProducer, @@ -970,7 +976,8 @@ public class PackageManagerService extends IPackageManager.Stub mInstaller = installer; mAbiHelper = abiHelper; mInstallLock = installLock; - mBackgroundExecutor = backgroundExecutor; + mBackgroundHandler = backgroundHandler; + mBackgroundExecutor = new HandlerExecutor(backgroundHandler); mComponentResolverProducer = new Singleton<>(componentResolverProducer); mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); mUserManagerProducer = new Singleton<>(userManagerProducer); @@ -1085,6 +1092,10 @@ public class PackageManagerService extends IPackageManager.Stub return mPlatformCompatProducer.get(this, mPackageManager); } + public Handler getBackgroundHandler() { + return mBackgroundHandler; + } + public Executor getBackgroundExecutor() { return mBackgroundExecutor; } @@ -2400,9 +2411,12 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUserId = UserHandle.getUserId(callingUid); for (String packageName : packages) { - PackageSetting setting = mSettings.mPackages.get(packageName); - if (setting != null - && !shouldFilterApplicationLocked(setting, callingUid, callingUserId)) { + final boolean filterApp; + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(packageName); + filterApp = shouldFilterApplicationLocked(ps, callingUid, callingUserId); + } + if (!filterApp) { notifyInstallObserver(packageName); } } @@ -2443,6 +2457,68 @@ public class PackageManagerService extends IPackageManager.Stub mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS); } + @Override + public void getChecksums(@NonNull String packageName, boolean includeSplits, + @PackageManager.FileChecksumKind int optional, + @PackageManager.FileChecksumKind int required, @Nullable List trustedInstallers, + @NonNull IntentSender statusReceiver, int userId) { + Objects.requireNonNull(packageName); + Objects.requireNonNull(statusReceiver); + + final ApplicationInfo applicationInfo = getApplicationInfoInternal(packageName, 0, + Binder.getCallingUid(), userId); + if (applicationInfo == null) { + throw new ParcelableException(new PackageManager.NameNotFoundException(packageName)); + } + + List<Pair<String, File>> filesToChecksum = new ArrayList<>(); + + // Adding base split. + filesToChecksum.add(Pair.create(null, new File(applicationInfo.sourceDir))); + + // Adding other splits. + if (includeSplits && applicationInfo.splitNames != null) { + for (int i = 0, size = applicationInfo.splitNames.length; i < size; ++i) { + filesToChecksum.add(Pair.create(applicationInfo.splitNames[i], + new File(applicationInfo.splitSourceDirs[i]))); + } + } + + for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { + final File file = filesToChecksum.get(i).second; + if (!file.exists()) { + throw new IllegalStateException("File not found: " + file.getPath()); + } + } + + final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates( + trustedInstallers) : null; + + mInjector.getBackgroundExecutor().execute(() -> { + ApkChecksums.Injector injector = new ApkChecksums.Injector( + () -> mContext, + () -> mInjector.getBackgroundHandler(), + () -> mContext.getSystemService(IncrementalManager.class)); + ApkChecksums.getChecksums(filesToChecksum, optional, required, trustedCerts, + statusReceiver, injector); + }); + } + + private static @NonNull Certificate[] decodeCertificates(@NonNull List certs) { + try { + final CertificateFactory cf = CertificateFactory.getInstance("X.509"); + final Certificate[] result = new Certificate[certs.size()]; + for (int i = 0, size = certs.size(); i < size; ++i) { + final InputStream is = new ByteArrayInputStream((byte[]) certs.get(i)); + final X509Certificate cert = (X509Certificate) cf.generateCertificate(is); + result[i] = cert; + } + return result; + } catch (CertificateException e) { + throw ExceptionUtils.propagate(e); + } + } + /** * Gets the type of the external storage a package is installed on. * @param packageVolume The storage volume of the package. @@ -2597,7 +2673,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector injector = new Injector( context, lock, installer, installLock, new PackageAbiHelperImpl(), - new HandlerExecutor(backgroundHandler), + backgroundHandler, (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), (i, pm) -> @@ -3268,6 +3344,7 @@ public class PackageManagerService extends IPackageManager.Stub // Remove disable package settings for updated system apps that were // removed via an OTA. If the update is no longer present, remove the // app completely. Otherwise, revoke their system privileges. + final int[] userIds = mUserManager.getUserIds(); for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) { final String packageName = possiblyDeletedUpdatedSystemApps.get(i); final AndroidPackage pkg = mPackages.get(packageName); @@ -3310,7 +3387,7 @@ public class PackageManagerService extends IPackageManager.Stub // partition], completely remove the package data. final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && mPackages.get(packageName) == null) { - removePackageDataLIF(ps, null, null, 0, false); + removePackageDataLIF(ps, userIds, null, 0, false); } logCriticalInfo(Log.WARN, msg); @@ -3777,8 +3854,9 @@ public class PackageManagerService extends IPackageManager.Stub // If we don't, installing the system package fails during scan enableSystemPackageLPw(stubPkg); } - installPackageFromSystemLIF(stubPkg.getCodePath(), null /*allUserHandles*/, - null /*origUserHandles*/, true /*writeSettings*/); + installPackageFromSystemLIF(stubPkg.getCodePath(), + mUserManager.getUserIds() /*allUserHandles*/, null /*origUserHandles*/, + true /*writeSettings*/); } catch (PackageManagerException pme) { // Serious WTF; we have to be able to install the stub Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(), @@ -8964,10 +9042,10 @@ public class PackageManagerService extends IPackageManager.Stub if (providerInfo == null) { return null; } - if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { - return null; - } synchronized (mLock) { + if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { + return null; + } final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); @@ -9054,9 +9132,11 @@ public class PackageManagerService extends IPackageManager.Stub String targetPackage, int flags) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); - final PackageSetting ps = mSettings.mPackages.get(targetPackage); - if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { - return ParceledListSlice.emptyList(); + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(targetPackage); + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { + return ParceledListSlice.emptyList(); + } } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags, callingUserId)); @@ -9504,8 +9584,8 @@ public class PackageManagerService extends IPackageManager.Stub try (@SuppressWarnings("unused") PackageFreezer freezer = freezePackage( parsedPackage.getPackageName(), "scanPackageInternalLI")) { - deletePackageLIF(parsedPackage.getPackageName(), null, true, null, 0, null, - false, null); + deletePackageLIF(parsedPackage.getPackageName(), null, true, + mUserManager.getUserIds(), 0, null, false, null); } pkgSetting = null; } else if (newPkgVersionGreater) { @@ -11587,7 +11667,7 @@ public class PackageManagerService extends IPackageManager.Stub configurePackageComponents(parsedPackage); } - final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting); + final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride); final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp(); if ((scanFlags & SCAN_NEW_INSTALL) == 0) { @@ -13793,7 +13873,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId); final long callingId = Binder.clearCallingIdentity(); try { - final String activeLauncherPackageName = getActiveLauncherPackageName(userId); + final String activeLauncherPackageName = mPermissionManager.getDefaultHome(userId); final String dialerPackageName = mPermissionManager.getDefaultDialer(userId); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; @@ -13869,18 +13949,6 @@ public class PackageManagerService extends IPackageManager.Stub return canSuspend; } - private String getActiveLauncherPackageName(int userId) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_HOME); - ResolveInfo resolveInfo = resolveIntent( - intent, - intent.resolveTypeIfNeeded(mContext.getContentResolver()), - PackageManager.MATCH_DEFAULT_ONLY, - userId); - - return resolveInfo == null ? null : resolveInfo.activityInfo.packageName; - } - @Override public void verifyPendingInstall(int id, int verificationCode) throws RemoteException { mContext.enforceCallingOrSelfPermission( @@ -14572,7 +14640,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting ps; int appId = -1; long ceDataInode = -1; - synchronized (mSettings) { + synchronized (mLock) { ps = mSettings.getPackageLPr(packageName); if (ps != null) { appId = ps.appId; @@ -16322,9 +16390,11 @@ public class PackageManagerService extends IPackageManager.Stub */ private static class CommitRequest { final Map<String, ReconciledPackage> reconciledPackages; + @NonNull final int[] mAllUsers; - private CommitRequest(Map<String, ReconciledPackage> reconciledPackages, int[] allUsers) { + private CommitRequest(Map<String, ReconciledPackage> reconciledPackages, + @NonNull int[] allUsers) { this.reconciledPackages = reconciledPackages; this.mAllUsers = allUsers; } @@ -17620,7 +17690,7 @@ public class PackageManagerService extends IPackageManager.Stub } boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null && pkgSetting.getPkgState().isUpdatedSystemApp(); - final String abiOverride = deriveAbiOverride(args.abiOverride, pkgSetting); + final String abiOverride = deriveAbiOverride(args.abiOverride); AndroidPackage oldPackage = mPackages.get(pkgName); boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem(); final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> @@ -18837,7 +18907,7 @@ public class PackageManagerService extends IPackageManager.Stub * make sure this flag is set for partially installed apps. If not its meaningless to * delete a partially installed application. */ - private void removePackageDataLIF(final PackageSetting deletedPs, int[] allUserHandles, + private void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles, PackageRemovedInfo outInfo, int flags, boolean writeSettings) { String packageName = deletedPs.name; if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs); @@ -18925,7 +18995,7 @@ public class PackageManagerService extends IPackageManager.Stub } // make sure to preserve per-user disabled state if this removal was just // a downgrade of a system app to the factory package - if (allUserHandles != null && outInfo != null && outInfo.origUsers != null) { + if (outInfo != null && outInfo.origUsers != null) { if (DEBUG_REMOVE) { Slog.d(TAG, "Propagating install state across downgrade"); } @@ -18978,11 +19048,10 @@ public class PackageManagerService extends IPackageManager.Stub * Tries to delete system package. */ private void deleteSystemPackageLIF(DeletePackageAction action, PackageSetting deletedPs, - int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo, + @NonNull int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo, boolean writeSettings) throws SystemDeleteException { - final boolean applyUserRestrictions = - (allUserHandles != null) && outInfo != null && (outInfo.origUsers != null); + final boolean applyUserRestrictions = outInfo != null && (outInfo.origUsers != null); final AndroidPackage deletedPkg = deletedPs.pkg; // Confirm if the system package has been updated // An updated system app can be deleted. This will also have to restore @@ -19066,7 +19135,7 @@ public class PackageManagerService extends IPackageManager.Stub * Installs a package that's already on the system partition. */ private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString, - @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) + @NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) throws PackageManagerException { final File codePath = new File(codePathString); @ParseFlags int parseFlags = @@ -19108,8 +19177,7 @@ public class PackageManagerService extends IPackageManager.Stub // and granting install permissions. mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); - final boolean applyUserRestrictions - = (allUserHandles != null) && (origUserHandles != null); + final boolean applyUserRestrictions = origUserHandles != null; if (applyUserRestrictions) { boolean installedStateChanged = false; if (DEBUG_REMOVE) { @@ -19146,7 +19214,7 @@ public class PackageManagerService extends IPackageManager.Stub } private void deleteInstalledPackageLIF(PackageSetting ps, - boolean deleteCodeAndResources, int flags, int[] allUserHandles, + boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles, PackageRemovedInfo outInfo, boolean writeSettings) { synchronized (mLock) { if (outInfo != null) { @@ -19266,7 +19334,7 @@ public class PackageManagerService extends IPackageManager.Stub * This method handles package deletion in general */ private boolean deletePackageLIF(@NonNull String packageName, UserHandle user, - boolean deleteCodeAndResources, int[] allUserHandles, int flags, + boolean deleteCodeAndResources, @NonNull int[] allUserHandles, int flags, PackageRemovedInfo outInfo, boolean writeSettings, ParsedPackage replacingPackage) { final DeletePackageAction action; @@ -19303,7 +19371,7 @@ public class PackageManagerService extends IPackageManager.Stub /** Deletes a package. Only throws when install of a disabled package fails. */ private void executeDeletePackageLIF(DeletePackageAction action, String packageName, boolean deleteCodeAndResources, - int[] allUserHandles, boolean writeSettings, + @NonNull int[] allUserHandles, boolean writeSettings, ParsedPackage replacingPackage) throws SystemDeleteException { final PackageSetting ps = action.deletingPs; final PackageRemovedInfo outInfo = action.outInfo; @@ -19453,6 +19521,9 @@ public class PackageManagerService extends IPackageManager.Stub } if (outInfo != null) { + if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { + outInfo.dataRemoved = true; + } outInfo.removedPackage = ps.name; outInfo.installerPackageName = ps.installSource.installerPackageName; outInfo.isStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null; @@ -19488,9 +19559,11 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "clear application data"); - final PackageSetting ps = mSettings.getPackageLPr(packageName); - final boolean filterApp = - (ps != null && shouldFilterApplicationLocked(ps, callingUid, userId)); + final boolean filterApp; + synchronized (mLock) { + final PackageSetting ps = mSettings.getPackageLPr(packageName); + filterApp = shouldFilterApplicationLocked(ps, callingUid, userId); + } if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) { throw new SecurityException("Cannot clear data for a protected package: " + packageName); @@ -19770,11 +19843,13 @@ public class PackageManagerService extends IPackageManager.Stub if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (getUidTargetSdkVersionLockedLPr(callingUid) - < Build.VERSION_CODES.FROYO) { - Slog.w(TAG, "Ignoring addPreferredActivity() from uid " - + callingUid); - return; + synchronized (mLock) { + if (getUidTargetSdkVersionLockedLPr(callingUid) + < Build.VERSION_CODES.FROYO) { + Slog.w(TAG, "Ignoring addPreferredActivity() from uid " + + callingUid); + return; + } } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); @@ -19954,8 +20029,9 @@ public class PackageManagerService extends IPackageManager.Stub /** This method takes a specific user id as well as UserHandle.USER_ALL. */ private void clearPackagePreferredActivities(String packageName, int userId) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); - - clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId); + synchronized (mLock) { + clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId); + } if (changedUsers.size() > 0) { updateDefaultHomeNotLocked(changedUsers); postPreferredActivityChangedBroadcast(userId); @@ -20077,7 +20153,9 @@ public class PackageManagerService extends IPackageManager.Stub // writer try { final SparseBooleanArray changedUsers = new SparseBooleanArray(); - clearPackagePreferredActivitiesLPw(null, changedUsers, userId); + synchronized (mLock) { + clearPackagePreferredActivitiesLPw(null, changedUsers, userId); + } if (changedUsers.size() > 0) { postPreferredActivityChangedBroadcast(userId); } @@ -20545,6 +20623,9 @@ public class PackageManagerService extends IPackageManager.Stub if (cn != null) { return cn; } + // TODO: This should not happen since there should always be a default package set for + // ROLE_HOME in RoleManager. Continue with a warning log for now. + Slog.w(TAG, "Default package for ROLE_HOME is not set in RoleManager"); // Find the launcher with the highest priority and return that component if there are no // other home activity with the same priority. @@ -20593,6 +20674,7 @@ public class PackageManagerService extends IPackageManager.Stub if (packageName == null) { return null; } + int resolveInfosSize = resolveInfos.size(); for (int i = 0; i < resolveInfosSize; i++) { ResolveInfo resolveInfo = resolveInfos.get(i); @@ -20652,6 +20734,11 @@ public class PackageManagerService extends IPackageManager.Stub // PermissionController manages default home directly. return false; } + + if (packageName == null) { + // Keep the default home package in RoleManager. + return false; + } mPermissionManager.setDefaultHome(packageName, userId, (successful) -> { if (successful) { postPreferredActivityChangedBroadcast(userId); @@ -21087,15 +21174,19 @@ public class PackageManagerService extends IPackageManager.Stub // Limit who can change which apps if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) { // Don't allow apps that don't have permission to modify other apps - if (!allowedByPermission - || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { + final boolean filterApp; + synchronized (mLock) { + filterApp = (!allowedByPermission + || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)); + } + if (filterApp) { throw new SecurityException( "Attempt to change component state; " - + "pid=" + Binder.getCallingPid() - + ", uid=" + callingUid - + (className == null + + "pid=" + Binder.getCallingPid() + + ", uid=" + callingUid + + (className == null ? ", package=" + packageName - : ", component=" + packageName + "/" + className)); + : ", component=" + packageName + "/" + className)); } // Don't allow changing protected packages. if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { @@ -21558,8 +21649,6 @@ public class PackageManagerService extends IPackageManager.Stub .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL); co.onChange(true); - mAppsFilter.onSystemReady(); - // Disable any carrier apps. We do this very early in boot to prevent the apps from being // disabled after already being started. CarrierAppUtils.disableCarrierAppsUntilPrivileged( @@ -21708,6 +21797,9 @@ public class PackageManagerService extends IPackageManager.Stub mInstallerService.restoreAndApplyStagedSessionIfNeeded(); mExistingPackages = null; + + // We'll do this last as it builds its cache while holding mLock via callback. + mAppsFilter.onSystemReady(); } public void waitForAppDataPrepared() { @@ -22694,6 +22786,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } + final int[] userIds = mUserManager.getUserIds(); final ArrayList<AndroidPackage> unloaded = new ArrayList<>(); synchronized (mInstallLock) { synchronized (mLock) { @@ -22707,7 +22800,7 @@ public class PackageManagerService extends IPackageManager.Stub try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags, "unloadPrivatePackagesInner")) { - if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo, + if (deletePackageLIF(ps.name, null, false, userIds, deleteFlags, outInfo, false, null)) { unloaded.add(pkg); } else { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 491b4fc515ce..5553cd0e2fb8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -421,18 +421,13 @@ public class PackageManagerServiceUtils { /** * Derive the value of the {@code cpuAbiOverride} based on the provided - * value and an optional stored value from the package settings. + * value. */ - public static String deriveAbiOverride(String abiOverride, PackageSetting settings) { - String cpuAbiOverride = null; + public static String deriveAbiOverride(String abiOverride) { if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) { - cpuAbiOverride = null; - } else if (abiOverride != null) { - cpuAbiOverride = abiOverride; - } else if (settings != null) { - cpuAbiOverride = settings.cpuAbiOverrideString; + return null; } - return cpuAbiOverride; + return abiOverride; } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java index 3614cc047bf8..2bbca79741bd 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java @@ -67,7 +67,6 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService { } } - // Sanity check. if (sShellCommands.size() > TOO_MANY_PENDING_SHELL_COMMANDS) { Slog.e(TAG, "Too many pending shell commands: " + sShellCommands.size()); } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 0c4eaec32ba5..462b21535371 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -60,7 +60,6 @@ import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.apk.ApkSignatureVerifier; @@ -1028,22 +1027,12 @@ public class StagingManager { /** * <p>Abort committed staged session - * - * <p>This method must be called while holding {@link PackageInstallerSession#mLock}. - * - * <p>The method returns {@code false} to indicate it is not safe to clean up the session from - * system yet. When it is safe, the method returns {@code true}. - * - * <p> When it is safe to clean up, {@link StagingManager} will call - * {@link PackageInstallerSession#abandon()} on the session again. - * - * @return {@code true} if it is safe to cleanup the session resources, otherwise {@code false}. */ - boolean abortCommittedSessionLocked(@NonNull PackageInstallerSession session) { + void abortCommittedSession(@NonNull PackageInstallerSession session) { int sessionId = session.sessionId; - if (session.isStagedSessionApplied()) { - Slog.w(TAG, "Cannot abort applied session : " + sessionId); - return false; + if (session.isStagedAndInTerminalState()) { + Slog.w(TAG, "Cannot abort session in final state: " + sessionId); + return; } if (!session.isDestroyed()) { throw new IllegalStateException("Committed session must be destroyed before aborting it" @@ -1051,15 +1040,7 @@ public class StagingManager { } if (getStagedSession(sessionId) == null) { Slog.w(TAG, "Session " + sessionId + " has been abandoned already"); - return false; - } - - // If pre-reboot verification is running, then return false. StagingManager will call - // abandon again when pre-reboot verification ends. - if (mPreRebootVerificationHandler.isVerificationRunning(sessionId)) { - Slog.w(TAG, "Session " + sessionId + " aborted before pre-reboot " - + "verification completed."); - return false; + return; } // A session could be marked ready once its pre-reboot verification ends @@ -1075,7 +1056,6 @@ public class StagingManager { // Session was successfully aborted from apexd (if required) and pre-reboot verification // is also complete. It is now safe to clean up the session from system. abortSession(session); - return true; } /** @@ -1264,8 +1244,8 @@ public class StagingManager { // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all // verification logic is extracted out of StagingManager into PMS, we can remove // this. - void notifyVerificationComplete(int sessionId) { - mPreRebootVerificationHandler.onPreRebootVerificationComplete(sessionId); + void notifyVerificationComplete(PackageInstallerSession session) { + mPreRebootVerificationHandler.onPreRebootVerificationComplete(session); } // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all @@ -1279,8 +1259,6 @@ public class StagingManager { // Hold session ids before handler gets ready to do the verification. private IntArray mPendingSessionIds; private boolean mIsReady; - @GuardedBy("mVerificationRunning") - private final SparseBooleanArray mVerificationRunning = new SparseBooleanArray(); PreRebootVerificationHandler(Looper looper) { super(looper); @@ -1316,7 +1294,7 @@ public class StagingManager { } if (session.isDestroyed() || session.isStagedSessionFailed()) { // No point in running verification on a destroyed/failed session - onPreRebootVerificationComplete(sessionId); + onPreRebootVerificationComplete(session); return; } switch (msg.what) { @@ -1357,15 +1335,10 @@ public class StagingManager { } PackageInstallerSession session = getStagedSession(sessionId); - synchronized (mVerificationRunning) { - // Do not start verification on a session that has been abandoned - if (session == null || session.isDestroyed()) { - return; - } + if (session != null && session.notifyStagedStartPreRebootVerification()) { Slog.d(TAG, "Starting preRebootVerification for session " + sessionId); - mVerificationRunning.put(sessionId, true); + obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget(); } - obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget(); } private void onPreRebootVerificationFailure(PackageInstallerSession session, @@ -1376,28 +1349,14 @@ public class StagingManager { // failed on next step and staging directory for session will be deleted. } session.setStagedSessionFailed(errorCode, errorMessage); - onPreRebootVerificationComplete(session.sessionId); + onPreRebootVerificationComplete(session); } // Things to do when pre-reboot verification completes for a particular sessionId - private void onPreRebootVerificationComplete(int sessionId) { - // Remove it from mVerificationRunning so that verification is considered complete - synchronized (mVerificationRunning) { - Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId); - mVerificationRunning.delete(sessionId); - } - // Check if the session was destroyed while pre-reboot verification was running. If so, - // abandon it again. - PackageInstallerSession session = getStagedSession(sessionId); - if (session != null && session.isDestroyed()) { - session.abandon(); - } - } - - private boolean isVerificationRunning(int sessionId) { - synchronized (mVerificationRunning) { - return mVerificationRunning.get(sessionId); - } + private void onPreRebootVerificationComplete(PackageInstallerSession session) { + int sessionId = session.sessionId; + Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId); + session.notifyStagedEndPreRebootVerification(); } private void notifyPreRebootVerification_Start_Complete(int sessionId) { @@ -1516,7 +1475,7 @@ public class StagingManager { // or activate its apex, there won't be any files to work with as they will be cleaned // up by the system as part of abandonment. If session is abandoned before this point, // then the session is already destroyed and cannot be marked ready anymore. - onPreRebootVerificationComplete(session.sessionId); + onPreRebootVerificationComplete(session); // Proactively mark session as ready before calling apexd. Although this call order // looks counter-intuitive, this is the easiest way to ensure that session won't end up diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index dbe96e63d978..485868237895 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -33,6 +33,9 @@ "name": "CtsContentTestCases", "options": [ { + "include-filter": "android.content.pm.cts.ChecksumsTest" + }, + { "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest" }, { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d137fd05f793..6ecaab6692a2 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -407,6 +407,10 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private int[] mUserIds; + + @GuardedBy("mUsersLock") + private int[] mUserIdsIncludingPreCreated; + @GuardedBy("mPackagesLock") private int mNextSerialNumber; private int mUserVersion = 0; @@ -760,6 +764,8 @@ public class UserManagerService extends IUserManager.Stub { return null; } + // TODO(b/157921703): replace by getAliveUsers() or remove (so callers + // explicitly call the 3-booleans version) public @NonNull List<UserInfo> getUsers(boolean excludeDying) { return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -2494,16 +2500,35 @@ public class UserManagerService extends IUserManager.Stub { } /** - * Returns an array of user ids. This array is cached here for quick access, so do not modify or - * cache it elsewhere. + * Returns an array of user ids. + * + * <p>This array is cached here for quick access, so do not modify or cache it elsewhere. + * * @return the array of user ids. */ - public int[] getUserIds() { + public @NonNull int[] getUserIds() { synchronized (mUsersLock) { return mUserIds; } } + /** + * Returns an array of user ids, including pre-created users. + * + * <p>This method should only used for the specific cases that need to handle pre-created users; + * most callers should call {@link #getUserIds()} instead. + * + * <p>This array is cached here for quick access, so do not modify or + * cache it elsewhere. + * + * @return the array of user ids. + */ + public @NonNull int[] getUserIdsIncludingPreCreated() { + synchronized (mUsersLock) { + return mUserIdsIncludingPreCreated; + } + } + @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) private void readUserListLP() { if (!mUserListFile.exists()) { @@ -4359,23 +4384,43 @@ public class UserManagerService extends IUserManager.Stub { */ private void updateUserIds() { int num = 0; + int numIncludingPreCreated = 0; synchronized (mUsersLock) { final int userSize = mUsers.size(); for (int i = 0; i < userSize; i++) { - UserInfo userInfo = mUsers.valueAt(i).info; - if (!userInfo.partial && !userInfo.preCreated) { - num++; + final UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial) { + numIncludingPreCreated++; + if (!userInfo.preCreated) { + num++; + } } } + if (DBG) { + Slog.d(LOG_TAG, "updateUserIds(): numberUsers= " + num + + " includingPreCreated=" + numIncludingPreCreated); + } final int[] newUsers = new int[num]; + final int[] newUsersIncludingPreCreated = new int[numIncludingPreCreated]; + int n = 0; + int nIncludingPreCreated = 0; for (int i = 0; i < userSize; i++) { - UserInfo userInfo = mUsers.valueAt(i).info; - if (!userInfo.partial && !userInfo.preCreated) { - newUsers[n++] = mUsers.keyAt(i); + final UserInfo userInfo = mUsers.valueAt(i).info; + if (!userInfo.partial) { + final int userId = mUsers.keyAt(i); + newUsersIncludingPreCreated[nIncludingPreCreated++] = userId; + if (!userInfo.preCreated) { + newUsers[n++] = userId; + } } } mUserIds = newUsers; + mUserIdsIncludingPreCreated = newUsersIncludingPreCreated; + if (DBG) { + Slog.d(LOG_TAG, "updateUserIds(): userIds= " + Arrays.toString(mUserIds) + + " includingPreCreated=" + Arrays.toString(mUserIdsIncludingPreCreated)); + } } } @@ -4839,6 +4884,8 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mUsersLock) { pw.print(" Cached user IDs: "); pw.println(Arrays.toString(mUserIds)); + pw.print(" Cached user IDs (including pre-created): "); + pw.println(Arrays.toString(mUserIdsIncludingPreCreated)); } } // synchronized (mPackagesLock) diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 75a88e2e04b6..f7721a4d6f22 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -414,17 +414,14 @@ public final class DefaultPermissionGrantPolicy { if (pkg == null || !doesPackageSupportRuntimePermissions(pkg) || ArrayUtils.isEmpty(pkg.requestedPermissions) - || !pkg.applicationInfo.isPrivilegedApp()) { + || !pm.isGranted(Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + pkg, UserHandle.of(userId))) { continue; } - for (String permission : pkg.requestedPermissions) { - if (Manifest.permission.READ_PRIVILEGED_PHONE_STATE.equals(permission)) { - grantRuntimePermissions(pm, pkg, - Collections.singleton(Manifest.permission.READ_PHONE_STATE), - true, // systemFixed - userId); - } - } + grantRuntimePermissions(pm, pkg, + Collections.singleton(Manifest.permission.READ_PHONE_STATE), + true, // systemFixed + userId); } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index f5dd918a18f3..ffdcc227b7f1 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -83,7 +83,6 @@ import android.content.pm.PackageParser; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.UserInfo; import android.content.pm.parsing.component.ParsedPermission; import android.content.pm.parsing.component.ParsedPermissionGroup; import android.content.pm.permission.SplitPermissionInfoParcelable; @@ -121,7 +120,6 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; -import android.util.TimingsTraceLog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -3146,17 +3144,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @return user ids for created users and pre-created users */ private int[] getAllUserIds() { - final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER); - t.traceBegin("getAllUserIds"); - List<UserInfo> users = UserManagerService.getInstance().getUsers( - /*excludePartial=*/ true, /*excludeDying=*/ true, /*excludePreCreated=*/ false); - int size = users.size(); - final int[] userIds = new int[size]; - for (int i = 0; i < size; i++) { - userIds[i] = users.get(i).id; - } - t.traceEnd(); - return userIds; + return UserManagerService.getInstance().getUserIdsIncludingPreCreated(); } /** diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index ae2b040d0a89..e1cd9e334f4c 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -31,6 +31,7 @@ import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.AppOpsManagerInternal; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -242,8 +243,9 @@ public final class PermissionPolicyService extends SystemService { public void onReceive(Context context, Intent intent) { boolean hasSetupRun = true; try { - hasSetupRun = Settings.Secure.getInt(getContext().getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE) != 0; + final ContentResolver cr = getContext().getContentResolver(); + hasSetupRun = Settings.Secure.getIntForUser(cr, + Settings.Secure.USER_SETUP_COMPLETE, cr.getUserId()) != 0; } catch (Settings.SettingNotFoundException e) { // Ignore error, assume setup has run } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 137c587b1d79..d01a30fbd818 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -69,6 +69,7 @@ import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; @@ -148,6 +149,7 @@ import android.os.UEventObserver; import android.os.UserHandle; import android.os.VibrationEffect; import android.os.Vibrator; +import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings; import android.service.dreams.DreamManagerInternal; @@ -1378,12 +1380,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private long getScreenshotChordLongPressDelay() { + long delayMs = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY, + ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout()); if (mKeyguardDelegate.isShowing()) { // Double the time it takes to take a screenshot from the keyguard - return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * - ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout()); + return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs); } - return ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout(); + return delayMs; } private long getRingerToggleChordDelay() { diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index 2b793c894eb0..f204aa2cc1ca 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -52,6 +52,9 @@ abstract public class VerityUtils { /** The maximum size of signature file. This is just to avoid potential abuse. */ private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192; + /** SHA256 hash size. */ + private static final int HASH_SIZE_BYTES = 32; + private static final boolean DEBUG = false; /** Returns true if the given file looks like containing an fs-verity signature. */ @@ -90,8 +93,23 @@ abstract public class VerityUtils { return (retval == 1); } + /** Returns hash of a root node for the fs-verity enabled file. */ + public static byte[] getFsverityRootHash(@NonNull String filePath) { + byte[] result = new byte[HASH_SIZE_BYTES]; + int retval = measureFsverityNative(filePath, result); + if (retval < 0) { + if (retval != -OsConstants.ENODATA) { + Slog.e(TAG, "Failed to measure fs-verity, errno " + -retval + ": " + filePath); + } + return null; + } + return result; + } + private static native int enableFsverityNative(@NonNull String filePath, @NonNull byte[] pkcs7Signature); + private static native int measureFsverityNative(@NonNull String filePath, + @NonNull byte[] digest); private static native int statxForFsverityNative(@NonNull String filePath); /** diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java index 6f5afa207d31..d25ddf877951 100644 --- a/services/core/java/com/android/server/slice/SliceFullAccessList.java +++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java @@ -101,7 +101,7 @@ public class SliceFullAccessList { public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException { // upgrade xml int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0); - final List<UserInfo> activeUsers = UserManager.get(mContext).getUsers(true); + final List<UserInfo> activeUsers = UserManager.get(mContext).getAliveUsers(); for (UserInfo userInfo : activeUsers) { upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier()); } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java index 8e5ecee8262b..ebe9733e5d55 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java @@ -35,7 +35,8 @@ import java.util.TimerTask; * HAL whenever they expire. */ public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 { - private static final long TIMEOUT_MS = 1000; + // TODO(b/166328980): Reduce this to 1000 as soon as HAL is fixed. + private static final long TIMEOUT_MS = 10000; private static final String TAG = "SoundTriggerHw2Watchdog"; private final @NonNull diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java index c382e1152e4b..5a587cc9764c 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -36,7 +36,6 @@ import android.media.soundtrigger_middleware.Status; import android.os.IBinder; import android.os.IHwBinder; import android.os.RemoteException; -import android.os.ServiceSpecificException; import android.util.Log; import java.util.ArrayList; @@ -293,7 +292,11 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient { } catch (Exception e) { // We must do this outside the lock, to avoid possible deadlocks with the remote // process that provides the audio sessions, which may also be calling into us. - mAudioSessionProvider.releaseSession(audioSession.mSessionHandle); + try { + mAudioSessionProvider.releaseSession(audioSession.mSessionHandle); + } catch (Exception ee) { + Log.e(TAG, "Failed to release session.", ee); + } throw e; } } @@ -321,7 +324,11 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient { } catch (Exception e) { // We must do this outside the lock, to avoid possible deadlocks with the remote // process that provides the audio sessions, which may also be calling into us. - mAudioSessionProvider.releaseSession(audioSession.mSessionHandle); + try { + mAudioSessionProvider.releaseSession(audioSession.mSessionHandle); + } catch (Exception ee) { + Log.e(TAG, "Failed to release session.", ee); + } throw e; } } diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java index aee3d8d3499b..b68c54fc6365 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java @@ -25,6 +25,7 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.timezonedetector.TimeZoneCapabilities; import android.app.timezonedetector.TimeZoneConfiguration; +import android.os.UserHandle; import java.util.Objects; @@ -56,6 +57,12 @@ public final class ConfigurationInternal { return mUserId; } + /** Returns the handle of the user this configuration is associated with. */ + @NonNull + public UserHandle getUserHandle() { + return UserHandle.of(mUserId); + } + /** Returns true if the user allowed to modify time zone configuration. */ public boolean isUserConfigAllowed() { return mUserConfigAllowed; @@ -198,13 +205,13 @@ public final class ConfigurationInternal { @Override public String toString() { - return "TimeZoneDetectorConfiguration{" + return "ConfigurationInternal{" + "mUserId=" + mUserId - + "mUserConfigAllowed=" + mUserConfigAllowed - + "mAutoDetectionSupported=" + mAutoDetectionSupported - + "mAutoDetectionEnabled=" + mAutoDetectionEnabled - + "mLocationEnabled=" + mLocationEnabled - + "mGeoDetectionEnabled=" + mGeoDetectionEnabled + + ", mUserConfigAllowed=" + mUserConfigAllowed + + ", mAutoDetectionSupported=" + mAutoDetectionSupported + + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled + + ", mLocationEnabled=" + mLocationEnabled + + ", mGeoDetectionEnabled=" + mGeoDetectionEnabled + '}'; } diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING new file mode 100644 index 000000000000..91e172c9d153 --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.timezonedetector." + } + ] + } + ] +} diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java index 3230ef192b4b..a8d5c0282025 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java @@ -23,7 +23,7 @@ import java.io.PrintWriter; import java.util.function.Consumer; import java.util.function.Supplier; -/** Implemented the shell command interface for {@link TimeZoneDetectorService}. */ +/** Implements the shell command interface for {@link TimeZoneDetectorService}. */ class TimeZoneDetectorShellCommand extends ShellCommand { private final TimeZoneDetectorService mInterface; diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 0c85387be695..89b108c24630 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -126,6 +126,8 @@ public class TrustManagerService extends SystemService { private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser"; private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000; + private static final String PRIV_NAMESPACE = "http://schemas.android.com/apk/prv/res/android"; + private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>(); private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>(); private final Receiver mReceiver = new Receiver(); @@ -379,7 +381,7 @@ public class TrustManagerService extends SystemService { } private void updateTrustAll() { - List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */); + List<UserInfo> userInfos = mUserManager.getAliveUsers(); for (UserInfo userInfo : userInfos) { updateTrust(userInfo.id, 0); } @@ -485,7 +487,7 @@ public class TrustManagerService extends SystemService { List<UserInfo> userInfos; if (userIdOrAll == UserHandle.USER_ALL) { - userInfos = mUserManager.getUsers(true /* excludeDying */); + userInfos = mUserManager.getAliveUsers(); } else { userInfos = new ArrayList<>(); userInfos.add(mUserManager.getUserInfo(userIdOrAll)); @@ -644,7 +646,7 @@ public class TrustManagerService extends SystemService { } List<UserInfo> userInfos; if (userId == UserHandle.USER_ALL) { - userInfos = mUserManager.getUsers(true /* excludeDying */); + userInfos = mUserManager.getAliveUsers(); } else { userInfos = new ArrayList<>(); userInfos.add(mUserManager.getUserInfo(userId)); @@ -811,8 +813,8 @@ public class TrustManagerService extends SystemService { TypedArray sa = res .obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent); cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity); - canUnlockProfile = sa.getBoolean( - com.android.internal.R.styleable.TrustAgent_unlockProfile, false); + canUnlockProfile = attrs.getAttributeBooleanValue( + PRIV_NAMESPACE, "unlockProfile", false); sa.recycle(); } catch (PackageManager.NameNotFoundException e) { caughtException = e; @@ -1171,7 +1173,7 @@ public class TrustManagerService extends SystemService { fout.println("disabled because the third-party apps can't run yet."); return; } - final List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */); + final List<UserInfo> userInfos = mUserManager.getAliveUsers(); mHandler.runWithScissors(new Runnable() { @Override public void run() { diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 0b0bb7059f3b..53d51463295f 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -115,7 +115,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { private static final String TAG = "UriGrantsManagerService"; // Maximum number of persisted Uri grants a package is allowed private static final int MAX_PERSISTED_URI_GRANTS = 512; - private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true; + private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false; private final Object mLock = new Object(); private final H mH; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 56261c4fce97..6df46ed2532b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -133,6 +133,7 @@ import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING; import static com.android.server.wm.ActivityRecordProto.NAME; import static com.android.server.wm.ActivityRecordProto.NUM_DRAWN_WINDOWS; import static com.android.server.wm.ActivityRecordProto.NUM_INTERESTING_WINDOWS; +import static com.android.server.wm.ActivityRecordProto.PIP_AUTO_ENTER_ALLOWED; import static com.android.server.wm.ActivityRecordProto.PROC_ID; import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN; import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE; @@ -401,7 +402,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final ActivityTaskManagerService mAtmService; final ActivityInfo info; // activity info provided by developer in AndroidManifest - // Non-null only for application tokens. // TODO: rename to mActivityToken final ActivityRecord.Token appToken; // Which user is this running for? @@ -1587,7 +1587,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A hasBeenLaunched = false; mStackSupervisor = supervisor; - info.taskAffinity = getTaskAffinityWithUid(info.taskAffinity, info.applicationInfo.uid); + info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid, + launchMode); taskAffinity = info.taskAffinity; final String uid = Integer.toString(info.applicationInfo.uid); if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null @@ -1648,17 +1649,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * Generate the task affinity with uid. For b/35954083, Limit task affinity to uid to avoid - * issues associated with sharing affinity across uids. + * Generate the task affinity with uid and activity launch mode. For b/35954083, Limit task + * affinity to uid to avoid issues associated with sharing affinity across uids. * * @param affinity The affinity of the activity. * @param uid The user-ID that has been assigned to this application. - * @return The task affinity with uid. + * @param launchMode The activity launch mode + * @return The task affinity */ - static String getTaskAffinityWithUid(String affinity, int uid) { + static String computeTaskAffinity(String affinity, int uid, int launchMode) { final String uidStr = Integer.toString(uid); if (affinity != null && !affinity.startsWith(uidStr)) { - affinity = uidStr + ":" + affinity; + affinity = uidStr + (launchMode == LAUNCH_SINGLE_INSTANCE ? "-si:" : ":") + affinity; } return affinity; } @@ -2123,11 +2125,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return task != null ? task.getRootTaskId() : INVALID_TASK_ID; } - DisplayContent getDisplay() { - final Task stack = getRootTask(); - return stack != null ? stack.getDisplay() : null; - } - @Override @Nullable TaskDisplayArea getDisplayArea() { @@ -2387,7 +2384,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - return (canReceiveKeys() || isAlwaysFocusable()) && getDisplay() != null; + // Check isAttached() because the method may be called when removing this activity from + // display, and WindowContainer#compareTo will throw exception if it doesn't have a parent + // when updating focused window from DisplayContent#findFocusedWindow. + return (canReceiveKeys() || isAlwaysFocusable()) && isAttached(); } /** @@ -2664,7 +2664,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } private void prepareActivityHideTransitionAnimation(int transit) { - final DisplayContent dc = getDisplay().mDisplayContent; + final DisplayContent dc = mDisplayContent; dc.prepareAppTransition(transit, false); setVisibility(false); dc.executeAppTransition(); @@ -2709,7 +2709,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (ensureVisibility) { - getDisplay().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + mDisplayContent.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, false /* preserveWindows */, true /* notifyClients */); } } @@ -4653,7 +4653,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Check if the activity is on a sleeping display, and if it can turn it ON. - if (getDisplay().isSleeping()) { + if (mDisplayContent.isSleeping()) { final boolean canTurnScreenOn = !mSetToSleep || canTurnScreenOn() || canShowWhenLocked() || containsDismissKeyguardWindow(); if (!canTurnScreenOn) { @@ -4742,6 +4742,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // returns. Just need to confirm this reasoning makes sense. final boolean deferHidingClient = canEnterPictureInPicture && !isState(STARTED, STOPPING, STOPPED, PAUSED); + if (deferHidingClient && pictureInPictureArgs.isAutoEnterAllowed()) { + // Go ahead and just put the activity in pip if it supports auto-pip. + mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs); + return; + } setDeferHidingClient(deferHidingClient); setVisibility(false); @@ -4932,12 +4937,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } r.setSavedState(null /* savedState */); - final DisplayContent display = r.getDisplay(); - if (display != null) { - display.handleActivitySizeCompatModeIfNeeded(r); - } - - r.getDisplayContent().mUnknownAppVisibilityController.notifyAppResumedFinished(r); + r.mDisplayContent.handleActivitySizeCompatModeIfNeeded(r); + r.mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(r); } /** @@ -5480,10 +5481,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void updateReportedVisibilityLocked() { - if (appToken == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this); final int count = mChildren.size(); @@ -6330,8 +6327,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) { - final IBinder binder = - (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null; + final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; setOrientation(requestedOrientation, binder, this); // Push the new configuration to the requested app in case where it's not pushed, e.g. when @@ -6840,7 +6836,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A onMergedOverrideConfigurationChanged(); } - final DisplayContent display = getDisplay(); + final DisplayContent display = mDisplayContent; if (display == null) { return; } @@ -7265,7 +7261,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final ActivityLifecycleItem lifecycleItem; if (andResume) { lifecycleItem = ResumeActivityItem.obtain( - getDisplay().mDisplayContent.isNextTransitionForward()); + mDisplayContent.isNextTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } @@ -7590,11 +7586,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * otherwise. */ boolean isFocusedActivityOnDisplay() { - final DisplayContent display = getDisplay(); - if (display == null) { - return false; - } - return display.forAllTaskDisplayAreas(taskDisplayArea -> + return mDisplayContent.forAllTaskDisplayAreas(taskDisplayArea -> taskDisplayArea.getFocusedActivity() == this); } @@ -7692,6 +7684,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (hasProcess()) { proto.write(PROC_ID, app.getPid()); } + proto.write(PIP_AUTO_ENTER_ALLOWED, pictureInPictureArgs.isAutoEnterAllowed()); } @Override @@ -7713,9 +7706,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void writeNameToProto(ProtoOutputStream proto, long fieldId) { - if (appToken != null) { - proto.write(fieldId, appToken.getName()); - } + proto.write(fieldId, appToken.getName()); } @Override @@ -7802,42 +7793,40 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Gets the horizontal centered container bounds for size compatibility mode. */ void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation, boolean orientationRequested, boolean canChangeOrientation) { + getFrameByOrientation(outBounds, orientation); if (mIsFloating) { - getFrameByOrientation(outBounds, orientation); outAppBounds.set(outBounds); return; } - if (canChangeOrientation) { - getBoundsByRotation(outBounds, rotation); - if (orientationRequested) { - getFrameByOrientation(outAppBounds, orientation); - } else { - outAppBounds.set(outBounds); - } - } else { - if (orientationRequested) { - getFrameByOrientation(outBounds, orientation); - if ((outBounds.width() > outBounds.height()) != (mWidth > mHeight)) { - // The orientation is mismatched but the display cannot rotate. The bounds - // will fit to the short side of display. - if (orientation == ORIENTATION_LANDSCAPE) { - outBounds.bottom = (int) ((float) mWidth * mWidth / mHeight); - outBounds.right = mWidth; - } else { - outBounds.bottom = mHeight; - outBounds.right = (int) ((float) mHeight * mHeight / mWidth); - } - outBounds.offset( - getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */); - } + getBoundsByRotation(outAppBounds, rotation); + final int dW = outAppBounds.width(); + final int dH = outAppBounds.height(); + final boolean isOrientationMismatched = + ((outBounds.width() > outBounds.height()) != (dW > dH)); + + if (isOrientationMismatched && !canChangeOrientation && orientationRequested) { + // The orientation is mismatched but the display cannot rotate. The bounds will fit + // to the short side of container. + if (orientation == ORIENTATION_LANDSCAPE) { + outBounds.bottom = (int) ((float) dW * dW / dH); + outBounds.right = dW; } else { - outBounds.set(0, 0, mWidth, mHeight); + outBounds.bottom = dH; + outBounds.right = (int) ((float) dH * dH / dW); } - outAppBounds.set(outBounds); - } - - if (rotation != ROTATION_UNDEFINED) { + outBounds.offset(getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */); + } + outAppBounds.set(outBounds); + + if (isOrientationMismatched) { + // One side of container is smaller than the requested size, then it will be scaled + // and the final position will be calculated according to the parent container and + // scale, so the original size shouldn't be shrunk by insets. + final Rect insets = mNonDecorInsets[rotation]; + outBounds.offset(insets.left, insets.top); + outAppBounds.offset(insets.left, insets.top); + } else if (rotation != ROTATION_UNDEFINED) { // Ensure the app bounds won't overlap with insets. Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 34f7f79d7716..9df192b76f9a 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -837,7 +837,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.appToken); - final DisplayContent dc = r.getDisplay().mDisplayContent; + final DisplayContent dc = r.mDisplayContent; clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index be7a6aed7489..19755f29043e 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1710,8 +1710,9 @@ class ActivityStarter { mRootWindowContainer.startPowerModeLaunchIfNeeded( false /* forceSend */, mStartActivity); - mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(), - newTask, mKeepCurTransition, mOptions); + mTargetStack.startActivityLocked(mStartActivity, + topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask, + mKeepCurTransition, mOptions); if (mDoResume) { final ActivityRecord topTaskActivity = mStartActivity.getTask().topRunningActivityLocked(); @@ -1730,7 +1731,7 @@ class ActivityStarter { 0 /* configChanges */, !PRESERVE_WINDOWS); // Go ahead and tell window manager to execute app transition for this activity // since the app transition will not be triggered through the resume channel. - mTargetStack.getDisplay().mDisplayContent.executeAppTransition(); + mTargetStack.mDisplayContent.executeAppTransition(); } else { // If the target stack was not previously focusable (previous top running activity // on that stack was not visible) then any prior calls to move the stack to the @@ -2480,7 +2481,7 @@ class ActivityStarter { // to the front if the caller is not itself in the front. final boolean differentTopTask; if (mTargetStack.getDisplayArea() == mPreferredTaskDisplayArea) { - final Task focusStack = mTargetStack.getDisplay().getFocusedStack(); + final Task focusStack = mTargetStack.mDisplayContent.getFocusedStack(); final ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); final Task topTask = curTop != null ? curTop.getTask() : null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 6a8cbfbb5840..080a438df357 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -125,6 +125,7 @@ import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import android.Manifest; import android.annotation.IntDef; @@ -252,7 +253,6 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.AttributeCache; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.SystemServiceManager; import com.android.server.UiThread; import com.android.server.Watchdog; @@ -2024,7 +2024,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (self.isState( Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) { - self.getDisplay().mDisplayContent.mAppTransition.overridePendingAppTransition( + self.mDisplayContent.mAppTransition.overridePendingAppTransition( packageName, enterAnim, exitAnim, null, null); } @@ -2408,7 +2408,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } else { stack.setWindowingMode(windowingMode); - stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, + stack.mDisplayContent.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS, true /* notifyClients */); } return true; @@ -4058,6 +4058,60 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { && r.getRootTask().isInTask(r) != null; } + /** + * Puts the given activity in picture in picture mode if possible. + * + * @return true if the activity is now in picture-in-picture mode, or false if it could not + * enter picture-in-picture mode. + */ + boolean enterPictureInPictureMode(ActivityRecord r, final PictureInPictureParams params) { + // If the activity is already in picture in picture mode, then just return early + if (isInPictureInPictureMode(r)) { + return true; + } + + // Activity supports picture-in-picture, now check that we can enter PiP at this + // point, if it is + if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode", + false /* beforeStopping */)) { + return false; + } + + final Runnable enterPipRunnable = () -> { + synchronized (mGlobalLock) { + if (r.getParent() == null) { + Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r); + return; + } + // Only update the saved args from the args that are set + r.setPictureInPictureParams(params); + final float aspectRatio = r.pictureInPictureArgs.getAspectRatio(); + final List<RemoteAction> actions = r.pictureInPictureArgs.getActions(); + mRootWindowContainer.moveActivityToPinnedStack( + r, "enterPictureInPictureMode"); + final Task stack = r.getRootTask(); + stack.setPictureInPictureAspectRatio(aspectRatio); + stack.setPictureInPictureActions(actions); + } + }; + + if (isKeyguardLocked()) { + // If the keyguard is showing or occluded, then try and dismiss it before + // entering picture-in-picture (this will prompt the user to authenticate if the + // device is currently locked). + dismissKeyguard(r.appToken, new KeyguardDismissCallback() { + @Override + public void onDismissSucceeded() { + mH.post(enterPipRunnable); + } + }, null /* message */); + } else { + // Enter picture in picture immediately otherwise + enterPipRunnable.run(); + } + return true; + } + @Override public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) { final long origId = Binder.clearCallingIdentity(); @@ -4065,52 +4119,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked( "enterPictureInPictureMode", token, params); - - // If the activity is already in picture in picture mode, then just return early - if (isInPictureInPictureMode(r)) { - return true; - } - - // Activity supports picture-in-picture, now check that we can enter PiP at this - // point, if it is - if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode", - false /* beforeStopping */)) { - return false; - } - - final Runnable enterPipRunnable = () -> { - synchronized (mGlobalLock) { - if (r.getParent() == null) { - Slog.e(TAG, "Skip enterPictureInPictureMode, destroyed " + r); - return; - } - // Only update the saved args from the args that are set - r.setPictureInPictureParams(params); - final float aspectRatio = r.pictureInPictureArgs.getAspectRatio(); - final List<RemoteAction> actions = r.pictureInPictureArgs.getActions(); - mRootWindowContainer.moveActivityToPinnedStack( - r, "enterPictureInPictureMode"); - final Task stack = r.getRootTask(); - stack.setPictureInPictureAspectRatio(aspectRatio); - stack.setPictureInPictureActions(actions); - } - }; - - if (isKeyguardLocked()) { - // If the keyguard is showing or occluded, then try and dismiss it before - // entering picture-in-picture (this will prompt the user to authenticate if the - // device is currently locked). - dismissKeyguard(token, new KeyguardDismissCallback() { - @Override - public void onDismissSucceeded() { - mH.post(enterPipRunnable); - } - }, null /* message */); - } else { - // Enter picture in picture immediately otherwise - enterPipRunnable.run(); - } - return true; + return enterPictureInPictureMode(r, params); } } finally { Binder.restoreCallingIdentity(origId); @@ -4175,7 +4184,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (params.hasSetAspectRatio() && !mWindowManager.isValidPictureInPictureAspectRatio( - r.getDisplay(), params.getAspectRatio())) { + r.mDisplayContent, params.getAspectRatio())) { final float minAspectRatio = mContext.getResources().getFloat( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); final float maxAspectRatio = mContext.getResources().getFloat( @@ -4619,7 +4628,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final long origId = Binder.clearCallingIdentity(); try { - display.mDisplayContent.registerRemoteAnimations(definition); + display.registerRemoteAnimations(definition); } finally { Binder.restoreCallingIdentity(origId); } @@ -4856,6 +4865,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "Requested PIP on an activity that doesn't support it"); } + if (activity.pictureInPictureArgs.isAutoEnterAllowed()) { + enterPictureInPictureMode(activity, activity.pictureInPictureArgs); + return; + } + try { final ClientTransaction transaction = ClientTransaction.obtain( activity.app.getThread(), @@ -5465,8 +5479,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { updateResumedAppTrace(r); mLastResumedActivity = r; - r.getDisplay().setFocusedApp(r, true); - + final boolean changed = r.mDisplayContent.setFocusedApp(r); + if (changed) { + mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, + true /*updateInputWindows*/); + } if (prevTask == null || task != prevTask) { if (prevTask != null) { mTaskChangeNotificationController.notifyTaskFocusChanged(prevTask.mTaskId, false); @@ -6202,12 +6219,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // We might change the visibilities here, so prepare an empty app transition which // might be overridden later if we actually change visibilities. - final DisplayContent displayContent = - mRootWindowContainer.getDisplayContent(displayId); - if (displayContent == null) { + final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId); + if (dc == null) { return; } - final DisplayContent dc = displayContent.mDisplayContent; final boolean wasTransitionSet = dc.mAppTransition.getAppTransition() != TRANSIT_NONE; if (!wasTransitionSet) { diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index 9a397fe07f4e..01c007e381b1 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -18,6 +18,9 @@ package com.android.server.wm; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; + import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -25,6 +28,8 @@ import android.view.SurfaceControl; import android.window.IDisplayAreaOrganizer; import android.window.IDisplayAreaOrganizerController; +import com.android.internal.protolog.common.ProtoLog; + import java.util.HashMap; public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub { @@ -67,9 +72,12 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl @Override public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) { enforceStackPermission("registerOrganizer()"); + final long uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register display organizer=%s uid=%d", + organizer.asBinder(), uid); if (mOrganizersByFeatureIds.get(feature) != null) { throw new IllegalStateException( "Replacing existing organizer currently unsupported"); @@ -96,9 +104,12 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl @Override public void unregisterOrganizer(IDisplayAreaOrganizer organizer) { enforceStackPermission("unregisterTaskOrganizer()"); + final long uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister display organizer=%s uid=%d", + organizer.asBinder(), uid); mOrganizersByFeatureIds.entrySet().removeIf( entry -> entry.getValue().asBinder() == organizer.asBinder()); @@ -113,6 +124,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName()); try { SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl(), "DisplayAreaOrganizerController.onDisplayAreaAppeared"); @@ -123,6 +135,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } void onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea vanished name=%s", da.getName()); try { organizer.onDisplayAreaVanished(da.getDisplayAreaInfo()); } catch (RemoteException e) { @@ -131,6 +144,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } void onDisplayAreaInfoChanged(IDisplayAreaOrganizer organizer, DisplayArea da) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea info changed name=%s", da.getName()); try { organizer.onDisplayAreaInfoChanged(da.getDisplayAreaInfo()); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2f7cc69b01a7..8ccbd1166a44 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -115,13 +115,11 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIG import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS; import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT; import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION; -import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS; @@ -649,8 +647,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> { final ActivityRecord focusedApp = mFocusedApp; - ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b", - w, w.mAttrs.flags, w.canReceiveKeys()); + ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s", + w, w.mAttrs.flags, w.canReceiveKeys(), + w.canReceiveKeysReason(false /* fromUserTouch */)); if (!w.canReceiveKeys()) { return false; @@ -3072,7 +3071,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ WindowState findFocusedWindowIfNeeded(int topFocusedDisplayId) { return (mWmService.mPerDisplayFocusEnabled || topFocusedDisplayId == INVALID_DISPLAY) - ? findFocusedWindow() : null; + ? findFocusedWindow() : null; } WindowState findFocusedWindow() { @@ -3081,7 +3080,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */); if (mTmpWindow == null) { - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows."); + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows, display=%d", + getDisplayId()); return null; } return mTmpWindow; @@ -3116,18 +3116,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) { assignWindowLayers(false /* setLayoutNeeded */); } - } - if (imWindowChanged) { - mWmService.mWindowsChanged = true; - setLayoutNeeded(); - newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId); - } - if (mCurrentFocus != newFocus) { - mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget(); + if (imWindowChanged) { + mWmService.mWindowsChanged = true; + setLayoutNeeded(); + newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId); + } } - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s", + ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s", mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4)); final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; @@ -3185,9 +3182,25 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mode == UPDATE_FOCUS_PLACING_SURFACES) { pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM; } + + // Notify the accessibility manager for the change so it has the windows before the newly + // focused one starts firing events. + // TODO(b/151179149) investigate what info accessibility service needs before input can + // dispatch focus to clients. + if (mWmService.mAccessibilityController != null) { + mWmService.mH.sendMessage(PooledLambda.obtainMessage( + this::updateAccessibilityOnWindowFocusChanged, + mWmService.mAccessibilityController)); + } + + mLastFocus = mCurrentFocus; return true; } + void updateAccessibilityOnWindowFocusChanged(AccessibilityController accessibilityController) { + accessibilityController.onWindowFocusChangedNotLocked(getDisplayId()); + } + private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) { final Task focusedTask = newFocus != null ? newFocus.getTask() : null; final Task unfocusedTask = oldFocus != null ? oldFocus.getTask() : null; @@ -3219,6 +3232,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mFocusedApp == newFocus) { return false; } + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "setFocusedApp %s displayId=%d Callers=%s", + newFocus, getDisplayId(), Debug.getCallers(4)); mFocusedApp = newFocus; getInputMonitor().setFocusedAppLw(newFocus); updateTouchExcludeRegion(); @@ -3473,7 +3488,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return false; } return mWmService.mDisplayWindowSettings.shouldShowImeLocked(this) - || mWmService.mForceDesktopModeOnExternalDisplays; + || forceDesktopMode(); + } + + boolean forceDesktopMode() { + return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate(); } private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) { @@ -4527,7 +4546,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp boolean supportsSystemDecorations() { return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this) || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 - || mWmService.mForceDesktopModeOnExternalDisplays) + || forceDesktopMode()) // VR virtual display will be used to run and render 2D app within a VR experience. && mDisplayId != mWmService.mVr2dDisplayId // Do not show system decorations on untrusted virtual display. @@ -4708,7 +4727,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Traverse all windows top down to assemble the gesture exclusion rects. // For each window, we only take the rects that fall within its touchable region. forAllWindows(w -> { - if (w.cantReceiveTouchInput() || !w.isVisible() + if (!w.canReceiveTouchInput() || !w.isVisible() || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0 || unhandled.isEmpty()) { return; @@ -5225,30 +5244,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && (mAtmService.mRunningVoice == null); } - void setFocusedApp(ActivityRecord r, boolean moveFocusNow) { - final ActivityRecord newFocus; - final IBinder token = r.appToken; - if (token == null) { - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Clearing focused app, displayId=%d", - mDisplayId); - newFocus = null; - } else { - newFocus = mWmService.mRoot.getActivityRecord(token); - if (newFocus == null) { - Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token - + ", displayId=" + mDisplayId); - } - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, - "Set focused app to: %s moveFocusNow=%b displayId=%d", newFocus, - moveFocusNow, mDisplayId); - } - - final boolean changed = setFocusedApp(newFocus); - if (moveFocusNow && changed) { - mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, - true /*updateInputWindows*/); - } - } void ensureActivitiesVisible(ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 1c147c259f07..779f6b2d30cc 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -29,6 +29,7 @@ import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; import static android.view.InsetsState.ITYPE_CAPTION_BAR; +import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_LEFT_GESTURES; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -38,11 +39,6 @@ import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_TOP_GESTURES; import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; -import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; @@ -57,20 +53,18 @@ import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCRE import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_ATTACHED_IN_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; -import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; -import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; @@ -78,27 +72,21 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; -import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; -import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED; @@ -171,7 +159,6 @@ import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; import android.view.View; -import android.view.ViewRootImpl; import android.view.WindowInsets.Side; import android.view.WindowInsets.Side.InsetsSide; import android.view.WindowInsets.Type; @@ -913,10 +900,6 @@ public class DisplayPolicy { (int) attrs.hideTimeoutMilliseconds, AccessibilityManager.FLAG_CONTENT_TEXT); attrs.windowAnimations = com.android.internal.R.style.Animation_Toast; - // Toast can show with below conditions when the screen is locked. - if (canToastShowWhenLocked(callingPid)) { - attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; - } // Toasts can't be clickable attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; break; @@ -947,16 +930,6 @@ public class DisplayPolicy { } /** - * @return {@code true} if the calling activity initiate toast and is visible with - * {@link WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} flag. - */ - boolean canToastShowWhenLocked(int callingPid) { - return mDisplayContent.forAllWindows(w -> { - return callingPid == w.mSession.mPid && w.isVisible() && w.canShowWhenLocked(); - }, true /* traverseTopToBottom */); - } - - /** * Check if a window can be added to the system. * * Currently enforces that two window types are singletons per display: @@ -1440,8 +1413,7 @@ public class DisplayPolicy { DisplayCutout.ParcelableWrapper outDisplayCutout) { final int fl = PolicyControl.getWindowFlags(null, attrs); final int pfl = attrs.privateFlags; - final int requestedSysUiVis = PolicyControl.getSystemUiVisibility(null, attrs); - final int sysUiVis = requestedSysUiVis | getImpliedSysUiFlagsForLayout(attrs); + final int sysUiVis = PolicyControl.getSystemUiVisibility(null, attrs); final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0; final boolean layoutInScreenAndInsetDecor = layoutInScreen @@ -1461,7 +1433,7 @@ public class DisplayPolicy { : mDisplayContent.mDisplayFrames; if (layoutInScreenAndInsetDecor && !screenDecor) { - if ((sysUiVis & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 + if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 || (attrs.getFitInsetsTypes() & Type.navigationBars()) == 0) { outFrame.set(displayFrames.mUnrestricted); } else { @@ -1492,7 +1464,6 @@ public class DisplayPolicy { InsetUtils.insetsBetweenFrames(outFrame, sf, outStableInsets); outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(outFrame) .getDisplayCutout()); - return mForceShowSystemBars; } else { if (layoutInScreen) { outFrame.set(displayFrames.mUnrestricted); @@ -1506,22 +1477,8 @@ public class DisplayPolicy { outContentInsets.setEmpty(); outStableInsets.setEmpty(); outDisplayCutout.set(DisplayCutout.NO_CUTOUT); - return mForceShowSystemBars; } - } - - // TODO(b/118118435): remove after migration - private static int getImpliedSysUiFlagsForLayout(LayoutParams attrs) { - int impliedFlags = 0; - final boolean forceWindowDrawsBarBackgrounds = - (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 - && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT; - if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 - || forceWindowDrawsBarBackgrounds) { - impliedFlags |= SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - impliedFlags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - } - return impliedFlags; + return mForceShowSystemBars; } private final Runnable mClearHideNavigationFlag = new Runnable() { @@ -1557,7 +1514,7 @@ public class DisplayPolicy { if (mInputConsumer == null) { return; } - showNavigationBar(); + showSystemBars(); // Any user activity always causes us to show the // navigation controls, if they had been hidden. // We also clear the low profile and only content @@ -1592,13 +1549,13 @@ public class DisplayPolicy { } } - private void showNavigationBar() { + private void showSystemBars() { final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController() .peekSourceProvider(ITYPE_NAVIGATION_BAR); final InsetsControlTarget target = provider != null ? provider.getControlTarget() : null; if (target != null) { - target.showInsets(Type.navigationBars(), false /* fromIme */); + target.showInsets(Type.systemBars(), false /* fromIme */); } } } @@ -1668,11 +1625,8 @@ public class DisplayPolicy { final int behavior = mLastBehavior; final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController().peekSourceProvider(ITYPE_NAVIGATION_BAR); - boolean navVisible = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL - ? (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 - : provider != null - ? provider.isClientVisible() - : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR); + boolean navVisible = provider != null ? provider.isClientVisible() + : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR); boolean navTranslucent = (sysui & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0; boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0 @@ -2057,80 +2011,6 @@ public class DisplayPolicy { return mNavigationBarController.checkHiddenLw(); } - private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached, - boolean insetDecors, Rect pf, Rect df, Rect cf, Rect vf, - DisplayFrames displayFrames) { - if (!win.isInputMethodTarget() && attached.isInputMethodTarget()) { - // Here's a special case: if the child window is not the 'dock window' - // or input method target, and the window it is attached to is below - // the dock window, then the frames we computed for the window it is - // attached to can not be used because the dock is effectively part - // of the underlying window and the attached window is floating on top - // of the whole thing. So, we ignore the attached window and explicitly - // compute the frames that would be appropriate without the dock. - vf.set(displayFrames.mDock); - cf.set(displayFrames.mDock); - df.set(displayFrames.mDock); - } else { - - // In case we forced the window to draw behind the navigation bar, restrict df to - // DF.Restricted to simulate old compat behavior. - Rect parentDisplayFrame = attached.getDisplayFrame(); - final WindowManager.LayoutParams attachedAttrs = attached.mAttrs; - if ((attachedAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0 - && (attachedAttrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 - && (attachedAttrs.systemUiVisibility - & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) { - parentDisplayFrame = new Rect(parentDisplayFrame); - parentDisplayFrame.intersect(displayFrames.mRestricted); - } - - // The effective display frame of the attached window depends on whether it is taking - // care of insetting its content. If not, we need to use the parent's content frame so - // that the entire window is positioned within that content. Otherwise we can use the - // parent display frame and let the attached window take care of positioning its content - // appropriately. - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - // Set the content frame of the attached window to the parent's decor frame - // (same as content frame when IME isn't present) if specifically requested by - // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag. - // Otherwise, use the overscan frame. - cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0 - ? attached.getContentFrame() : parentDisplayFrame); - } else { - // If the window is resizing, then we want to base the content frame on our attached - // content frame to resize...however, things can be tricky if the attached window is - // NOT in resize mode, in which case its content frame will be larger. - // Ungh. So to deal with that, make sure the content frame we end up using is not - // covering the IM dock. - cf.set(attached.getContentFrame()); - if (attached.isVoiceInteraction()) { - cf.intersectUnchecked(displayFrames.mVoiceContent); - } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) { - cf.intersectUnchecked(displayFrames.mContent); - } - } - df.set(insetDecors ? parentDisplayFrame : cf); - vf.set(attached.getVisibleFrame()); - } - // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be - // positioned relative to its parent or the entire screen. - pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df); - } - - private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) { - if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) { - return; - } - // If app is requesting a stable layout, don't let the content insets go below the stable - // values. - if ((fl & FLAG_FULLSCREEN) != 0) { - r.intersectUnchecked(displayFrames.mStableFullscreen); - } else { - r.intersectUnchecked(displayFrames.mStable); - } - } - private boolean canReceiveInput(WindowState win) { boolean notFocusable = (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; @@ -2165,8 +2045,6 @@ public class DisplayPolicy { final int fl = PolicyControl.getWindowFlags(win, attrs); final int pfl = attrs.privateFlags; final int sim = attrs.softInputMode; - final int requestedSysUiFl = PolicyControl.getSystemUiVisibility(null, attrs); - final int sysUiFl = requestedSysUiFl | getImpliedSysUiFlagsForLayout(attrs); displayFrames = win.getDisplayFrames(displayFrames); final WindowFrames windowFrames = win.getWindowFrames(); @@ -2191,351 +2069,60 @@ public class DisplayPolicy { sf.set(displayFrames.mStable); - if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { - final @InsetsType int typesToFit = attrs.getFitInsetsTypes(); - final @InsetsSide int sidesToFit = attrs.getFitInsetsSides(); - final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit); - getRotatedWindowBounds(displayFrames, win, sTmpRect); - final Rect dfu = sTmpRect; - Insets insets = Insets.of(0, 0, 0, 0); - for (int i = types.size() - 1; i >= 0; i--) { - final InsetsSource source = mDisplayContent.getInsetsPolicy() - .getInsetsForDispatch(win).peekSource(types.valueAt(i)); - if (source == null) { - continue; - } - insets = Insets.max(insets, source.calculateInsets( - dfu, attrs.isFitInsetsIgnoringVisibility())); - } - final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0; - final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0; - final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0; - final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0; - df.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom); - if (attached == null) { - pf.set(df); - vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING - ? displayFrames.mCurrent : displayFrames.mDock); - } else { - pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df); - vf.set(attached.getVisibleFrame()); - } - cf.set(adjust != SOFT_INPUT_ADJUST_RESIZE - ? displayFrames.mDock : displayFrames.mContent); - dcf.set(displayFrames.mSystem); - } else if (type == TYPE_INPUT_METHOD) { - vf.set(displayFrames.mDock); - cf.set(displayFrames.mDock); - df.set(displayFrames.mDock); - pf.set(displayFrames.mDock); - // IM dock windows layout below the nav bar... - pf.bottom = df.bottom = displayFrames.mUnrestricted.bottom; - // ...with content insets above the nav bar - cf.bottom = vf.bottom = displayFrames.mStable.bottom; - if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) { - // The status bar forces the navigation bar while it's visible. Make sure the IME - // avoids the navigation bar in that case. - if (mNavigationBarPosition == NAV_BAR_RIGHT) { - pf.right = df.right = cf.right = vf.right = - displayFrames.mStable.right; - } else if (mNavigationBarPosition == NAV_BAR_LEFT) { - pf.left = df.left = cf.left = vf.left = displayFrames.mStable.left; - } - } - - // In case the navigation bar is on the bottom, we use the frame height instead of the - // regular height for the insets we send to the IME as we need some space to show - // additional buttons in SystemUI when the IME is up. - if (mNavigationBarPosition == NAV_BAR_BOTTOM) { - final int rotation = displayFrames.mRotation; - final int uimode = mService.mPolicy.getUiMode(); - final int navHeightOffset = getNavigationBarFrameHeight(rotation, uimode) - - getNavigationBarHeight(rotation, uimode); - if (navHeightOffset > 0) { - cf.bottom -= navHeightOffset; - sf.bottom -= navHeightOffset; - vf.bottom -= navHeightOffset; - dcf.bottom -= navHeightOffset; - } - } - - // IM dock windows always go to the bottom of the screen. - attrs.gravity = Gravity.BOTTOM; - } else if (type == TYPE_VOICE_INTERACTION) { - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - } else { - cf.set(displayFrames.mContent); - } - if (adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); - } else { - vf.set(cf); + final @InsetsType int typesToFit = attrs.getFitInsetsTypes(); + final @InsetsSide int sidesToFit = attrs.getFitInsetsSides(); + final ArraySet<Integer> types = InsetsState.toInternalType(typesToFit); + getRotatedWindowBounds(displayFrames, win, sTmpRect); + final Rect dfu = sTmpRect; + Insets insets = Insets.of(0, 0, 0, 0); + for (int i = types.size() - 1; i >= 0; i--) { + final InsetsSource source = mDisplayContent.getInsetsPolicy() + .getInsetsForDispatch(win).peekSource(types.valueAt(i)); + if (source == null) { + continue; } - } else if (type == TYPE_WALLPAPER) { - layoutWallpaper(displayFrames, pf, df, cf); - } else if (win == mStatusBar || type == TYPE_NOTIFICATION_SHADE) { - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - cf.set(displayFrames.mStable); - vf.set(displayFrames.mStable); - - if (adjust == SOFT_INPUT_ADJUST_RESIZE) { - // cf.bottom should not be below the stable bottom, or the content might be obscured - // by the navigation bar. - if (cf.bottom > displayFrames.mContent.bottom) { - cf.bottom = displayFrames.mContent.bottom; - } - } else { - if (cf.bottom > displayFrames.mDock.bottom) { - cf.bottom = displayFrames.mDock.bottom; - } - if (vf.bottom > displayFrames.mContent.bottom) { - vf.bottom = displayFrames.mContent.bottom; + insets = Insets.max(insets, source.calculateInsets( + dfu, attrs.isFitInsetsIgnoringVisibility())); + } + final int left = (sidesToFit & Side.LEFT) != 0 ? insets.left : 0; + final int top = (sidesToFit & Side.TOP) != 0 ? insets.top : 0; + final int right = (sidesToFit & Side.RIGHT) != 0 ? insets.right : 0; + final int bottom = (sidesToFit & Side.BOTTOM) != 0 ? insets.bottom : 0; + df.set(dfu.left + left, dfu.top + top, dfu.right - right, dfu.bottom - bottom); + if (attached == null) { + pf.set(df); + if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { + final InsetsSource source = mDisplayContent.getInsetsPolicy() + .getInsetsForDispatch(win).peekSource(ITYPE_IME); + if (source != null) { + pf.inset(source.calculateInsets(pf, false /* ignoreVisibility */)); } } + vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING + ? displayFrames.mCurrent : displayFrames.mDock); } else { - dcf.set(displayFrames.mSystem); - final boolean isAppWindow = - type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW; - final boolean topAtRest = - win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw(); - if (isAppWindow && !topAtRest) { - if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 - && (fl & FLAG_FULLSCREEN) == 0 - && (fl & FLAG_TRANSLUCENT_STATUS) == 0 - && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 - && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) { - // Ensure policy decor includes status bar - dcf.top = displayFrames.mStable.top; - } - if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0 - && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 - && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 - && (pfl & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) == 0) { - // Ensure policy decor includes navigation bar - dcf.bottom = displayFrames.mStable.bottom; - dcf.right = displayFrames.mStable.right; - } - } - - if (layoutInScreen && layoutInsetDecor) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): IN_SCREEN, INSET_DECOR"); - // This is the case for a normal activity window: we want it to cover all of the - // screen space, and it can take care of moving its contents to account for screen - // decorations that intrude into that space. - if (attached != null) { - // If this window is attached to another, our display - // frame is the same as the one we are attached to. - setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, cf, vf, - displayFrames); - } else { - if (type == TYPE_STATUS_BAR_ADDITIONAL || type == TYPE_STATUS_BAR_SUB_PANEL) { - // Status bar panels are the only windows who can go on top of the status - // bar. They are protected by the STATUS_BAR_SERVICE permission, so they - // have the same privileges as the status bar itself. - // - // However, they should still dodge the navigation bar if it exists. - - pf.left = df.left = hasNavBar - ? displayFrames.mDock.left : displayFrames.mUnrestricted.left; - pf.top = df.top = displayFrames.mUnrestricted.top; - pf.right = df.right = hasNavBar - ? displayFrames.mRestricted.right - : displayFrames.mUnrestricted.right; - pf.bottom = df.bottom = hasNavBar - ? displayFrames.mRestricted.bottom - : displayFrames.mUnrestricted.bottom; - - if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out status bar window: " + pf); - } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 - && (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW - || type == TYPE_VOLUME_OVERLAY - || type == TYPE_KEYGUARD_DIALOG)) { - // Asking for layout as if the nav bar is hidden, lets the application - // extend into the unrestricted overscan screen area. We only do this for - // application windows and certain system windows to ensure no window that - // can be above the nav bar can do this. - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - } else { - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); - } - - if ((fl & FLAG_FULLSCREEN) == 0) { - if (win.isVoiceInteraction()) { - cf.set(displayFrames.mVoiceContent); - } else { - // IME Insets are handled on the client for ADJUST_RESIZE in the new - // insets world - if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE - || adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - } else { - cf.set(displayFrames.mContent); - } - } - } else { - // Full screen windows are always given a layout that is as if the status - // bar and other transient decors are gone. This is to avoid bad states when - // moving from a window that is not hiding the status bar to one that is. - cf.set(displayFrames.mRestricted); - } - applyStableConstraints(sysUiFl, fl, cf, displayFrames); - if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE - && adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); - } else { - vf.set(cf); - } - } - } else if (layoutInScreen || (sysUiFl - & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): IN_SCREEN"); - // A window that has requested to fill the entire screen just - // gets everything, period. - if (type == TYPE_STATUS_BAR_ADDITIONAL || type == TYPE_STATUS_BAR_SUB_PANEL) { - cf.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - if (hasNavBar) { - pf.left = df.left = cf.left = displayFrames.mDock.left; - pf.right = df.right = cf.right = displayFrames.mRestricted.right; - pf.bottom = df.bottom = cf.bottom = - displayFrames.mRestricted.bottom; - } - if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out IN_SCREEN status bar window: " + pf); - } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) { - // The navigation bar has Real Ultimate Power. - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - if (DEBUG_LAYOUT) Slog.v(TAG, "Laying out navigation bar window: " + pf); - } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT) - && ((fl & FLAG_FULLSCREEN) != 0)) { - // Fullscreen secure system overlays get what they ask for. Screenshot region - // selection overlay should also expand to full screen. - cf.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - } else if (type == TYPE_BOOT_PROGRESS) { - // Boot progress screen always covers entire display. - cf.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0 - && (type == TYPE_NOTIFICATION_SHADE - || type == TYPE_TOAST - || type == TYPE_DOCK_DIVIDER - || type == TYPE_VOICE_INTERACTION_STARTING - || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) { - // Asking for layout as if the nav bar is hidden, lets the - // application extend into the unrestricted screen area. We - // only do this for application windows (or toasts) to ensure no window that - // can be above the nav bar can do this. - // XXX This assumes that an app asking for this will also - // ask for layout in only content. We can't currently figure out - // what the screen would be if only laying out to hide the nav bar. - cf.set(displayFrames.mUnrestricted); - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - } else if ((sysUiFl & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) { - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); - - // IME Insets are handled on the client for ADJUST_RESIZE in the new insets - // world - if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE - || adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - } else { - cf.set(displayFrames.mContent); - } - } else { - cf.set(displayFrames.mRestricted); - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); - } - - applyStableConstraints(sysUiFl, fl, cf, displayFrames); - - if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE - && adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); - } else { - vf.set(cf); - } - } else if (attached != null) { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): attached to " + attached); - // A child window should be placed inside of the same visible - // frame that its parent had. - setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, cf, vf, - displayFrames); - } else { - if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() - + "): normal window"); - // Otherwise, a normal window must be placed inside the content - // of all screen decorations. - if (type == TYPE_STATUS_BAR_ADDITIONAL) { - // Status bar panels can go on - // top of the status bar. They are protected by the STATUS_BAR_SERVICE - // permission, so they have the same privileges as the status bar itself. - cf.set(displayFrames.mRestricted); - df.set(displayFrames.mRestricted); - pf.set(displayFrames.mRestricted); - } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) { - // These dialogs are stable to interim decor changes. - cf.set(displayFrames.mStable); - df.set(displayFrames.mStable); - pf.set(displayFrames.mStable); - } else { - pf.set(displayFrames.mContent); - if (win.isVoiceInteraction()) { - cf.set(displayFrames.mVoiceContent); - df.set(displayFrames.mVoiceContent); - } else if (adjust != SOFT_INPUT_ADJUST_RESIZE) { - cf.set(displayFrames.mDock); - df.set(displayFrames.mDock); - } else { - cf.set(displayFrames.mContent); - df.set(displayFrames.mContent); - } - if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE - && adjust != SOFT_INPUT_ADJUST_NOTHING) { - vf.set(displayFrames.mCurrent); - } else { - vf.set(cf); - } - } - } + pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df); + vf.set(attached.getVisibleFrame()); } + cf.set(adjust != SOFT_INPUT_ADJUST_RESIZE + ? displayFrames.mDock : displayFrames.mContent); + dcf.set(displayFrames.mSystem); final int cutoutMode = attrs.layoutInDisplayCutoutMode; - final boolean attachedInParent = attached != null && !layoutInScreen; - final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 - || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0 - || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL - && !win.getRequestedInsetsState().getSourceOrDefaultVisibility( - ITYPE_STATUS_BAR)); - final boolean requestedHideNavigation = - (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 - || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL - && !win.getRequestedInsetsState().getSourceOrDefaultVisibility( - ITYPE_NAVIGATION_BAR)); - - // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get - // cropped / shifted to the displayFrame in WindowState. - final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen - && type != TYPE_BASE_APPLICATION; // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in // the cutout safe zone. if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { + final boolean attachedInParent = attached != null && !layoutInScreen; + final InsetsState requestedInsetsState = win.getRequestedInsetsState(); + final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 + || !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR); + final boolean requestedHideNavigation = + !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR); + + // TYPE_BASE_APPLICATION windows are never considered floating here because they don't + // get cropped / shifted to the displayFrame in WindowState. + final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen + && type != TYPE_BASE_APPLICATION; final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect; displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe); if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) { @@ -2631,13 +2218,6 @@ public class DisplayPolicy { } } - private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect cf) { - // The wallpaper has Real Ultimate Power - df.set(displayFrames.mUnrestricted); - pf.set(displayFrames.mUnrestricted); - cf.set(displayFrames.mUnrestricted); - } - private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) { final int rotation = displayFrames.mRotation; final int navBarPosition = navigationBarPosition(displayFrames.mDisplayWidth, @@ -3343,54 +2923,37 @@ public class DisplayPolicy { // Swipe-up for navigation bar is disabled during setup return; } - if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) { - final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider(); - final InsetsControlTarget controlTarget = provider != null - ? provider.getControlTarget() : null; + final InsetsSourceProvider provider = swipeTarget.getControllableInsetProvider(); + final InsetsControlTarget controlTarget = provider != null + ? provider.getControlTarget() : null; - if (controlTarget == null || controlTarget == getNotificationShade()) { - // No transient mode on lockscreen (in notification shade window). - return; - } + if (controlTarget == null || controlTarget == getNotificationShade()) { + // No transient mode on lockscreen (in notification shade window). + return; + } - final InsetsState requestedState = controlTarget.getRequestedInsetsState(); - final @InsetsType int restorePositionTypes = - (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) - ? Type.navigationBars() : 0) - | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) - ? Type.statusBars() : 0); - - if (swipeTarget == mNavigationBar - && (restorePositionTypes & Type.navigationBars()) != 0) { - // Don't show status bar when swiping on already visible navigation bar. - // But restore the position of navigation bar if it has been moved by the control - // target. - controlTarget.showInsets(Type.navigationBars(), false); - return; - } + final InsetsState requestedState = controlTarget.getRequestedInsetsState(); + final @InsetsType int restorePositionTypes = (requestedState.getSourceOrDefaultVisibility( + ITYPE_NAVIGATION_BAR) ? Type.navigationBars() : 0) | ( + requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) ? Type.statusBars() + : 0); - if (controlTarget.canShowTransient()) { - // Show transient bars if they are hidden; restore position if they are visible. - mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE); - controlTarget.showInsets(restorePositionTypes, false); - } else { - // Restore visibilities and positions of system bars. - controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false); - } + if (swipeTarget == mNavigationBar + && (restorePositionTypes & Type.navigationBars()) != 0) { + // Don't show status bar when swiping on already visible navigation bar. + // But restore the position of navigation bar if it has been moved by the control + // target. + controlTarget.showInsets(Type.navigationBars(), false); + return; + } + + if (controlTarget.canShowTransient()) { + // Show transient bars if they are hidden; restore position if they are visible. + mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE); + controlTarget.showInsets(restorePositionTypes, false); } else { - boolean sb = mStatusBarController.checkShowTransientBarLw(); - boolean nb = mNavigationBarController.checkShowTransientBarLw() - && !isNavBarEmpty(mLastSystemUiFlags); - if (sb || nb) { - // Don't show status bar when swiping on already visible navigation bar - if (!nb && swipeTarget == mNavigationBar) { - if (DEBUG) Slog.d(TAG, "Not showing transient bar, wrong swipe target"); - return; - } - if (sb) mStatusBarController.showTransient(); - if (nb) mNavigationBarController.showTransient(); - updateSystemUiVisibilityLw(); - } + // Restore visibilities and positions of system bars. + controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false); } mImmersiveModeConfirmation.confirmCurrentPrompt(); } @@ -3479,11 +3042,10 @@ public class DisplayPolicy { navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow; final int opaqueAppearance = InsetsFlags.getAppearance(visibility) & (APPEARANCE_OPAQUE_STATUS_BARS | APPEARANCE_OPAQUE_NAVIGATION_BARS); - final int appearance = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL - ? updateLightNavigationBarAppearanceLw(win.mAttrs.insetsFlags.appearance, - mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, - mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance - : InsetsFlags.getAppearance(visibility); + final int appearance = updateLightNavigationBarAppearanceLw( + win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState, + mTopFullscreenOpaqueOrDimmingWindowState, + mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance; final int diff = visibility ^ mLastSystemUiFlags; final InsetsPolicy insetsPolicy = getInsetsPolicy(); final boolean isFullscreen = (visibility & (View.SYSTEM_UI_FLAG_FULLSCREEN @@ -3805,9 +3367,8 @@ public class DisplayPolicy { vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis); // update navigation bar - boolean newInsetsMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL; - boolean oldImmersiveMode = newInsetsMode ? mLastImmersiveMode : isImmersiveMode(oldVis); - boolean newImmersiveMode = newInsetsMode ? isImmersiveMode(win) : isImmersiveMode(vis); + boolean oldImmersiveMode = mLastImmersiveMode; + boolean newImmersiveMode = isImmersiveMode(win); if (oldImmersiveMode != newImmersiveMode) { mLastImmersiveMode = newImmersiveMode; final String pkg = win.getOwningPackage(); @@ -3911,15 +3472,6 @@ public class DisplayPolicy { } } - // TODO(b/118118435): Remove this after migration - private boolean isImmersiveMode(int vis) { - final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; - return getNavigationBar() != null - && (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 - && (vis & flags) != 0 - && canHideNavigationBar(); - } - private boolean isImmersiveMode(WindowState win) { if (win == null) { return false; @@ -4110,9 +3662,7 @@ public class DisplayPolicy { mPointerLocationView = new PointerLocationView(mContext); mPointerLocationView.setPrintCoords(false); - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - WindowManager.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.MATCH_PARENT); + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; lp.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 0206787ef226..0f43e49b568b 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -330,10 +330,8 @@ public class DisplayRotation { // It's also not likely to rotate a TV screen. final boolean isTv = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); - final boolean forceDesktopMode = - mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay; mDefaultFixedToUserRotation = - (isCar || isTv || mService.mIsPc || forceDesktopMode) + (isCar || isTv || mService.mIsPc || mDisplayContent.forceDesktopMode()) // For debug purposes the next line turns this feature off with: // $ adb shell setprop config.override_forced_orient true // $ adb shell wm size reset diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index df7c07055e87..c8c83a6e34f0 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -248,7 +248,7 @@ class DisplayWindowSettings { writeSettingsIfNeeded(entry, displayInfo); } - private int getWindowingModeLocked(Entry entry, int displayId) { + private int getWindowingModeLocked(Entry entry, DisplayContent dc) { int windowingMode = entry != null ? entry.mWindowingMode : WindowConfiguration.WINDOWING_MODE_UNDEFINED; // This display used to be in freeform, but we don't support freeform anymore, so fall @@ -259,10 +259,8 @@ class DisplayWindowSettings { } // No record is present so use default windowing mode policy. if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { - final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays - && displayId != Display.DEFAULT_DISPLAY; windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement - && (mService.mIsPc || forceDesktopMode) + && (mService.mIsPc || dc.forceDesktopMode()) ? WindowConfiguration.WINDOWING_MODE_FREEFORM : WindowConfiguration.WINDOWING_MODE_FULLSCREEN; } @@ -272,7 +270,7 @@ class DisplayWindowSettings { int getWindowingModeLocked(DisplayContent dc) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final Entry entry = getEntry(displayInfo); - return getWindowingModeLocked(entry, dc.getDisplayId()); + return getWindowingModeLocked(entry, dc); } void setWindowingModeLocked(DisplayContent dc, int mode) { @@ -382,7 +380,7 @@ class DisplayWindowSettings { final Entry entry = getOrCreateEntry(displayInfo); // Setting windowing mode first, because it may override overscan values later. - dc.setWindowingMode(getWindowingModeLocked(entry, dc.getDisplayId())); + dc.setWindowingMode(getWindowingModeLocked(entry, dc)); dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode, entry.mUserRotation, entry.mFixedToUserRotation); diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 999aab982816..ec62ed44c640 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -114,7 +114,7 @@ class DragDropController { final WindowState callingWin = mService.windowForClientLocked( null, window, false); - if (callingWin == null || callingWin.cantReceiveTouchInput()) { + if (callingWin == null || !callingWin.canReceiveTouchInput()) { Slog.w(TAG_WM, "Bad requesting window " + window); return null; // !!! TODO: throw here? } diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index e166bfc08ad4..0978636ea502 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -24,6 +24,7 @@ import android.view.InputApplicationHandle; import android.view.KeyEvent; import android.view.WindowManager; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.am.ActivityManagerService; import com.android.server.input.InputManagerService; import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow; @@ -252,7 +253,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal // All the calls below need to happen without the WM lock held since they call into AM. mService.mAtmInternal.saveANRState(reason); - if (activity != null && activity.appToken != null) { + if (activity != null) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. final boolean abort = activity.keyDispatchingTimedOut(reason, windowPid); @@ -410,6 +411,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal requestRefreshConfiguration = dispatchPointerCaptureChanged(focusedWindow, false); } mFocusedWindow.set(newFocusedWindow); + mService.mH.sendMessage(PooledLambda.obtainMessage(mService::reportFocusChanged, + oldToken, newToken)); return requestRefreshConfiguration; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index fb511e032c98..4efd687b7bb4 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -370,7 +370,8 @@ final class InputMonitor { * Layer assignment is assumed to be complete by the time this is called. */ public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { - ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s", newWindow); + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d", + newWindow, mDisplayId); if (newWindow != mInputFocus) { if (newWindow != null && newWindow.canReceiveKeys()) { @@ -493,7 +494,7 @@ final class InputMonitor { final int type = w.mAttrs.type; final boolean isVisible = w.isVisibleLw(); if (inputChannel == null || inputWindowHandle == null || w.mRemoved - || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { + || (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { if (w.mWinAnimator.hasSurface()) { // Assign an InputInfo with type to the overlay window which can't receive input // event. This is used to omit Surfaces from occlusion detection. diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index c36a75b01293..69e8c57a489c 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -190,7 +190,7 @@ class KeyguardController { mAodShowing ? 1 : 0, 1 /* keyguardGoingAway */, "keyguardGoingAway"); - mRootWindowContainer.getDefaultDisplay().mDisplayContent + mRootWindowContainer.getDefaultDisplay() .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */, convertTransitFlags(flags), false /* forceOverride */); @@ -314,7 +314,7 @@ class KeyguardController { if (isKeyguardLocked()) { mService.deferWindowLayout(); try { - mRootWindowContainer.getDefaultDisplay().mDisplayContent + mRootWindowContainer.getDefaultDisplay() .prepareAppTransition(resolveOccludeTransit(), false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); @@ -344,8 +344,7 @@ class KeyguardController { // If we are about to unocclude the Keyguard, but we can dismiss it without security, // we immediately dismiss the Keyguard so the activity gets shown without a flicker. - final DisplayContent dc = - mRootWindowContainer.getDefaultDisplay().mDisplayContent; + final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); if (mKeyguardShowing && canDismissKeyguard() && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { dc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, @@ -368,7 +367,7 @@ class KeyguardController { } private int resolveOccludeTransit() { - final DisplayContent dc = mRootWindowContainer.getDefaultDisplay().mDisplayContent; + final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); if (mBeforeUnoccludeTransit != TRANSIT_UNSET && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE // TODO(b/113840485): Handle app transition for individual display. @@ -485,7 +484,7 @@ class KeyguardController { } // TODO(b/123372519): isShowingDream can only works on default display. if (mDisplayId == DEFAULT_DISPLAY) { - mOccluded |= mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent + mOccluded |= mService.mRootWindowContainer.getDefaultDisplay() .getDisplayPolicy().isShowingDreamLw(); } diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index c8d7693c9229..c49690157c08 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -619,7 +619,7 @@ public class LockTaskController { mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities(); final Task rootTask = task.getRootTask(); if (rootTask != null) { - rootTask.getDisplay().mDisplayContent.executeAppTransition(); + rootTask.mDisplayContent.executeAppTransition(); } } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) { mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS index 4be4c896cbff..1077736cebb5 100644 --- a/services/core/java/com/android/server/wm/OWNERS +++ b/services/core/java/com/android/server/wm/OWNERS @@ -11,3 +11,4 @@ erosky@google.com riddlehsu@google.com louischang@google.com winsonc@google.com +tigerhuang@google.com diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 3c64ffb237d6..255b3f147d30 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -1346,11 +1346,8 @@ class RecentTasks { // singleTaskInstance is set on the VirtualDisplay managed by ActivityView // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance final Task rootTask = task.getRootTask(); - if (rootTask != null) { - DisplayContent display = rootTask.getDisplay(); - if (display != null && display.isSingleTaskInstance()) { - return false; - } + if (rootTask != null && rootTask.isSingleTaskInstance()) { + return false; } // If we're in lock task mode, ignore the root task diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 21e30ce0a495..6539e1325ec1 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -129,6 +129,7 @@ import android.os.UserHandle; import android.os.storage.StorageManager; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.IntArray; @@ -162,7 +163,6 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Set; @@ -214,7 +214,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private int mTopFocusedDisplayId = INVALID_DISPLAY; // Map from the PID to the top most app which has a focused window of the process. - final HashMap<Integer, ActivityRecord> mTopFocusedAppByProcess = new HashMap<>(); + final ArrayMap<Integer, ActivityRecord> mTopFocusedAppByProcess = new ArrayMap<>(); // Only a separate transaction until we separate the apply surface changes // transaction from the global transaction. @@ -480,8 +480,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mTopFocusedDisplayId = topFocusedDisplayId; mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId); mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId); - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", - topFocusedDisplayId); + ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId); } return changed; } @@ -2289,10 +2288,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent display = getChildAt(displayNdx); - if (display.shouldSleep()) { - continue; - } - final boolean curResult = result; boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas( (taskDisplayArea, resumed) -> { @@ -2753,7 +2748,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); r.detachFromProcess(); - r.getDisplay().mDisplayContent.prepareAppTransition( + r.mDisplayContent.prepareAppTransition( TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); r.destroyIfPossible("handleAppCrashed"); } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 33935d61ead2..7df2b407557d 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -26,6 +26,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.DebugUtils; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; @@ -429,7 +430,8 @@ class SurfaceAnimator { void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mLeash="); pw.print(mLeash); - pw.print(" mAnimationType=" + mAnimationType); + pw.print(" mAnimationType=" + DebugUtils.valueToString(SurfaceAnimator.class, + "ANIMATION_TYPE_", mAnimationType)); pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : ""); pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation); if (mAnimation != null) { @@ -442,56 +444,56 @@ class SurfaceAnimator { * No animation is specified. * @hide */ - static final int ANIMATION_TYPE_NONE = 0; + public static final int ANIMATION_TYPE_NONE = 0; /** * Animation for an app transition. * @hide */ - static final int ANIMATION_TYPE_APP_TRANSITION = 1; + public static final int ANIMATION_TYPE_APP_TRANSITION = 1; /** * Animation for screen rotation. * @hide */ - static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1; + public static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1; /** * Animation for dimming. * @hide */ - static final int ANIMATION_TYPE_DIMMER = 1 << 2; + public static final int ANIMATION_TYPE_DIMMER = 1 << 2; /** * Animation for recent apps. * @hide */ - static final int ANIMATION_TYPE_RECENTS = 1 << 3; + public static final int ANIMATION_TYPE_RECENTS = 1 << 3; /** * Animation for a {@link WindowState} without animating the activity. * @hide */ - static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4; + public static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4; /** * Animation to control insets. This is actually not an animation, but is used to give the * client a leash over the system window causing insets. * @hide */ - static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5; + public static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5; /** * Animation when a fixed rotation transform is applied to a window token. * @hide */ - static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6; + public static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6; /** * Bitmask to include all animation types. This is NOT an {@link AnimationType} * @hide */ - static final int ANIMATION_TYPE_ALL = -1; + public static final int ANIMATION_TYPE_ALL = -1; /** * The type of the animation. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 19bf451cec05..c35d73282442 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2319,8 +2319,7 @@ class Task extends WindowContainer<WindowContainer> { taskDisplayArea.onStackWindowingModeChanged(this); } - final DisplayContent display = getDisplay(); - if (display == null ) { + if (mDisplayContent == null) { return; } @@ -2336,7 +2335,7 @@ class Task extends WindowContainer<WindowContainer> { final int newRotation = getWindowConfiguration().getRotation(); final boolean rotationChanged = prevRotation != newRotation; if (rotationChanged) { - display.mDisplayContent.rotateBounds( + mDisplayContent.rotateBounds( newParentConfig.windowConfiguration.getBounds(), prevRotation, newRotation, newBounds); hasNewOverrideBounds = true; @@ -2594,15 +2593,12 @@ class Task extends WindowContainer<WindowContainer> { outNonDecorBounds.set(bounds); outStableBounds.set(bounds); final Task rootTask = getRootTask(); - if (rootTask == null || rootTask.getDisplay() == null) { - return; - } - DisplayPolicy policy = rootTask.getDisplay().mDisplayContent.getDisplayPolicy(); - if (policy == null) { + if (rootTask == null || rootTask.mDisplayContent == null) { return; } mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy(); policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets); intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets); @@ -2990,14 +2986,6 @@ class Task extends WindowContainer<WindowContainer> { } } - @Override - DisplayContent getDisplayContent() { - // TODO: Why aren't we just using our own display content vs. parent's??? - final Task stack = getRootTask(); - return stack != null && stack != this - ? stack.getDisplayContent() : super.getDisplayContent(); - } - int getDisplayId() { final DisplayContent dc = getDisplayContent(); return dc != null ? dc.mDisplayId : INVALID_DISPLAY; @@ -5173,14 +5161,9 @@ class Task extends WindowContainer<WindowContainer> { !PRESERVE_WINDOWS); } - DisplayContent getDisplay() { - return getDisplayContent(); - } - /** @return true if the stack can only contain one task */ boolean isSingleTaskInstance() { - final DisplayContent display = getDisplay(); - return display != null && display.isSingleTaskInstance(); + return mDisplayContent != null && mDisplayContent.isSingleTaskInstance(); } final boolean isHomeOrRecentsStack() { @@ -5445,6 +5428,7 @@ class Task extends WindowContainer<WindowContainer> { mAtmService.updateCpuStats(); boolean pauseImmediately = false; + boolean shouldAutoPip = false; if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) { // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous // activity to be paused, while at the same time resuming the new resume activity @@ -5452,26 +5436,39 @@ class Task extends WindowContainer<WindowContainer> { // activities a chance to enter Pip before resuming the next activity. final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState( "shouldResumeWhilePausing", userLeaving); - if (!lastResumedCanPip) { + if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterAllowed()) { + shouldAutoPip = true; + } else if (!lastResumedCanPip) { pauseImmediately = true; + } else { + // The previous activity may still enter PIP even though it did not allow auto-PIP. } } + boolean didAutoPip = false; if (prev.attachedToProcess()) { - if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev); - try { - EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), - prev.shortComponentName, "userLeaving=" + userLeaving); - - mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), - prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, - prev.configChangeFlags, pauseImmediately)); - } catch (Exception e) { - // Ignore exception, if process died other code will cleanup. - Slog.w(TAG, "Exception thrown during pause", e); + if (shouldAutoPip) { + if (DEBUG_PAUSE) { + Slog.d(TAG_PAUSE, "Auto-PIP allowed, entering PIP mode directly: " + prev); + } + didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs); mPausingActivity = null; - mLastPausedActivity = null; - mLastNoHistoryActivity = null; + } else { + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev); + try { + EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev), + prev.shortComponentName, "userLeaving=" + userLeaving); + + mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(), + prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving, + prev.configChangeFlags, pauseImmediately)); + } catch (Exception e) { + // Ignore exception, if process died other code will cleanup. + Slog.w(TAG, "Exception thrown during pause", e); + mPausingActivity = null; + mLastPausedActivity = null; + mLastNoHistoryActivity = null; + } } } else { mPausingActivity = null; @@ -5485,6 +5482,11 @@ class Task extends WindowContainer<WindowContainer> { mStackSupervisor.acquireLaunchWakelock(); } + if (didAutoPip) { + // Already entered PIP mode, no need to keep pausing. + return true; + } + if (mPausingActivity != null) { // Have the window manager pause its key dispatching until the new // activity has started. If we're pausing the activity just because @@ -5620,8 +5622,7 @@ class Task extends WindowContainer<WindowContainer> { * otherwise. */ boolean isFocusedStackOnDisplay() { - final DisplayContent display = getDisplay(); - return display != null && this == display.getFocusedStack(); + return mDisplayContent != null && this == mDisplayContent.getFocusedStack(); } /** @@ -5760,7 +5761,7 @@ class Task extends WindowContainer<WindowContainer> { * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied. */ boolean canShowWithInsecureKeyguard() { - final DisplayContent displayContent = getDisplay(); + final DisplayContent displayContent = mDisplayContent; if (displayContent == null) { throw new IllegalStateException("Stack is not attached to any display, stackId=" + getRootTaskId()); @@ -6348,7 +6349,7 @@ class Task extends WindowContainer<WindowContainer> { return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea()); } - void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, + void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity, boolean newTask, boolean keepCurTransition, ActivityOptions options) { Task rTask = r.getTask(); final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront(); @@ -6399,7 +6400,7 @@ class Task extends WindowContainer<WindowContainer> { // The transition animation and starting window are not needed if {@code allowMoveToFront} // is false, because the activity won't be visible. if ((!isHomeOrRecentsStack() || hasActivity()) && allowMoveToFront) { - final DisplayContent dc = getDisplay().mDisplayContent; + final DisplayContent dc = mDisplayContent; if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: starting " + r); if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) { @@ -6410,7 +6411,7 @@ class Task extends WindowContainer<WindowContainer> { if (newTask) { if (r.mLaunchTaskBehind) { transit = TRANSIT_TASK_OPEN_BEHIND; - } else if (getDisplay().isSingleTaskInstance()) { + } else if (dc.isSingleTaskInstance()) { // If a new task is being launched in a single task display, we don't need // to play normal animation, but need to trigger a callback when an app // transition is actually handled. So ignore already prepared activity, and @@ -6454,7 +6455,7 @@ class Task extends WindowContainer<WindowContainer> { ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); // Go ahead to execute app transition for this activity since the app transition // will not be triggered through the resume channel. - getDisplay().mDisplayContent.executeAppTransition(); + mDisplayContent.executeAppTransition(); } else if (SHOW_APP_STARTING_PREVIEW && doShow) { // Figure out if we are transitioning from another activity that is // "has the same starting icon" as the next one. This allows the @@ -6567,7 +6568,7 @@ class Task extends WindowContainer<WindowContainer> { Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); Task finishedTask = r.getTask(); - getDisplay().mDisplayContent.prepareAppTransition( + mDisplayContent.prepareAppTransition( TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); r.finishIfPossible(reason, false /* oomAdj */); @@ -6655,7 +6656,8 @@ class Task extends WindowContainer<WindowContainer> { // Basic case: for simple app-centric recents, we need to recreate // the task if the affinity has changed. - final String affinity = ActivityRecord.getTaskAffinityWithUid(destAffinity, srec.getUid()); + final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid(), + srec.launchMode); if (srec == null || srec.getTask().affinity == null || !srec.getTask().affinity.equals(affinity)) { return true; @@ -6807,7 +6809,7 @@ class Task extends WindowContainer<WindowContainer> { ActivityOptions.abort(options); } } - getDisplay().mDisplayContent.prepareAppTransition(transit, false, + mDisplayContent.prepareAppTransition(transit, false, 0 /* flags */, forceOverride); } @@ -6855,7 +6857,7 @@ class Task extends WindowContainer<WindowContainer> { // Defer updating the IME target since the new IME target will try to get computed // before updating all closing and opening apps, which can cause the ime target to // get calculated incorrectly. - getDisplay().deferUpdateImeTarget(); + mDisplayContent.deferUpdateImeTarget(); // Shift all activities with this task up to the top // of the stack, keeping them in the same internal order. @@ -6879,7 +6881,7 @@ class Task extends WindowContainer<WindowContainer> { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr); if (noAnimation) { - getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_NONE, false); + mDisplayContent.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */); if (r != null) { mStackSupervisor.mNoAnimActivities.add(r); } @@ -6909,7 +6911,7 @@ class Task extends WindowContainer<WindowContainer> { mAtmService.getTaskChangeNotificationController() .notifyTaskMovedToFront(tr.getTaskInfo()); } finally { - getDisplay().continueUpdateImeTarget(); + mDisplayContent.continueUpdateImeTarget(); } } @@ -6959,7 +6961,7 @@ class Task extends WindowContainer<WindowContainer> { if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + tr.mTaskId); - getDisplay().mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false); + mDisplayContent.prepareAppTransition(TRANSIT_TASK_TO_BACK, false /* alwaysKeepCurrent */); moveToBack("moveTaskToBackLocked", tr); if (inPinnedWindowingMode()) { @@ -6968,7 +6970,7 @@ class Task extends WindowContainer<WindowContainer> { } mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, - getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */, + mDisplayContent.mDisplayId, false /* markFrozenIfConfigChanged */, false /* deferResume */); ActivityRecord topActivity = getDisplayArea().topRunningActivity(); @@ -6976,7 +6978,7 @@ class Task extends WindowContainer<WindowContainer> { if (topStack != null && topStack != this && topActivity.isState(RESUMED)) { // Usually resuming a top activity triggers the next app transition, but nothing's got // resumed in this case, so we need to execute it explicitly. - getDisplay().mDisplayContent.executeAppTransition(); + mDisplayContent.executeAppTransition(); } else { mRootWindowContainer.resumeFocusedStacksTopActivities(); } @@ -7575,17 +7577,21 @@ class Task extends WindowContainer<WindowContainer> { } void executeAppTransition(ActivityOptions options) { - getDisplay().mDisplayContent.executeAppTransition(); + mDisplayContent.executeAppTransition(); ActivityOptions.abort(options); } boolean shouldSleepActivities() { - final DisplayContent display = getDisplay(); + final DisplayContent display = mDisplayContent; // Do not sleep activities in this stack if we're marked as focused and the keyguard // is in the process of going away. if (isFocusedStackOnDisplay() - && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) { + && mStackSupervisor.getKeyguardController().isKeyguardGoingAway() + // Avoid resuming activities on secondary displays since we don't want bubble + // activities to be resumed while bubble is still collapsed. + // TODO(b/113840485): Having keyguard going away state for secondary displays. + && display.isDefaultDisplay) { return false; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index f8465ddc02a2..d9290fb18f08 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS; import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS; @@ -43,16 +44,15 @@ import android.window.ITaskOrganizerController; import android.window.WindowContainerToken; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Set; import java.util.WeakHashMap; import java.util.function.Consumer; @@ -126,6 +126,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void onTaskAppeared(Task task) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId); final boolean visible = task.isVisible(); final RunningTaskInfo taskInfo = task.getTaskInfo(); mDeferTaskOrgCallbacksConsumer.accept(() -> { @@ -147,6 +148,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void onTaskVanished(Task task) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId); final RunningTaskInfo taskInfo = task.getTaskInfo(); mDeferTaskOrgCallbacksConsumer.accept(() -> { try { @@ -163,6 +165,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // by the organizer that don't receive that signal return; } + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId); mDeferTaskOrgCallbacksConsumer.accept(() -> { if (!task.isOrganized()) { // This is safe to ignore if the task is no longer organized @@ -177,6 +180,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void onBackPressedOnTaskRoot(Task task) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task back pressed on root taskId=%d", + task.mTaskId); if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) { // Skip if the task has not yet received taskAppeared(), except for tasks created // by the organizer that don't receive that signal @@ -201,7 +206,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final DeathRecipient mDeathRecipient; private final ArrayList<Task> mOrganizedTasks = new ArrayList<>(); private final int mUid; - private boolean mInterceptBackPressedOnTaskRoot; TaskOrganizerState(ITaskOrganizer organizer, int uid) { final Consumer<Runnable> deferTaskOrgCallbacksConsumer = @@ -219,10 +223,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mUid = uid; } - void setInterceptBackPressedOnTaskRoot(boolean interceptBackPressed) { - mInterceptBackPressedOnTaskRoot = interceptBackPressed; - } - void addTask(Task t) { if (t.mTaskAppearedSent) return; @@ -242,6 +242,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mOrganizer.onTaskVanished(t); } mOrganizedTasks.remove(t); + mInterceptBackPressedOnRootTasks.remove(t.mTaskId); } void dispose() { @@ -273,6 +274,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); + // Set of organized tasks (by taskId) that dispatch back pressed to their organizers + private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet(); private final ActivityTaskManagerService mService; @@ -306,6 +309,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d", + organizer.asBinder(), uid); for (int winMode : SUPPORTED_WINDOWING_MODES) { if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) { mTaskOrganizers.add(organizer); @@ -327,6 +332,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { @Override public void unregisterTaskOrganizer(ITaskOrganizer organizer) { enforceStackPermission("unregisterTaskOrganizer()"); + final int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -334,6 +340,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (state == null) { return; } + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister task organizer=%s uid=%d", + organizer.asBinder(), uid); state.unlinkDeath(); state.dispose(); } @@ -383,6 +391,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return null; } + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d", + displayId, windowingMode); final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode, ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(), true /* createdByOrganizer */); @@ -407,6 +417,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { throw new IllegalArgumentException( "Attempt to delete task not created by organizer task=" + task); } + + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d", + task.getDisplayId(), task.getWindowingMode()); task.removeImmediately(); return true; } @@ -608,15 +621,23 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public void setInterceptBackPressedOnTaskRoot(ITaskOrganizer organizer, + public void setInterceptBackPressedOnTaskRoot(WindowContainerToken token, boolean interceptBackPressed) { enforceStackPermission("setInterceptBackPressedOnTaskRoot()"); final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - if (state != null) { - state.setInterceptBackPressedOnTaskRoot(interceptBackPressed); + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set intercept back pressed on root=%b", + interceptBackPressed); + final Task task = WindowContainer.fromBinder(token.asBinder()).asTask(); + if (task == null) { + Slog.w(TAG, "Could not resolve task from token"); + return; + } + if (interceptBackPressed) { + mInterceptBackPressedOnRootTasks.add(task.mTaskId); + } else { + mInterceptBackPressedOnRootTasks.remove(task.mTaskId); } } } finally { @@ -625,15 +646,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } public boolean handleInterceptBackPressedOnTaskRoot(Task task) { - if (task == null || !task.isOrganized()) { + if (task == null || !task.isOrganized() + || !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) { return false; } final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder()); - if (!state.mInterceptBackPressedOnTaskRoot) { - return false; - } - state.mOrganizer.onBackPressedOnTaskRoot(task); return true; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index e3112efdead2..f39fa1b7fbc8 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -16,7 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -615,8 +617,8 @@ class TaskSnapshotController { return state.calculateInsets(frame, null /* ignoringVisibilityState */, false /* isScreenRound */, false /* alwaysConsumeSystemBars */, null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacyWindowFlags */, - 0 /* legacySystemUiFlags */, null /* typeSideMap */).getInsets( - WindowInsets.Type.systemBars()).toRect(); + 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + null /* typeSideMap */).getInsets(WindowInsets.Type.systemBars()).toRect(); } void dump(PrintWriter pw, String prefix) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c45ccb6e17e3..017747f03ca0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -376,8 +376,11 @@ public class WindowManagerService extends IWindowManager.Stub private static final String BOOT_ANIMATION_SERVICE = "bootanim"; static final int UPDATE_FOCUS_NORMAL = 0; + /** Caller will assign layers */ static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; + /** Caller is performing surface placement */ static final int UPDATE_FOCUS_PLACING_SURFACES = 2; + /** Caller will performSurfacePlacement */ static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; /** Indicates we are removing the focused window when updating the focus. */ static final int UPDATE_FOCUS_REMOVING_FOCUS = 4; @@ -1407,7 +1410,8 @@ public class WindowManagerService extends IWindowManager.Stub if (!displayContent.hasAccess(session.mUid)) { ProtoLog.w(WM_ERROR, "Attempted to add window to a display for which the application " - + "does not have access: %d. Aborting.", displayId); + + "does not have access: %d. Aborting.", + displayContent.getDisplayId()); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } @@ -4730,12 +4734,30 @@ public class WindowManagerService extends IWindowManager.Stub return false; } + void reportFocusChanged(IBinder oldToken, IBinder newToken) { + WindowState lastFocus; + WindowState newFocus; + synchronized (mGlobalLock) { + lastFocus = mInputToWindowMap.get(oldToken); + newFocus = mInputToWindowMap.get(newToken); + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastFocus, newFocus); + } + + if (newFocus != null) { + newFocus.reportFocusChangedSerialized(true); + notifyFocusChanged(); + } + + if (lastFocus != null) { + lastFocus.reportFocusChangedSerialized(false); + } + } + // ------------------------------------------------------------- // Async Handler // ------------------------------------------------------------- final class H extends android.os.Handler { - public static final int REPORT_FOCUS_CHANGE = 2; public static final int WINDOW_FREEZE_TIMEOUT = 11; public static final int PERSIST_ANIMATION_SCALE = 14; @@ -4788,50 +4810,6 @@ public class WindowManagerService extends IWindowManager.Stub Slog.v(TAG_WM, "handleMessage: entry what=" + msg.what); } switch (msg.what) { - case REPORT_FOCUS_CHANGE: { - final DisplayContent displayContent = (DisplayContent) msg.obj; - WindowState lastFocus; - WindowState newFocus; - - AccessibilityController accessibilityController = null; - - synchronized (mGlobalLock) { - if (mAccessibilityController != null) { - accessibilityController = mAccessibilityController; - } - - lastFocus = displayContent.mLastFocus; - newFocus = displayContent.mCurrentFocus; - if (lastFocus == newFocus) { - // Focus is not changing, so nothing to do. - return; - } - displayContent.mLastFocus = newFocus; - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus moving from %s" - + " to %s displayId=%d", lastFocus, newFocus, - displayContent.getDisplayId()); - } - - // First notify the accessibility manager for the change so it has - // the windows before the newly focused one starts firing events. - if (accessibilityController != null) { - accessibilityController.onWindowFocusChangedNotLocked( - displayContent.getDisplayId()); - } - - if (newFocus != null) { - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Gaining focus: %s", newFocus); - newFocus.reportFocusChangedSerialized(true); - notifyFocusChanged(); - } - - if (lastFocus != null) { - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing focus: %s", lastFocus); - lastFocus.reportFocusChangedSerialized(false); - } - break; - } - case WINDOW_FREEZE_TIMEOUT: { final DisplayContent displayContent = (DisplayContent) msg.obj; synchronized (mGlobalLock) { @@ -7994,6 +7972,8 @@ public class WindowManagerService extends IWindowManager.Stub return; } + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s", + touchedWindow); final DisplayContent displayContent = touchedWindow.getDisplayContent(); if (!displayContent.isOnTop()) { displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent, @@ -8022,10 +8002,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - try { - mActivityTaskManager.setFocusedTask(task.mTaskId); - } catch (RemoteException e) { - } + mAtmService.setFocusedTask(task.mTaskId); } /** diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index d25a64890337..c7cad2f76486 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.READ_FRAME_BUFFER; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; @@ -45,6 +46,7 @@ import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; @@ -127,6 +129,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (callback != null) { syncId = startSyncWithOrganizer(callback); } + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", + syncId); mService.deferWindowLayout(); try { ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); @@ -427,6 +431,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub @VisibleForTesting void setSyncReady(int id) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id); mBLASTSyncEngine.setReady(id); } @@ -436,9 +441,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } @Override - public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) { + public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId); final IWindowContainerTransactionCallback callback = - mTransactionCallbacksByPendingSyncId.get(mSyncId); + mTransactionCallbacksByPendingSyncId.get(syncId); SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction(); for (WindowContainer container : windowContainersReady) { @@ -446,14 +452,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } try { - callback.onTransactionReady(mSyncId, mergedTransaction); + callback.onTransactionReady(syncId, mergedTransaction); } catch (RemoteException e) { // If there's an exception when trying to send the mergedTransaction to the client, we // should immediately apply it here so the transactions aren't lost. mergedTransaction.apply(); } - mTransactionCallbacksByPendingSyncId.remove(mSyncId); + mTransactionCallbacksByPendingSyncId.remove(syncId); } @Override diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index c5ebace78261..8bf0820c7dad 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -729,15 +729,16 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return true; } - final DisplayContent display = activity.getDisplay(); - if (display == null) { + if (!activity.isAttached()) { // No need to update if the activity hasn't attach to any display. return false; } boolean canUpdate = false; final DisplayContent topDisplay = - mPreQTopResumedActivity != null ? mPreQTopResumedActivity.getDisplay() : null; + (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isAttached()) + ? mPreQTopResumedActivity.mDisplayContent + : null; // Update the topmost activity if current top activity is // - not on any display OR // - no longer visible OR @@ -748,8 +749,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio canUpdate = true; } + final DisplayContent display = activity.mDisplayContent; // Update the topmost activity if the current top activity wasn't on top of the other one. - if (!canUpdate && topDisplay.mDisplayContent.compareTo(display.mDisplayContent) < 0) { + if (!canUpdate && topDisplay.compareTo(display) < 0) { canUpdate = true; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9ff33b18cb89..84a9c750d2d3 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2884,12 +2884,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return canReceiveKeys(false /* fromUserTouch */); } + public String canReceiveKeysReason(boolean fromUserTouch) { + return "fromTouch= " + fromUserTouch + + " isVisibleOrAdding=" + isVisibleOrAdding() + + " mViewVisibility=" + mViewVisibility + + " mRemoveOnExit=" + mRemoveOnExit + + " flags=" + mAttrs.flags + + " appWindowsAreFocusable=" + + (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch)) + + " canReceiveTouchInput=" + canReceiveTouchInput() + + " displayIsOnTop=" + getDisplayContent().isOnTop() + + " displayIsTrusted=" + getDisplayContent().isTrusted(); + } + public boolean canReceiveKeys(boolean fromUserTouch) { final boolean canReceiveKeys = isVisibleOrAdding() && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) && (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch)) - && !cantReceiveTouchInput(); + && canReceiveTouchInput(); if (!canReceiveKeys) { return false; } @@ -2907,15 +2920,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return showBecauseOfActivity || showBecauseOfWindow; } - /** @return {@code false} if this window desires touch events. */ - boolean cantReceiveTouchInput() { - if (mActivityRecord == null || mActivityRecord.getTask() == null) { - return false; + /** + * @return {@code true} if this window can receive touches based on among other things, + * windowing state and recents animation state. + **/ + boolean canReceiveTouchInput() { + if (mActivityRecord == null || mActivityRecord.getTask() == null) { + return true; } - return mActivityRecord.getTask().getRootTask().shouldIgnoreInput() - || !mActivityRecord.mVisibleRequested - || isRecentsAnimationConsumingAppInput(); + return !mActivityRecord.getTask().getRootTask().shouldIgnoreInput() + && mActivityRecord.mVisibleRequested + && !isRecentsAnimationConsumingAppInput(); } /** diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 4b5f38c40e4f..d84f9d1a7dad 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -97,6 +97,7 @@ cc_defaults { "libnativehelper", "libnativewindow", "libpowermanager", + "libprocessgroup", "libutils", "libui", "libvibratorservice", diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 6a6da0e2b395..7e9e11d209a6 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -30,6 +30,7 @@ #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> #include <jni.h> +#include <processgroup/processgroup.h> using android::base::StringPrintf; using android::base::WriteStringToFile; @@ -74,9 +75,26 @@ static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, job } } +static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal( + JNIEnv *env, jobject clazz, jboolean enable) { + bool success = true; + + if (enable) { + success = SetTaskProfiles(0, {"FreezerEnabled"}, true); + } else { + success = SetTaskProfiles(0, {"FreezerDisabled"}, true); + } + + if (!success) { + jniThrowException(env, "java/lang/RuntimeException", "Unknown error"); + } +} + static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, + {"enableFreezerInternal", "(Z)V", + (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal}, }; int register_android_server_am_CachedAppOptimizer(JNIEnv* env) diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 9751c46f93c9..5dd6cd7b42e9 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -233,7 +233,8 @@ public: virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId); virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices); - virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier); + virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( + const InputDeviceIdentifier& identifier); virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier); virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env, jfloatArray matrixArr); @@ -622,12 +623,12 @@ void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDevice checkAndClearExceptionFromCallback(env, "notifyInputDevicesChanged"); } -sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay( +std::shared_ptr<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier) { ATRACE_CALL(); JNIEnv* env = jniEnv(); - sp<KeyCharacterMap> result; + std::shared_ptr<KeyCharacterMap> result; ScopedLocalRef<jstring> descriptor(env, env->NewStringUTF(identifier.descriptor.c_str())); ScopedLocalRef<jobject> identifierObj(env, env->NewObject(gInputDeviceIdentifierInfo.clazz, gInputDeviceIdentifierInfo.constructor, descriptor.get(), @@ -642,8 +643,12 @@ sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay( ScopedUtfChars filenameChars(env, filenameObj.get()); ScopedUtfChars contentsChars(env, contentsObj.get()); - KeyCharacterMap::loadContents(filenameChars.c_str(), - contentsChars.c_str(), KeyCharacterMap::FORMAT_OVERLAY, &result); + base::Result<std::shared_ptr<KeyCharacterMap>> ret = + KeyCharacterMap::loadContents(filenameChars.c_str(), contentsChars.c_str(), + KeyCharacterMap::FORMAT_OVERLAY); + if (ret) { + result = *ret; + } } checkAndClearExceptionFromCallback(env, "getKeyboardLayoutOverlay"); return result; diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 777cbf410b9b..4e53aa218b71 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -928,7 +928,7 @@ Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSys * GnssPsdsCallback class implements the callback methods for the IGnssPsds * interface. */ -class GnssPsdsCallback : public IGnssPsdsCallback { +struct GnssPsdsCallback : public IGnssPsdsCallback { Return<void> downloadRequestCb() override; Return<void> downloadRequestCb_3_0(int32_t psdsType) override; }; @@ -2743,19 +2743,26 @@ static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env static jboolean android_location_GnssLocationProvider_supports_psds( JNIEnv* /* env */, jobject /* obj */) { - return (gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE; + return (gnssPsdsIface != nullptr || gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE; } static void android_location_GnssLocationProvider_inject_psds_data(JNIEnv* env, jobject /* obj */, - jbyteArray data, jint length) { - if (gnssXtraIface == nullptr) { - ALOGE("%s: IGnssXtra interface not available.", __func__); + jbyteArray data, jint length, + jint psdsType) { + if (gnssPsdsIface == nullptr && gnssXtraIface == nullptr) { + ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__); return; } jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0)); - auto result = gnssXtraIface->injectXtraData(std::string((const char*)bytes, length)); - checkHidlReturn(result, "IGnssXtra injectXtraData() failed."); + if (gnssPsdsIface != nullptr) { + auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType, + std::string((const char*)bytes, length)); + checkHidlReturn(result, "IGnssPsds injectPsdsData() failed."); + } else if (gnssXtraIface != nullptr) { + auto result = gnssXtraIface->injectXtraData(std::string((const char*)bytes, length)); + checkHidlReturn(result, "IGnssXtra injectXtraData() failed."); + } env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT); } @@ -3685,7 +3692,7 @@ static const JNINativeMethod sLocationProviderMethods[] = { reinterpret_cast<void*>(android_location_GnssLocationProvider_inject_location)}, {"native_supports_psds", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_supports_psds)}, - {"native_inject_psds_data", "([BI)V", + {"native_inject_psds_data", "([BII)V", reinterpret_cast<void*>(android_location_GnssLocationProvider_inject_psds_data)}, {"native_agps_set_id", "(ILjava/lang/String;)V", reinterpret_cast<void*>(android_location_GnssLocationProvider_agps_set_id)}, diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp index 0277f16d5e54..46e6f912edb0 100644 --- a/services/core/jni/com_android_server_security_VerityUtils.cpp +++ b/services/core/jni/com_android_server_security_VerityUtils.cpp @@ -33,6 +33,8 @@ #include <android-base/unique_fd.h> +#include <type_traits> + namespace android { namespace { @@ -53,7 +55,7 @@ int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArra fsverity_enable_arg arg = {}; arg.version = 1; - arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; + arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; // hardcoded in measureFsverity below arg.block_size = 4096; arg.salt_size = 0; arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr); @@ -85,9 +87,41 @@ int statxForFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath) { return (out.stx_attributes & STATX_ATTR_VERITY) != 0; } +int measureFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray digest) { + static constexpr auto kDigestSha256 = 32; + using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kDigestSha256>; + + Storage bytes; + fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes); + data->digest_size = kDigestSha256; // the only input/output parameter + + ScopedUtfChars path(env, filePath); + ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC)); + if (rfd.get() < 0) { + return rfd.get(); + } + if (auto err = ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data); err < 0) { + return err; + } + + if (data->digest_algorithm != FS_VERITY_HASH_ALG_SHA256) { + return -EINVAL; + } + + if (digest != nullptr && data->digest_size > 0) { + auto digestSize = env->GetArrayLength(digest); + if (data->digest_size > digestSize) { + return -E2BIG; + } + env->SetByteArrayRegion(digest, 0, data->digest_size, (const jbyte *)data->digest); + } + + return 0; +} const JNINativeMethod sMethods[] = { {"enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity}, {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity}, + {"measureFsverityNative", "(Ljava/lang/String;[B)I", (void *)measureFsverity}, }; } // namespace diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 22e309cdc2b4..80455833a3eb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5782,9 +5782,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - final CallerIdentity identity = getCallerIdentity(); - Preconditions.checkCallAuthorization(isSystemUid(identity) || isRootUid(identity) - || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)); final ActiveAdmin admin; synchronized (getLockObject()) { @@ -9438,8 +9435,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(isDeviceOwner(identity)); return mInjector.binderWithCleanCallingIdentity(() -> { - final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true - /*excludeDying*/); + final List<UserInfo> userInfos = mInjector.getUserManager().getAliveUsers(); final List<UserHandle> userHandles = new ArrayList<>(); for (UserInfo userInfo : userInfos) { UserHandle userHandle = userInfo.getUserHandle(); @@ -10362,7 +10358,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void maybeClearLockTaskPolicyLocked() { mInjector.binderWithCleanCallingIdentity(() -> { - final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true); + final List<UserInfo> userInfos = mUserManager.getAliveUsers(); for (int i = userInfos.size() - 1; i >= 0; i--) { int userId = userInfos.get(i).id; if (canUserUseLockTaskLocked(userId)) { @@ -10849,7 +10845,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * them. */ void updateUserSetupCompleteAndPaired() { - List<UserInfo> users = mUserManager.getUsers(true); + List<UserInfo> users = mUserManager.getAliveUsers(); final int N = users.size(); for (int i = 0; i < N; i++) { int userHandle = users.get(i).id; @@ -12052,14 +12048,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public boolean isSystemOnlyUser(ComponentName admin) { - Objects.requireNonNull(admin, "ComponentName is null"); - final CallerIdentity identity = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(identity)); - return UserManager.isSplitSystemUser() && identity.getUserId() == UserHandle.USER_SYSTEM; - } - - @Override public void reboot(ComponentName admin) { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity identity = getCallerIdentity(admin); @@ -12579,7 +12567,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private boolean areAllUsersAffiliatedWithDeviceLocked() { return mInjector.binderWithCleanCallingIdentity(() -> { - final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true); + final List<UserInfo> userInfos = mUserManager.getAliveUsers(); for (int i = 0; i < userInfos.size(); i++) { int userId = userInfos.get(i).id; if (!isUserAffiliatedWithDeviceLocked(userId)) { @@ -13048,7 +13036,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } else { // Caller is the device owner: Look for profile owners that it can bind to. - final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true); + final List<UserInfo> userInfos = mUserManager.getAliveUsers(); for (int i = 0; i < userInfos.size(); i++) { final int userId = userInfos.get(i).id; if (userId != callingUserId && canUserBindToDeviceOwnerLocked(userId)) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 3cdd482ffa37..7649af4ee911 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -169,7 +169,7 @@ class Owners { // First, try to read from the legacy file. final File legacy = getLegacyConfigFile(); - final List<UserInfo> users = mUserManager.getUsers(true); + final List<UserInfo> users = mUserManager.getAliveUsers(); if (readLegacyOwnerFileLocked(legacy)) { if (DEBUG) { diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index 7534c7c40a3d..e978ed4000e0 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -51,9 +51,9 @@ cc_defaults { static_libs: [ "libbase", "libext2_uuid", - "libdataloader_aidl-cpp", - "libincremental_aidl-cpp", - "libincremental_manager_aidl-cpp", + "libdataloader_aidl-unstable-cpp", + "libincremental_aidl-unstable-cpp", + "libincremental_manager_aidl-unstable-cpp", "libprotobuf-cpp-lite", "service.incremental.proto", "libutils", diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index 41945a276fde..87ae4d719d11 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -237,6 +237,13 @@ binder::Status BinderIncrementalService::unlink(int32_t storageId, const std::st return ok(); } +binder::Status BinderIncrementalService::isFileFullyLoaded(int32_t storageId, + const std::string& path, + int32_t* _aidl_return) { + *_aidl_return = mImpl.isFileFullyLoaded(storageId, path); + return ok(); +} + binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId, float* _aidl_return) { *_aidl_return = mImpl.getLoadingProgress(storageId); diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 8b40350468ce..8478142b2d95 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -66,6 +66,8 @@ public: int32_t destStorageId, const std::string& destPath, int32_t* _aidl_return) final; binder::Status unlink(int32_t storageId, const std::string& path, int32_t* _aidl_return) final; + binder::Status isFileFullyLoaded(int32_t storageId, const std::string& path, + int32_t* _aidl_return) final; binder::Status getLoadingProgress(int32_t storageId, float* _aidl_return) final; binder::Status getMetadataByPath(int32_t storageId, const std::string& path, std::vector<uint8_t>* _aidl_return) final; diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index ba6ae9262aea..447ee552a335 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -1603,7 +1603,8 @@ void IncrementalService::extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId); if (!writeFd.ok()) { - LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd; + LOG(ERROR) << "Failed to open write fd for: " << targetLibPath + << " errno: " << writeFd.get(); return; } @@ -1673,6 +1674,37 @@ bool IncrementalService::waitForNativeBinariesExtraction(StorageId storage) { return mRunning; } +int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const { + std::unique_lock l(mLock); + const auto ifs = getIfsLocked(storage); + if (!ifs) { + LOG(ERROR) << "isFileFullyLoaded failed, invalid storageId: " << storage; + return -EINVAL; + } + const auto storageInfo = ifs->storages.find(storage); + if (storageInfo == ifs->storages.end()) { + LOG(ERROR) << "isFileFullyLoaded failed, no storage: " << storage; + return -EINVAL; + } + l.unlock(); + return isFileFullyLoadedFromPath(*ifs, path); +} + +int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs, + std::string_view filePath) const { + const auto [filledBlocks, totalBlocks] = mIncFs->countFilledBlocks(ifs.control, filePath); + if (filledBlocks < 0) { + LOG(ERROR) << "isFileFullyLoadedFromPath failed to get filled blocks count for: " + << filePath << " errno: " << filledBlocks; + return filledBlocks; + } + if (totalBlocks < filledBlocks) { + LOG(ERROR) << "isFileFullyLoadedFromPath failed to get total num of blocks"; + return -EINVAL; + } + return totalBlocks - filledBlocks; +} + float IncrementalService::getLoadingProgress(StorageId storage) const { std::unique_lock l(mLock); const auto ifs = getIfsLocked(storage); @@ -1706,8 +1738,8 @@ float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs, } if (totalBlocks == 0) { - LOG(ERROR) << "getLoadingProgress failed to get total num of blocks"; - return -EINVAL; + // No file in the storage or files are empty; regarded as fully loaded + return 1; } return (float)filledBlocks / (float)totalBlocks; } diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index cd6bfedb8a9e..267458d8769c 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -132,6 +132,7 @@ public: std::string_view newPath); int unlink(StorageId storage, std::string_view path); + int isFileFullyLoaded(StorageId storage, const std::string& path) const; float getLoadingProgress(StorageId storage) const; RawMetadata getMetadata(StorageId storage, std::string_view path) const; @@ -339,6 +340,7 @@ private: int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode); binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); + int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const; float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const; void registerAppOpsCallback(const std::string& packageName); diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index 1ed46c49c5e1..f6d89c53be32 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -195,8 +195,8 @@ public: ErrorCode unlink(const Control& control, std::string_view path) const final { return incfs::unlink(control, path); } - base::unique_fd openForSpecialOps(const Control& control, FileId id) const final { - return base::unique_fd{incfs::openForSpecialOps(control, id).release()}; + incfs::UniqueFd openForSpecialOps(const Control& control, FileId id) const final { + return incfs::openForSpecialOps(control, id); } ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final { return incfs::writeBlocks({blocks.data(), size_t(blocks.size())}); diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index 82a170470fee..6376d86543f8 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -74,6 +74,7 @@ public: using Control = incfs::Control; using FileId = incfs::FileId; using ErrorCode = incfs::ErrorCode; + using UniqueFd = incfs::UniqueFd; using WaitResult = incfs::WaitResult; using ExistingMountCallback = @@ -96,7 +97,7 @@ public: virtual ErrorCode link(const Control& control, std::string_view from, std::string_view to) const = 0; virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0; - virtual base::unique_fd openForSpecialOps(const Control& control, FileId id) const = 0; + virtual UniqueFd openForSpecialOps(const Control& control, FileId id) const = 0; virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0; virtual WaitResult waitForPendingReads( const Control& control, std::chrono::milliseconds timeout, diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 44cef49a716c..a290a1791481 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -289,7 +289,7 @@ public: ErrorCode(const Control& control, std::string_view from, std::string_view to)); MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path)); - MOCK_CONST_METHOD2(openForSpecialOps, base::unique_fd(const Control& control, FileId id)); + MOCK_CONST_METHOD2(openForSpecialOps, UniqueFd(const Control& control, FileId id)); MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks)); MOCK_CONST_METHOD3(waitForPendingReads, WaitResult(const Control& control, std::chrono::milliseconds timeout, @@ -304,6 +304,10 @@ public: ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(1, 2))); } + void countFilledBlocksFullyLoaded() { + ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(10000, 10000))); + } + void countFilledBlocksFails() { ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(-1, -1))); } @@ -1069,7 +1073,54 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) { ASSERT_EQ(res, 0); } -TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithNoFile) { +TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithNoFile) { + mIncFs->countFilledBlocksFails(); + mFs->hasNoFile(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}); + ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); +} + +TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithFailedRanges) { + mIncFs->countFilledBlocksFails(); + mFs->hasFiles(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}); + EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); + ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); +} + +TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccessWithEmptyRanges) { + mIncFs->countFilledBlocksEmpty(); + mFs->hasFiles(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}); + EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); + ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); +} + +TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) { + mIncFs->countFilledBlocksFullyLoaded(); + mFs->hasFiles(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}); + EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); + ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); +} + +TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) { mIncFs->countFilledBlocksSuccess(); mFs->hasNoFile(); @@ -1077,7 +1128,7 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithNoFile) { int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), IncrementalService::CreateOptions::CreateNew, {}, {}, {}); - ASSERT_EQ(-EINVAL, mIncrementalService->getLoadingProgress(storageId)); + ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId)); } TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) { @@ -1092,7 +1143,7 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) { ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId)); } -TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithEmptyRanges) { +TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) { mIncFs->countFilledBlocksEmpty(); mFs->hasFiles(); @@ -1101,7 +1152,7 @@ TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithEmptyRanges) { IncrementalService::CreateOptions::CreateNew, {}, {}, {}); EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3); - ASSERT_EQ(-EINVAL, mIncrementalService->getLoadingProgress(storageId)); + ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId)); } TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) { diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 6cd083e11754..f4c6918c0e96 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -67,7 +67,6 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManagerInternal; import android.app.AlarmManager; import android.app.IActivityManager; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.hardware.Sensor; @@ -87,6 +86,7 @@ import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.SystemClock; +import android.provider.DeviceConfig; import androidx.test.runner.AndroidJUnit4; @@ -99,6 +99,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoSession; @@ -106,6 +107,8 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; +import java.util.concurrent.Executor; + /** * Tests for {@link com.android.server.DeviceIdleController}. */ @@ -124,8 +127,6 @@ public class DeviceIdleControllerTest { @Mock private ConnectivityManager mConnectivityManager; @Mock - private ContentResolver mContentResolver; - @Mock private IActivityManager mIActivityManager; @Mock private LocationManager mLocationManager; @@ -294,6 +295,7 @@ public class DeviceIdleControllerTest { mMockingSession = mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) + .spyStatic(DeviceConfig.class) .spyStatic(LocalServices.class) .startMocking(); spyOn(getContext()); @@ -310,6 +312,14 @@ public class DeviceIdleControllerTest { .thenReturn(mock(PowerSaveState.class)); doReturn(mock(NetworkPolicyManagerInternal.class)) .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class)); + doAnswer((Answer<Void>) invocationOnMock -> null) + .when(() -> DeviceConfig.addOnPropertiesChangedListener( + anyString(), any(Executor.class), + any(DeviceConfig.OnPropertiesChangedListener.class))); + doAnswer((Answer<DeviceConfig.Properties>) invocationOnMock + -> mock(DeviceConfig.Properties.class)) + .when(() -> DeviceConfig.getProperties( + anyString(), ArgumentMatchers.<String>any())); when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock); doNothing().when(mWakeLock).acquire(); doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any()); @@ -319,7 +329,6 @@ public class DeviceIdleControllerTest { mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper()); mAnyMotionDetector = new AnyMotionDetectorForTest(); mInjector = new InjectorForTest(getContext()); - doNothing().when(mContentResolver).registerContentObserver(any(), anyBoolean(), any()); mDeviceIdleController = new DeviceIdleController(getContext(), mInjector); spyOn(mDeviceIdleController); @@ -330,8 +339,7 @@ public class DeviceIdleControllerTest { mDeviceIdleController.setLightEnabledForTest(true); // Get the same Constants object that mDeviceIdleController got. - mConstants = mInjector.getConstants(mDeviceIdleController, - mInjector.getHandler(mDeviceIdleController), mContentResolver); + mConstants = mInjector.getConstants(mDeviceIdleController); } @After diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 77232dc4644c..0789d680a80c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -780,7 +780,7 @@ public class AlarmManagerServiceTest { setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, alarmPi); assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed()); - final SparseArray<ArrayList<AlarmManagerService.Alarm>> restrictedAlarms = + final SparseArray<ArrayList<Alarm>> restrictedAlarms = mService.mPendingBackgroundAlarms; assertNull(restrictedAlarms.get(TEST_CALLING_UID)); @@ -993,7 +993,7 @@ public class AlarmManagerServiceTest { @Test public void alarmCountOnRemoveFromPendingWhileIdle() { - mService.mPendingIdleUntil = mock(AlarmManagerService.Alarm.class); + mService.mPendingIdleUntil = mock(Alarm.class); final int numAlarms = 15; final PendingIntent[] pis = new PendingIntent[numAlarms]; for (int i = 0; i < numAlarms; i++) { diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java new file mode 100644 index 000000000000..9e43b4ab9b5a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.alarm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.platform.test.annotations.Presubmit; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class AlarmStoreTest { + private static final int TEST_CALLING_UID = 12345; + private static final String TEST_CALLING_PACKAGE = "android.alarm.unit.test"; + + private AlarmStore mAlarmStore; + + @Before + public void setUp() { + mAlarmStore = new BatchingAlarmStore(null); + } + + private static Alarm createAlarm(long whenElapsed, long windowLength, PendingIntent mockPi, + AlarmManager.AlarmClockInfo alarmClock) { + return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, mockPi, + alarmClock); + } + + private static Alarm createWakeupAlarm(long whenElapsed, long windowLength, + PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) { + return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, mockPi, + alarmClock); + } + + private static Alarm createAlarm(int type, long whenElapsed, long windowLength, + PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) { + return new Alarm(type, whenElapsed, whenElapsed, windowLength, whenElapsed + windowLength, + 0, mockPi, null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE); + } + + private void addAlarmsToStore(Alarm... alarms) { + for (Alarm a : alarms) { + mAlarmStore.add(a); + } + } + + @Test + public void add() { + final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null); + mAlarmStore.add(a1); + assertEquals(1, mAlarmStore.size()); + + final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null); + mAlarmStore.add(a2); + assertEquals(2, mAlarmStore.size()); + + ArrayList<Alarm> alarmsAdded = mAlarmStore.asList(); + assertEquals(2, alarmsAdded.size()); + assertTrue(alarmsAdded.contains(a1) && alarmsAdded.contains(a2)); + } + + @Test + public void remove() { + final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null); + final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null); + final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null); + addAlarmsToStore(a1, a2, a5); + + ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.whenElapsed < 4)); + assertEquals(2, removed.size()); + assertEquals(1, mAlarmStore.size()); + assertTrue(removed.contains(a1) && removed.contains(a2)); + + final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null); + addAlarmsToStore(a8, a2, a1); + + removed = mAlarmStore.remove(unused -> false); + assertEquals(0, removed.size()); + assertEquals(4, mAlarmStore.size()); + + removed = mAlarmStore.remove(unused -> true); + assertEquals(4, removed.size()); + assertEquals(0, mAlarmStore.size()); + } + + @Test + public void removePendingAlarms() { + final Alarm a1_11 = createAlarm(1, 10, mock(PendingIntent.class), null); + final Alarm a2_5 = createAlarm(2, 3, mock(PendingIntent.class), null); + final Alarm a6_9 = createAlarm(6, 3, mock(PendingIntent.class), null); + addAlarmsToStore(a2_5, a6_9, a1_11); + + final ArrayList<Alarm> pendingAt0 = mAlarmStore.removePendingAlarms(0); + assertEquals(0, pendingAt0.size()); + assertEquals(3, mAlarmStore.size()); + + final ArrayList<Alarm> pendingAt3 = mAlarmStore.removePendingAlarms(3); + assertEquals(2, pendingAt3.size()); + assertTrue(pendingAt3.contains(a1_11) && pendingAt3.contains(a2_5)); + assertEquals(1, mAlarmStore.size()); + + addAlarmsToStore(a2_5, a1_11); + final ArrayList<Alarm> pendingAt7 = mAlarmStore.removePendingAlarms(7); + assertEquals(3, pendingAt7.size()); + assertTrue(pendingAt7.contains(a1_11) && pendingAt7.contains(a2_5) && pendingAt7.contains( + a6_9)); + assertEquals(0, mAlarmStore.size()); + } + + @Test + public void getNextWakeupDeliveryTime() { + final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null); + final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null); + final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null); + final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null); + addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10); + + // The wakeup alarms are [6] and [3, 8], hence 6 is the latest time till when we can + // defer delivering any wakeup alarm. + assertTrue(mAlarmStore.getNextWakeupDeliveryTime() <= 6); + + mAlarmStore.remove(a -> a.wakeup); + assertEquals(2, mAlarmStore.size()); + // No wakeup alarms left. + assertEquals(0, mAlarmStore.getNextWakeupDeliveryTime()); + + mAlarmStore.remove(unused -> true); + assertEquals(0, mAlarmStore.getNextWakeupDeliveryTime()); + } + + @Test + public void getNextDeliveryTime() { + final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null); + final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null); + final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null); + final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null); + addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10); + + assertTrue(mAlarmStore.getNextDeliveryTime() <= 5); + + mAlarmStore.remove(unused -> true); + assertEquals(0, mAlarmStore.getNextWakeupDeliveryTime()); + } + + @Test + public void recalculateAlarmDeliveries() { + final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null); + final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null); + final Alarm a10 = createAlarm(10, 0, mock(PendingIntent.class), null); + addAlarmsToStore(a8, a10, a5); + + assertEquals(5, mAlarmStore.getNextDeliveryTime()); + + mAlarmStore.recalculateAlarmDeliveries(a -> { + a.whenElapsed += 3; + a.maxWhenElapsed = a.whenElapsed; + return true; + }); + assertEquals(8, mAlarmStore.getNextDeliveryTime()); + + mAlarmStore.recalculateAlarmDeliveries(a -> { + a.whenElapsed = 20 - a.whenElapsed; + a.maxWhenElapsed = a.whenElapsed; + return true; + }); + assertEquals(7, mAlarmStore.getNextDeliveryTime()); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java index 12e953a4d4e8..6465739f6822 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java @@ -27,7 +27,6 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ObjectUtils; -import com.android.server.alarm.AlarmManagerService.Alarm; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index fdcadf3e3088..80ad0a838bbb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -33,6 +33,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; import static com.android.server.location.LocationPermissions.PERMISSION_FINE; import static com.android.server.location.LocationUtils.createLocation; +import static com.android.server.location.listeners.RemoteListenerRegistration.IN_PROCESS_EXECUTOR; import static com.google.common.truth.Truth.assertThat; @@ -85,7 +86,6 @@ import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.server.FgThread; import com.android.server.LocalServices; -import com.android.server.location.listeners.ListenerRegistration; import com.android.server.location.util.FakeUserInfoHelper; import com.android.server.location.util.TestInjector; @@ -274,60 +274,55 @@ public class LocationProviderManagerTest { @Test public void testGetLastLocation_Fine() { - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); } @Test public void testGetLastLocation_Coarse() { - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - Location coarse = mManager.getLastLocation(request, IDENTITY, PERMISSION_COARSE); + Location coarse = mManager.getLastLocation(IDENTITY, PERMISSION_COARSE, false); assertThat(coarse).isNotEqualTo(loc); assertThat(coarse).isNearby(loc, 5000); } @Test public void testGetLastLocation_Bypass() { - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); - LocationRequest bypassRequest = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, - false).setLocationSettingsIgnored(true); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isNull(); Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); mProvider.setProviderAllowed(false); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); mProvider.setProviderAllowed(true); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); - assertThat(mManager.getLastLocation(bypassRequest, IDENTITY, PERMISSION_FINE)).isEqualTo( + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, true)).isEqualTo( loc); } @@ -337,13 +332,12 @@ public class LocationProviderManagerTest { mockProvider.setAllowed(true); mManager.setMockProvider(mockProvider); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); Location loc = createLocation(NAME, mRandom); mockProvider.setProviderLocation(loc); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); mManager.setMockProvider(null); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isNull(); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isNull(); } @Test @@ -351,13 +345,12 @@ public class LocationProviderManagerTest { Location loc1 = createLocation(NAME, mRandom); mManager.injectLastLocation(loc1, CURRENT_USER); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(NAME, 0, 0, false); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc1); Location loc2 = createLocation(NAME, mRandom); mManager.injectLastLocation(loc2, CURRENT_USER); - assertThat(mManager.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc1); + assertThat(mManager.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc1); } @Test @@ -381,9 +374,7 @@ public class LocationProviderManagerTest { Location loc = createLocation(NAME, mRandom); mProvider.setProviderLocation(loc); - LocationRequest request = LocationRequest.createFromDeprecatedProvider(PASSIVE_PROVIDER, 0, - 0, false); - assertThat(mPassive.getLastLocation(request, IDENTITY, PERMISSION_FINE)).isEqualTo(loc); + assertThat(mPassive.getLastLocation(IDENTITY, PERMISSION_FINE, false)).isEqualTo(loc); } @Test @@ -484,7 +475,7 @@ public class LocationProviderManagerTest { PERMISSION_FINE, listener); CountDownLatch blocker = new CountDownLatch(1); - ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> { + IN_PROCESS_EXECUTOR.execute(() -> { try { blocker.await(); } catch (InterruptedException e) { @@ -622,7 +613,7 @@ public class LocationProviderManagerTest { PERMISSION_FINE, listener); CountDownLatch blocker = new CountDownLatch(1); - ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> { + IN_PROCESS_EXECUTOR.execute(() -> { try { blocker.await(); } catch (InterruptedException e) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java index 1ef12555a83a..69a9f4415fe7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java @@ -16,6 +16,8 @@ package com.android.server.location.listeners; +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -27,8 +29,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.testng.Assert.assertThrows; -import android.location.util.identity.CallerIdentity; -import android.os.Process; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -324,10 +324,8 @@ public class ListenerMultiplexerTest { boolean mActive = true; protected TestListenerRegistration(Integer integer, - Consumer<TestListenerRegistration> consumer, - boolean outOfProcess) { - super(integer, CallerIdentity.forTest(Process.myUid(), - Process.myPid() + (outOfProcess ? 1 : 0), "test", "test"), consumer); + Consumer<TestListenerRegistration> consumer) { + super(DIRECT_EXECUTOR, integer, consumer); } } @@ -345,7 +343,7 @@ public class ListenerMultiplexerTest { } public void addListener(Integer request, Consumer<TestListenerRegistration> consumer) { - addRegistration(consumer, new TestListenerRegistration(request, consumer, true)); + addRegistration(consumer, new TestListenerRegistration(request, consumer)); } public void removeListener(Consumer<TestListenerRegistration> consumer) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java index 42ba842f8434..f896d75ecb3b 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -27,6 +28,7 @@ import static org.mockito.Mockito.when; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; +import android.os.RemoteCallback; import android.os.RemoteException; import android.view.Display; import android.view.accessibility.IWindowMagnificationConnection; @@ -69,13 +71,28 @@ class MockWindowMagnificationConnection { if (displayId != TEST_DISPLAY) { throw new IllegalArgumentException("only support default display :" + displayId); } - computeMirrorWindowFrame(invocation.getArgument(1), invocation.getArgument(2)); - + computeMirrorWindowFrame(invocation.getArgument(2), invocation.getArgument(3)); + final RemoteCallback callback = invocation.getArgument(4); + if (callback != null) { + callback.sendResult(null); + } mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY, mMirrorWindowFrame); return null; }).when(mConnection).enableWindowMagnification(anyInt(), - anyFloat(), anyFloat(), anyFloat()); + anyFloat(), anyFloat(), anyFloat(), nullable(RemoteCallback.class)); + + doAnswer((invocation) -> { + final int displayId = invocation.getArgument(0); + if (displayId != TEST_DISPLAY) { + throw new IllegalArgumentException("only support default display :" + displayId); + } + final RemoteCallback callback = invocation.getArgument(1); + if (callback != null) { + callback.sendResult(null); + } + return null; + }).when(mConnection).disableWindowMagnification(anyInt(), nullable(RemoteCallback.class)); } private void computeMirrorWindowFrame(float centerX, float centerY) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java index 36b304b4884c..9ef65d9cce09 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java @@ -19,6 +19,7 @@ package com.android.server.accessibility.magnification; import static org.mockito.Mockito.verify; +import android.os.RemoteCallback; import android.os.RemoteException; import android.provider.Settings; import android.view.Display; @@ -43,18 +44,22 @@ public class WindowMagnificationConnectionWrapperTest { private IWindowMagnificationConnection mConnection; @Mock private IWindowMagnificationConnectionCallback mCallback; + @Mock + private RemoteCallback.OnResultListener mOnResultListener; + private RemoteCallback mRemoteCallback; private WindowMagnificationConnectionWrapper mConnectionWrapper; @Before public void setUp() { MockitoAnnotations.initMocks(this); mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection); + mRemoteCallback = new RemoteCallback(mOnResultListener); } @Test public void enableWindowMagnification() throws RemoteException { - mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f); - verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f); + mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback); + verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f, mRemoteCallback); } @Test @@ -65,8 +70,8 @@ public class WindowMagnificationConnectionWrapperTest { @Test public void disableWindowMagnification() throws RemoteException { - mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY); - verify(mConnection).disableWindowMagnification(TEST_DISPLAY); + mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY, mRemoteCallback); + verify(mConnection).disableWindowMagnification(TEST_DISPLAY, mRemoteCallback); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index bec9f26672f4..a10e0ba5020c 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -81,7 +81,7 @@ public class WindowMagnificationGestureHandlerTest { @After public void tearDown() { - mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, true); + mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, true); } @Test @@ -225,7 +225,7 @@ public class WindowMagnificationGestureHandlerTest { } break; case STATE_SHOW_MAGNIFIER: { - mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, false); + mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false); } break; case STATE_TWO_FINGERS_DOWN: { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index 70e6a340816a..dcb1262ad2de 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -68,8 +68,12 @@ public class WindowMagnificationManagerTest { private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT; private MockWindowMagnificationConnection mMockConnection; - @Mock private Context mContext; - @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal; + @Mock + private Context mContext; + @Mock + private StatusBarManagerInternal mMockStatusBarManagerInternal; + @Mock + private Runnable mEndCallback; private MockContentResolver mResolver; private WindowMagnificationManager mWindowMagnificationManager; @@ -84,7 +88,7 @@ public class WindowMagnificationManagerTest { when(mContext.getContentResolver()).thenReturn(mResolver); doAnswer((InvocationOnMock invocation) -> { - final boolean connect = (Boolean) invocation.getArguments()[0]; + final boolean connect = (Boolean) invocation.getArguments()[0]; mWindowMagnificationManager.setConnection( connect ? mMockConnection.getConnection() : null); return null; @@ -158,32 +162,53 @@ public class WindowMagnificationManagerTest { } @Test - public void enable_TestDisplay_enableWindowMagnification() throws RemoteException { + public void enable_hasConnection_enableWindowMagnification() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, 200f, 300f); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f); verify(mMockConnection.getConnection()).enableWindowMagnification(TEST_DISPLAY, 2f, - 200f, 300f); + 200f, 300f, null); } @Test - public void disable_testDisplay_disableWindowMagnification() throws RemoteException { + public void enableWithCallback_hasConnection_enableWindowMagnification() + throws RemoteException { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f, + mEndCallback); + + verify(mEndCallback).run(); + } + + @Test + public void disable_hasConnectionAndEnabled_disableWindowMagnification() + throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); - mWindowMagnificationManager.disableWindowMagnifier(TEST_DISPLAY, false); + mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false); - verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY); + verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null); } @Test - public void isWindowMagnifierEnabled_returnExpectedValue() { + public void disableWithCallback_hasConnectionAndEnabled_disableWindowMagnification() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); + mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false, mEndCallback); + + verify(mEndCallback).run(); + } + + @Test + public void isWindowMagnifierEnabled_hasConnectionAndEnabled_returnExpectedValue() { + mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN); assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } @@ -198,7 +223,7 @@ public class WindowMagnificationManagerTest { @Test public void persistScale_setValue_expectedValueInProvider() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.0f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN); mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f); mWindowMagnificationManager.persistScale(TEST_DISPLAY); @@ -211,7 +236,7 @@ public class WindowMagnificationManagerTest { @Test public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.0f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN); mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f); @@ -221,7 +246,7 @@ public class WindowMagnificationManagerTest { @Test public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.5f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN); mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f); @@ -230,16 +255,17 @@ public class WindowMagnificationManagerTest { } @Test - public void moveWindowMagnifier() throws RemoteException { + public void moveWindowMagnifier_enabled_invokeConnectionMethod() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN); - mWindowMagnificationManager.moveWindowMagnifier(TEST_DISPLAY, 200, 300); + mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300); verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300); } @Test - public void showMagnificationButton() throws RemoteException { + public void showMagnificationButton_hasConnection_invokeConnectionMethod() + throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationManager.showMagnificationButton(TEST_DISPLAY, @@ -252,9 +278,9 @@ public class WindowMagnificationManagerTest { } @Test - public void pointersInWindow_returnCorrectValue() throws RemoteException { + public void pointersInWindow_magnifierEnabled_returnCorrectValue() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3.0f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN); mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY, new Rect(0, 0, 500, 500)); PointF[] pointersLocation = new PointF[2]; @@ -268,7 +294,7 @@ public class WindowMagnificationManagerTest { @Test public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); mMockConnection.getDeathRecipient().binderDied(); @@ -280,11 +306,11 @@ public class WindowMagnificationManagerTest { requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection() throws RemoteException { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN); assertTrue(mWindowMagnificationManager.requestConnection(false)); - verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY); + verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null); verify(mMockStatusBarManagerInternal).requestWindowMagnificationConnection(false); } @@ -306,21 +332,24 @@ public class WindowMagnificationManagerTest { @Test public void requestConnection_registerAndUnregisterBroadcastReceiver() { assertTrue(mWindowMagnificationManager.requestConnection(true)); - verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); + verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); assertTrue(mWindowMagnificationManager.requestConnection(false)); verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); } @Test - public void onReceiveScreenOff_removeMagnificationButtonAndDisableWindowMagnification() + public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification() throws RemoteException { mWindowMagnificationManager.requestConnection(true); + mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN); + mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF)); verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY); - verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY); + verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY, null); + assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY)); } private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index b306ff091267..431cc27a6635 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -16,7 +16,6 @@ package com.android.server.devicepolicy; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; @@ -236,7 +235,7 @@ public class MockSystemServices { } mUserInfos.add(uh); when(userManager.getUsers()).thenReturn(mUserInfos); - when(userManager.getUsers(anyBoolean())).thenReturn(mUserInfos); + when(userManager.getAliveUsers()).thenReturn(mUserInfos); when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(true); when(userManager.getProfileParent(anyInt())).thenAnswer( invocation -> { diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index b1f3871274ac..73dda0736d2f 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -19,6 +19,7 @@ package com.android.server.display; import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -28,18 +29,23 @@ import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; import android.content.Context; +import android.graphics.Insets; +import android.graphics.Rect; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; +import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.InputManagerInternal; import android.os.Handler; import android.os.IBinder; import android.view.Display; +import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; @@ -282,6 +288,68 @@ public class DisplayManagerServiceTest { } /** + * Tests that there should be a display change notification to WindowManager to update its own + * internal state for things like display cutout when nonOverrideDisplayInfo is changed. + */ + @Test + public void testShouldNotifyChangeWhenNonOverrideDisplayInfoChanged() throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + // Add the FakeDisplayDevice + FakeDisplayDevice displayDevice = new FakeDisplayDevice(); + DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); + displayDeviceInfo.width = 100; + displayDeviceInfo.height = 200; + final Rect zeroRect = new Rect(); + displayDeviceInfo.displayCutout = new DisplayCutout( + Insets.of(0, 10, 0, 0), + zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect); + displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY; + displayDevice.setDisplayDeviceInfo(displayDeviceInfo); + displayManager.handleDisplayDeviceAdded(displayDevice); + + // Find the display id of the added FakeDisplayDevice + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + final int[] displayIds = bs.getDisplayIds(); + assertTrue(displayIds.length > 0); + int displayId = Display.INVALID_DISPLAY; + for (int i = 0; i < displayIds.length; i++) { + DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayIds[i]); + if (displayDeviceInfo.equals(ddi)) { + displayId = displayIds[i]; + break; + } + } + assertFalse(displayId == Display.INVALID_DISPLAY); + + // Setup override DisplayInfo + DisplayInfo overrideInfo = bs.getDisplayInfo(displayId); + displayManager.setDisplayInfoOverrideFromWindowManagerInternal(displayId, overrideInfo); + + Handler handler = displayManager.getDisplayHandler(); + handler.runWithScissors(() -> { + }, 0 /* now */); + + // register display listener callback + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId); + bs.registerCallback(callback); + + // Simulate DisplayDevice change + DisplayDeviceInfo displayDeviceInfo2 = new DisplayDeviceInfo(); + displayDeviceInfo2.copyFrom(displayDeviceInfo); + displayDeviceInfo2.displayCutout = null; + displayDevice.setDisplayDeviceInfo(displayDeviceInfo2); + displayManager.handleDisplayDeviceChanged(displayDevice); + + handler.runWithScissors(() -> { + }, 0 /* now */); + assertTrue(callback.mCalled); + } + + /** * Tests that we get a Runtime exception when we cannot initialize the default display. */ @Test @@ -512,4 +580,42 @@ public class DisplayManagerServiceTest { // flush the handler handler.runWithScissors(() -> {}, 0 /* now */); } + + private class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub { + int mDisplayId; + boolean mCalled = false; + + FakeDisplayManagerCallback(int displayId) { + mDisplayId = displayId; + } + + @Override + public void onDisplayEvent(int displayId, int event) { + if (displayId == mDisplayId && event == DisplayManagerGlobal.EVENT_DISPLAY_CHANGED) { + mCalled = true; + } + } + } + + private class FakeDisplayDevice extends DisplayDevice { + private DisplayDeviceInfo mDisplayDeviceInfo; + + FakeDisplayDevice() { + super(null, null, ""); + } + + public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) { + mDisplayDeviceInfo = displayDeviceInfo; + } + + @Override + public boolean hasStableUniqueId() { + return false; + } + + @Override + public DisplayDeviceInfo getDisplayDeviceInfoLocked() { + return mDisplayDeviceInfo; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index ef2365e6da3e..0c35797b38ea 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -15,6 +15,7 @@ */ package com.android.server.hdmi; +import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -693,6 +694,84 @@ public class HdmiCecLocalDevicePlaybackTest { } @Test + public void sendVolumeKeyEvent_toTv_activeSource() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setSystemAudioActivated(false); + mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress, + "HdmiCecLocalDevicePlaybackTest"); + + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, ADDR_TV); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue(); + assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released); + } + + @Test + public void sendVolumeKeyEvent_toAudio_activeSource() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setSystemAudioActivated(true); + mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress, + "HdmiCecLocalDevicePlaybackTest"); + + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue(); + assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released); + } + + @Test + public void sendVolumeKeyEvent_toTv_inactiveSource() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setSystemAudioActivated(false); + mHdmiControlService.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest"); + + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, ADDR_TV); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released); + } + + @Test + public void sendVolumeKeyEvent_toAudio_inactiveSource() { + mHdmiControlService.setHdmiCecVolumeControlEnabled(true); + mHdmiControlService.setSystemAudioActivated(true); + mHdmiControlService.setActiveSource(ADDR_TV, 0x0000, "HdmiCecLocalDevicePlaybackTest"); + + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true); + mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false); + + HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed( + mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP); + HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased( + mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM); + mTestLooper.dispatchAll(); + + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released); + } + + @Test public void handleSetStreamPath_broadcastsActiveSource() { HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, mPlaybackPhysicalAddress); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 775e88750cef..31cf59ee7bde 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -63,7 +63,7 @@ import java.util.ArrayList; @RunWith(JUnit4.class) public class HdmiControlServiceTest { - private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDevice { + private class HdmiCecLocalDeviceMyDevice extends HdmiCecLocalDeviceSource { private boolean mCanGoToStandby; private boolean mIsStandby; @@ -405,6 +405,83 @@ public class HdmiControlServiceTest { assertThat(callback2.mVolumeControlEnabled).isTrue(); } + @Test + public void setActiveSource_localDevice_playback() { + int physicalAddress = 0x1000; + mNativeWrapper.setPhysicalAddress(physicalAddress); + + mHdmiControlService.setActiveSource(mMyPlaybackDevice.mAddress, physicalAddress, + "HdmiControlServiceTest"); + + assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo( + mMyPlaybackDevice.mAddress); + assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo( + physicalAddress); + assertThat(mMyPlaybackDevice.mIsActiveSource).isTrue(); + assertThat(mMyAudioSystemDevice.mIsActiveSource).isFalse(); + } + + @Test + public void setActiveSource_localDevice_audio() { + int physicalAddress = 0x1000; + mNativeWrapper.setPhysicalAddress(physicalAddress); + + mHdmiControlService.setActiveSource(mMyAudioSystemDevice.mAddress, physicalAddress, + "HdmiControlServiceTest"); + + assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo( + mMyAudioSystemDevice.mAddress); + assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo( + physicalAddress); + assertThat(mMyPlaybackDevice.mIsActiveSource).isFalse(); + assertThat(mMyAudioSystemDevice.mIsActiveSource).isTrue(); + } + + @Test + public void setActiveSource_remoteDevice() { + int physicalAddress = 0x1000; + mNativeWrapper.setPhysicalAddress(physicalAddress); + + mHdmiControlService.setActiveSource(Constants.ADDR_TV, 0x0000, "HdmiControlServiceTest"); + + assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo( + Constants.ADDR_TV); + assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(0x000); + assertThat(mMyPlaybackDevice.mIsActiveSource).isFalse(); + assertThat(mMyAudioSystemDevice.mIsActiveSource).isFalse(); + } + + @Test + public void setActiveSource_nonCecDevice() { + int physicalAddress = 0x1000; + mNativeWrapper.setPhysicalAddress(physicalAddress); + + mHdmiControlService.setActiveSource(Constants.ADDR_INVALID, 0x1234, + "HdmiControlServiceTest"); + + assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo( + Constants.ADDR_INVALID); + assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(0x1234); + assertThat(mMyPlaybackDevice.mIsActiveSource).isFalse(); + assertThat(mMyAudioSystemDevice.mIsActiveSource).isFalse(); + } + + @Test + public void setActiveSource_unknown() { + int physicalAddress = 0x1000; + mNativeWrapper.setPhysicalAddress(physicalAddress); + + mHdmiControlService.setActiveSource(Constants.ADDR_INVALID, + Constants.INVALID_PHYSICAL_ADDRESS, "HdmiControlServiceTest"); + + assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo( + Constants.ADDR_INVALID); + assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo( + Constants.INVALID_PHYSICAL_ADDRESS); + assertThat(mMyPlaybackDevice.mIsActiveSource).isFalse(); + assertThat(mMyAudioSystemDevice.mIsActiveSource).isFalse(); + } + private static class VolumeControlFeatureCallback extends IHdmiCecVolumeControlFeatureListener.Stub { boolean mCallbackReceived = false; diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index b2512d3ed8ca..eec7d125d219 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -379,7 +379,7 @@ public class AppsFilterTest { } @Test - public void testForceQueryable_DoesntFilter() throws Exception { + public void testForceQueryable_SystemDoesntFilter() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, mMockExecutor); @@ -387,7 +387,8 @@ public class AppsFilterTest { appsFilter.onSystemReady(); PackageSetting target = simulateAddPackage(appsFilter, - pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID); + pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID, + setting -> setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"), DUMMY_CALLING_APPID); @@ -395,6 +396,24 @@ public class AppsFilterTest { SYSTEM_USER)); } + + @Test + public void testForceQueryable_NonSystemFilters() throws Exception { + final AppsFilter appsFilter = + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + PackageSetting target = simulateAddPackage(appsFilter, + pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_APPID); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package"), DUMMY_CALLING_APPID); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + @Test public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception { final AppsFilter appsFilter = diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java new file mode 100644 index 000000000000..7049efa1cc2f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.timezonedetector; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; + +/** + * A test support class used for tracking a piece of state in test objects like fakes and mocks. + * State can optionally be initialized using {@link #init}, which sets the value to an initial + * value, but is not treated as a change. Calls to {@link #set} are tracked and can be checked for + * during tests. The change-tracking can be cleared by calling {@link #commitLatest}, which puts the + * object into an unchanged state and sets the initial value to the latest value passed to + * {@link #set}. + */ +public class TestState<T> { + private T mInitialValue; + private final ArrayList<T> mValues = new ArrayList<>(5); + + /** Sets the initial value for the state. */ + public void init(T value) { + mValues.clear(); + mInitialValue = value; + } + + /** Sets the latest value for the state. */ + public void set(T value) { + mValues.add(value); + } + + /** Returns {@code true} if {@link #set} has been called. */ + public boolean hasBeenSet() { + return mValues.size() > 0; + } + + /** Fails if {@link #set} has been called. */ + public void assertHasNotBeenSet() { + assertFalse(hasBeenSet()); + } + + /** Fails if {@link #set} has not been called. */ + public void assertHasBeenSet() { + assertTrue(hasBeenSet()); + } + + /** + * Clears tracked changes and re-initializes using the latest set value as the initial value. + */ + public void commitLatest() { + if (hasBeenSet()) { + mInitialValue = mValues.get(mValues.size() - 1); + mValues.clear(); + } + } + + /** Asserts the latest value passed to {@link #set} equals {@code expected}. */ + public void assertLatestEquals(T expected) { + assertEquals(expected, getLatest()); + } + + /** Asserts the number of times {@link #set} has been called. */ + public void assertChangeCount(int expectedCount) { + assertEquals(expectedCount, mValues.size()); + } + + /** + * Returns the latest value passed to {@link #set}. If {@link #set} hasn't been called then the + * initial value is returned. + */ + public T getLatest() { + if (hasBeenSet()) { + return mValues.get(mValues.size() - 1); + } + return mInitialValue; + } +} diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index 2bee5e5e7295..1cdf19319209 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -54,7 +54,6 @@ import org.junit.Test; import java.io.StringWriter; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -994,55 +993,6 @@ public class TimeZoneDetectorStrategyImplTest { } } - /** Some piece of state that tests want to track. */ - private static class TestState<T> { - private T mInitialValue; - private LinkedList<T> mValues = new LinkedList<>(); - - void init(T value) { - mValues.clear(); - mInitialValue = value; - } - - void set(T value) { - mValues.addFirst(value); - } - - boolean hasBeenSet() { - return mValues.size() > 0; - } - - void assertHasNotBeenSet() { - assertFalse(hasBeenSet()); - } - - void assertHasBeenSet() { - assertTrue(hasBeenSet()); - } - - void commitLatest() { - if (hasBeenSet()) { - mInitialValue = mValues.getLast(); - mValues.clear(); - } - } - - void assertLatestEquals(T expected) { - assertEquals(expected, getLatest()); - } - - void assertChangeCount(int expectedCount) { - assertEquals(expectedCount, mValues.size()); - } - - public T getLatest() { - if (hasBeenSet()) { - return mValues.getFirst(); - } - return mInitialValue; - } - } - /** * A "fluent" class allows reuse of code in tests: initialization, simulation and verification * logic. diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 7c7b1a296673..2aeab209162a 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -139,10 +139,13 @@ public class AppStandbyControllerTests { private AppStandbyController mController; private CountDownLatch mStateChangedLatch = new CountDownLatch(1); + private String mLatchPkgName = null; private AppIdleStateChangeListener mListener = new AppIdleStateChangeListener() { @Override public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket, int reason) { + // Ignore events not related to mLatchPkgName, if set. + if (mLatchPkgName != null && !mLatchPkgName.equals(packageName)) return; mStateChangedLatch.countDown(); } }; @@ -374,6 +377,7 @@ public class AppStandbyControllerTests { mInjector.mElapsedRealtime, false)); controller.addListener(mListener); + mLatchPkgName = null; return controller; } @@ -1377,7 +1381,7 @@ public class AppStandbyControllerTests { @Test public void testUnexemptedSyncScheduled() throws Exception { - mStateChangedLatch = new CountDownLatch(1); + rearmLatch(PACKAGE_1); mController.addListener(mListener); assertEquals("Test package did not start in the Never bucket", STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1)); @@ -1389,7 +1393,7 @@ public class AppStandbyControllerTests { setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); - mStateChangedLatch = new CountDownLatch(1); + rearmLatch(PACKAGE_1); mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false); mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS); assertEquals("Unexempted sync scheduled should not elevate a non Never bucket", @@ -1400,7 +1404,7 @@ public class AppStandbyControllerTests { public void testExemptedSyncScheduled() throws Exception { setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); mInjector.mDeviceIdleMode = true; - mStateChangedLatch = new CountDownLatch(1); + rearmLatch(PACKAGE_1); mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true); mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS); assertEquals("Exempted sync scheduled in doze should set bucket to working set", @@ -1408,7 +1412,7 @@ public class AppStandbyControllerTests { setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); mInjector.mDeviceIdleMode = false; - mStateChangedLatch = new CountDownLatch(1); + rearmLatch(PACKAGE_1); mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true); mStateChangedLatch.await(1000, TimeUnit.MILLISECONDS); assertEquals("Exempted sync scheduled while not in doze should set bucket to active", @@ -1558,10 +1562,19 @@ public class AppStandbyControllerTests { } private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception { - mStateChangedLatch = new CountDownLatch(1); + rearmLatch(pkg); mController.setAppStandbyBucket(pkg, user, bucket, reason); mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); assertEquals("Failed to set package bucket", bucket, getStandbyBucket(mController, PACKAGE_1)); } + + private void rearmLatch(String pkgName) { + mLatchPkgName = pkgName; + mStateChangedLatch = new CountDownLatch(1); + } + + private void rearmLatch() { + rearmLatch(null); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 99433a6603c9..d7e431f3bb51 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -978,6 +978,7 @@ public class ManagedServicesTest extends UiServiceTestCase { assertFalse(services.isSameUser(service, 0)); assertTrue(services.isSameUser(service, 10)); + assertTrue(services.isSameUser(service, UserHandle.USER_ALL)); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index ab4dc476ff20..5796e848ff6e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -103,7 +103,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { when(mUm.getUserInfo(eq(user.id))).thenReturn(user); } when(mUm.getUsers()).thenReturn(users); - when(mUm.getUsers(anyBoolean())).thenReturn(users); + when(mUm.getAliveUsers()).thenReturn(users); IntArray profileIds = new IntArray(); profileIds.add(0); profileIds.add(11); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9319bea497fb..86447192a441 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5058,7 +5058,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 10, 10, r.getKey(), actionIndex, action, notificationVisibility, generatedByAssistant); verify(mAssistants).notifyAssistantActionClicked( - eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant)); + eq(r.getSbn()), eq(action), eq(generatedByAssistant)); assertEquals(1, mNotificationRecordLogger.numCalls()); assertEquals( @@ -5082,7 +5082,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 10, 10, r.getKey(), actionIndex, action, notificationVisibility, generatedByAssistant); verify(mAssistants).notifyAssistantActionClicked( - eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant)); + eq(r.getSbn()), eq(action), eq(generatedByAssistant)); assertEquals(1, mNotificationRecordLogger.numCalls()); assertEquals( @@ -6948,4 +6948,63 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS + 1, mService.getNotificationRecordCount()); } + + @Test + public void testIsVisibleToListener_notEnabled() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getUserId()).thenReturn(10); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class); + info.userid = 10; + when(info.isSameUser(anyInt())).thenReturn(true); + when(assistant.isSameUser(anyInt())).thenReturn(true); + when(info.enabledAndUserMatches(info.userid)).thenReturn(false); + when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant); + + assertFalse(mService.isVisibleToListener(sbn, info)); + } + + @Test + public void testIsVisibleToListener_noAssistant() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getUserId()).thenReturn(10); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + info.userid = 10; + when(info.isSameUser(anyInt())).thenReturn(true); + when(info.enabledAndUserMatches(info.userid)).thenReturn(true); + when(mAssistants.checkServiceTokenLocked(any())).thenReturn(null); + + assertTrue(mService.isVisibleToListener(sbn, info)); + } + + @Test + public void testIsVisibleToListener_assistant_differentUser() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getUserId()).thenReturn(10); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class); + info.userid = 0; + when(info.isSameUser(anyInt())).thenReturn(true); + when(assistant.isSameUser(anyInt())).thenReturn(true); + when(info.enabledAndUserMatches(info.userid)).thenReturn(true); + when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant); + + assertFalse(mService.isVisibleToListener(sbn, info)); + } + + @Test + public void testIsVisibleToListener_assistant_sameUser() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getUserId()).thenReturn(10); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class); + info.userid = 10; + when(info.isSameUser(anyInt())).thenReturn(true); + when(assistant.isSameUser(anyInt())).thenReturn(true); + when(info.enabledAndUserMatches(info.userid)).thenReturn(true); + when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant); + + assertTrue(mService.isVisibleToListener(sbn, info)); + } + } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java index 1eb45d587d33..cf07183a007d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -67,7 +67,7 @@ public class ActivityDisplayTests extends WindowTestsBase { final TaskDisplayArea taskDisplayAreas = mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea(); final Task stack = - new StackBuilder(mRootWindowContainer).setOnTop(!ON_TOP).build(); + new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); final Task prevFocusedStack = taskDisplayAreas.getFocusedStack(); stack.moveToFront("moveStackToFront"); @@ -90,7 +90,7 @@ public class ActivityDisplayTests extends WindowTestsBase { final Task pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea() .createStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task pinnedTask = new TaskBuilder(mAtm.mStackSupervisor) - .setStack(pinnedStack).build(); + .setParentTask(pinnedStack).build(); new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE) .setTask(pinnedTask).build(); pinnedStack.moveToFront("movePinnedStackToFront"); @@ -144,7 +144,7 @@ public class ActivityDisplayTests extends WindowTestsBase { doReturn(false).when(display).shouldDestroyContentOnRemove(); // Put home stack on the display. - final Task homeStack = new StackBuilder(mRootWindowContainer) + final Task homeStack = new TaskBuilder(mSupervisor) .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build(); // Put a finishing standard activity which will be reparented. @@ -163,7 +163,7 @@ public class ActivityDisplayTests extends WindowTestsBase { final Task fullscreenStack = display.getDefaultTaskDisplayArea().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task fullscreenTask = new TaskBuilder(mAtm.mStackSupervisor) - .setStack(fullscreenStack).build(); + .setParentTask(fullscreenStack).build(); new ActivityBuilder(mAtm).setTask(fullscreenTask).build(); return fullscreenStack; } @@ -175,12 +175,11 @@ public class ActivityDisplayTests extends WindowTestsBase { public void testTopRunningActivity() { final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final Task stack = new StackBuilder(mRootWindowContainer).build(); + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getTopNonFinishingActivity(); // Create empty stack on top. - final Task emptyStack = - new StackBuilder(mRootWindowContainer).setCreateActivity(false).build(); + final Task emptyStack = new TaskBuilder(mSupervisor).build(); // Make sure the top running activity is not affected when keyguard is not locked. assertTopRunningActivity(activity, display); @@ -322,10 +321,10 @@ public class ActivityDisplayTests extends WindowTestsBase { ACTIVITY_TYPE_STANDARD, ON_TOP); final Task stack4 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task task1 = new TaskBuilder(mAtm.mStackSupervisor).setStack(stack1).build(); - final Task task2 = new TaskBuilder(mAtm.mStackSupervisor).setStack(stack2).build(); - final Task task3 = new TaskBuilder(mAtm.mStackSupervisor).setStack(stack3).build(); - final Task task4 = new TaskBuilder(mAtm.mStackSupervisor).setStack(stack4).build(); + final Task task1 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(stack1).build(); + final Task task2 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(stack2).build(); + final Task task3 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(stack3).build(); + final Task task4 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(stack4).build(); // Reordering stacks while removing stacks. doAnswer(invocation -> { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index eb78172cd562..e860f2508983 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -341,9 +341,8 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { public void testConsecutiveLaunchOnDifferentDisplay() { onActivityLaunched(mTopActivity); - final Task stack = new StackBuilder(mRootWindowContainer) + final Task stack = new TaskBuilder(mSupervisor) .setDisplay(addNewDisplayContentAt(DisplayContent.POSITION_BOTTOM)) - .setCreateActivity(false) .build(); final ActivityRecord activityOnNewDisplay = new ActivityBuilder(mAtm) .setStack(stack) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 59f0a7987bda..09375db01888 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -131,7 +131,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Before public void setUp() throws Exception { - mStack = new StackBuilder(mRootWindowContainer).build(); + mStack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); mTask = mStack.getBottomMostTask(); mActivity = mTask.getTopNonFinishingActivity(); @@ -172,7 +172,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testNoCleanupMovingActivityInSameStack() { - final Task newTask = new TaskBuilder(mAtm.mStackSupervisor).setStack(mStack).build(); + final Task newTask = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(mStack).build(); mActivity.reparent(newTask, 0, null /*reason*/); verify(mStack, times(0)).cleanUpActivityReferences(any()); } @@ -262,7 +262,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options. // Pending options should be cleared for only ActivityRecord that was applied - Task task2 = new TaskBuilder(mAtm.mStackSupervisor).setStack(mStack).build(); + Task task2 = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(mStack).build(); activity2 = new ActivityBuilder(mAtm).setTask(task2).build(); activity2.updateOptionsLocked(activityOptions); mActivity.updateOptionsLocked(activityOptions); @@ -415,12 +415,12 @@ public class ActivityRecordTests extends WindowTestsBase { public void ignoreRequestedOrientationInFreeformWindows() { mStack.setWindowingMode(WINDOWING_MODE_FREEFORM); final Rect stableRect = new Rect(); - mStack.getDisplay().mDisplayContent.getStableRect(stableRect); + mStack.mDisplayContent.getStableRect(stableRect); // Carve out non-decor insets from stableRect final Rect insets = new Rect(); - final DisplayInfo displayInfo = mStack.getDisplay().getDisplayInfo(); - final DisplayPolicy policy = mStack.getDisplay().getDisplayPolicy(); + final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo(); + final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy(); policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, insets); policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation); @@ -454,12 +454,12 @@ public class ActivityRecordTests extends WindowTestsBase { public void ignoreRequestedOrientationInSplitWindows() { mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final Rect stableRect = new Rect(); - mStack.getDisplay().getStableRect(stableRect); + mStack.mDisplayContent.getStableRect(stableRect); // Carve out non-decor insets from stableRect final Rect insets = new Rect(); - final DisplayInfo displayInfo = mStack.getDisplay().getDisplayInfo(); - final DisplayPolicy policy = mStack.getDisplay().getDisplayPolicy(); + final DisplayInfo displayInfo = mStack.mDisplayContent.getDisplayInfo(); + final DisplayPolicy policy = mStack.mDisplayContent.getDisplayPolicy(); policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth, displayInfo.logicalHeight, displayInfo.displayCutout, insets); policy.convertNonDecorInsetsToStableInsets(insets, displayInfo.rotation); @@ -548,7 +548,7 @@ public class ActivityRecordTests extends WindowTestsBase { .build(); mActivity.setState(Task.ActivityState.STOPPED, "Testing"); - final Task stack = new StackBuilder(mRootWindowContainer).build(); + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); try { doReturn(false).when(stack).isTranslucent(any()); assertFalse(mStack.shouldBeVisible(null /* starting */)); @@ -756,14 +756,14 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testFinishActivityIfPossible_adjustStackOrder() { // Prepare the stacks with order (top to bottom): mStack, stack1, stack2. - final Task stack1 = new StackBuilder(mRootWindowContainer).build(); + final Task stack1 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); mStack.moveToFront("test"); // The stack2 is needed here for moving back to simulate the // {@link DisplayContent#mPreferredTopFocusableStack} is cleared, so // {@link DisplayContent#getFocusedStack} will rely on the order of focusable-and-visible // stacks. Then when mActivity is finishing, its stack will be invisible (no running // activities in the stack) that is the key condition to verify. - final Task stack2 = new StackBuilder(mRootWindowContainer).build(); + final Task stack2 = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); stack2.moveToBack("test", stack2.getBottomMostTask()); assertTrue(mStack.isTopStackInDisplayArea()); @@ -844,7 +844,7 @@ public class ActivityRecordTests extends WindowTestsBase { FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */)); assertEquals(PAUSING, mActivity.getState()); verify(mActivity).setVisibility(eq(false)); - verify(mActivity.getDisplay().mDisplayContent) + verify(mActivity.mDisplayContent) .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */); } @@ -888,9 +888,9 @@ public class ActivityRecordTests extends WindowTestsBase { mActivity.finishIfPossible("test", false /* oomAdj */); verify(mActivity).setVisibility(eq(false)); - verify(mActivity.getDisplay().mDisplayContent) + verify(mActivity.mDisplayContent) .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */); - verify(mActivity.getDisplay().mDisplayContent, never()).executeAppTransition(); + verify(mActivity.mDisplayContent, never()).executeAppTransition(); } /** @@ -904,9 +904,9 @@ public class ActivityRecordTests extends WindowTestsBase { mActivity.finishIfPossible("test", false /* oomAdj */); verify(mActivity, atLeast(1)).setVisibility(eq(false)); - verify(mActivity.getDisplay().mDisplayContent) + verify(mActivity.mDisplayContent) .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */); - verify(mActivity.getDisplay().mDisplayContent).executeAppTransition(); + verify(mActivity.mDisplayContent).executeAppTransition(); } /** @@ -922,7 +922,7 @@ public class ActivityRecordTests extends WindowTestsBase { mActivity.finishIfPossible("test", false /* oomAdj */); - verify(mActivity.getDisplay().mDisplayContent, never()) + verify(mActivity.mDisplayContent, never()) .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */); } @@ -973,7 +973,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Simulates that {@code currentTop} starts an existing activity from background (so its // state is stopped) and the starting flow just goes to place it at top. - final Task nextStack = new StackBuilder(mRootWindowContainer).build(); + final Task nextStack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord nextTop = nextStack.getTopNonFinishingActivity(); nextTop.setState(STOPPED, "test"); @@ -1095,7 +1095,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Add another stack to become focused and make the activity there visible. This way it // simulates finishing in non-focused stack in split-screen. - final Task stack = new StackBuilder(mRootWindowContainer).build(); + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord focusedActivity = stack.getTopMostActivity(); focusedActivity.nowVisible = true; focusedActivity.mVisibleRequested = true; @@ -1166,7 +1166,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Finish the second activity secondActivity.finishing = true; secondActivity.completeFinishing("test"); - verify(secondActivity.getDisplay()).ensureActivitiesVisible(null /* starting */, + verify(secondActivity.mDisplayContent).ensureActivitiesVisible(null /* starting */, 0 /* configChanges */ , false /* preserveWindows */, true /* notifyClients */); @@ -1174,7 +1174,7 @@ public class ActivityRecordTests extends WindowTestsBase { firstActivity.finishing = true; firstActivity.mVisibleRequested = true; firstActivity.completeFinishing("test"); - verify(firstActivity.getDisplay(), times(2)).ensureActivitiesVisible(null /* starting */, + verify(firstActivity.mDisplayContent, times(2)).ensureActivitiesVisible(null /* starting */, 0 /* configChanges */ , false /* preserveWindows */, true /* notifyClients */); } @@ -1581,7 +1581,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Create a new task with custom config to reparent the activity to. final Task newTask = - new TaskBuilder(mSupervisor).setStack(initialTask.getRootTask()).build(); + new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build(); final Configuration newConfig = newTask.getConfiguration(); newConfig.densityDpi += 100; newTask.onRequestedOverrideConfigurationChanged(newConfig); @@ -1613,7 +1613,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Create a new task with custom config to reparent the second activity to. final Task newTask = - new TaskBuilder(mSupervisor).setStack(initialTask.getRootTask()).build(); + new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build(); final Configuration newConfig = newTask.getConfiguration(); newConfig.densityDpi += 100; newTask.onRequestedOverrideConfigurationChanged(newConfig); @@ -1750,7 +1750,7 @@ public class ActivityRecordTests extends WindowTestsBase { } final Task stack = display.getDefaultTaskDisplayArea() .createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); return new ActivityBuilder(mAtm).setTask(task).setUseProcess(process).build(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index 27e2d1370fae..f9ad49bb5034 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -115,8 +115,8 @@ public class ActivityStackSupervisorTests extends WindowTestsBase { public void testHandleNonResizableTaskOnSecondaryDisplay() { // Create an unresizable task on secondary display. final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); - final Task stack = new StackBuilder(mRootWindowContainer) - .setDisplay(newDisplay).build(); + final Task stack = new TaskBuilder(mSupervisor) + .setDisplay(newDisplay).setCreateActivity(true).build(); final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); final Task task = unresizableActivity.getTask(); unresizableActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index e2948a724acd..4951658bbd78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -100,7 +100,7 @@ public class ActivityStackTests extends WindowTestsBase { mStack = mDefaultTaskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); spyOn(mStack); - mTask = new TaskBuilder(mSupervisor).setStack(mStack).build(); + mTask = new TaskBuilder(mSupervisor).setParentTask(mStack).build(); } @Test @@ -330,7 +330,7 @@ public class ActivityStackTests extends WindowTestsBase { targetActivity); final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity); - final Task task = new TaskBuilder(mAtm.mStackSupervisor).setStack(mStack).build(); + final Task task = new TaskBuilder(mAtm.mStackSupervisor).setParentTask(mStack).build(); task.origActivity = alias; task.realActivity = target; new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity( @@ -995,7 +995,7 @@ public class ActivityStackTests extends WindowTestsBase { mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task, false /* includingParents */); } else { - task = new StackBuilder(mRootWindowContainer) + task = new TaskBuilder(mSupervisor) .setTaskDisplayArea(taskDisplayArea) .setWindowingMode(windowingMode) .setActivityType(activityType) @@ -1202,19 +1202,22 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testShouldSleepActivities() { // When focused activity and keyguard is going away, we should not sleep regardless - // of the display state + // of the display state, but keyguard-going-away should only take effects on default + // display since there is no keyguard on secondary displays (yet). verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, - true /* displaySleeping */, false /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); + verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, + true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */); // When not the focused stack, defer to display sleeping state. verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/, - true /* displaySleeping */, true /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); // If keyguard is going away, defer to the display sleeping state. verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, - true /* displaySleeping */, true /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, - false /* displaySleeping */, false /* expected*/); + false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); } @Test @@ -1423,11 +1426,13 @@ public class ActivityStackTests extends WindowTestsBase { } private void verifyShouldSleepActivities(boolean focusedStack, - boolean keyguardGoingAway, boolean displaySleeping, boolean expected) { + boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, + boolean expected) { final DisplayContent display = mock(DisplayContent.class); final KeyguardController keyguardController = mSupervisor.getKeyguardController(); + display.isDefaultDisplay = isDefaultDisplay; - doReturn(display).when(mStack).getDisplay(); + mStack.mDisplayContent = display; doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); doReturn(displaySleeping).when(display).isSleeping(); doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index e5c9ecc7676d..076047b35604 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -217,7 +217,10 @@ public class ActivityStarterTests extends WindowTestsBase { // Create source token final ActivityBuilder builder = new ActivityBuilder(service).setTask( - new TaskBuilder(service.mStackSupervisor).setVoiceSession(voiceSession).build()); + new TaskBuilder(service.mStackSupervisor) + .setVoiceSession(voiceSession) + .setCreateParentTask(true) + .build()); if (aInfo != null) { aInfo.applicationInfo = new ApplicationInfo(); @@ -706,7 +709,9 @@ public class ActivityStarterTests extends WindowTestsBase { @Test public void testBringTaskToFrontWhenFocusedStackIsFinising() { // Put 2 tasks in the same stack (simulate the behavior of home stack). + final Task rootTask = new TaskBuilder(mSupervisor).build(); final ActivityRecord activity = new ActivityBuilder(mAtm) + .setStack(rootTask) .setCreateTask(true).build(); new ActivityBuilder(mAtm) .setStack(activity.getRootTask()) @@ -792,7 +797,7 @@ public class ActivityStarterTests extends WindowTestsBase { // Create another activity on top of the secondary display. final Task topStack = secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task topTask = new TaskBuilder(mSupervisor).setStack(topStack).build(); + final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build(); new ActivityBuilder(mAtm).setTask(topTask).build(); // Start activity with the same intent as {@code singleTaskActivity} on secondary display. @@ -851,7 +856,7 @@ public class ActivityStarterTests extends WindowTestsBase { DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity"); final Task task = new TaskBuilder(mSupervisor) .setComponent(componentName) - .setStack(stack) + .setParentTask(stack) .build(); return new ActivityBuilder(mAtm) .setComponent(componentName) @@ -890,7 +895,7 @@ public class ActivityStarterTests extends WindowTestsBase { .execute(); // Ensure the activity is moved to secondary display. - assertEquals(secondaryDisplay, topActivity.getDisplay()); + assertEquals(secondaryDisplay, topActivity.mDisplayContent); } /** @@ -987,7 +992,7 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityStarter starter = prepareStarter(0 /* flags */); starter.mStartActivity = new ActivityBuilder(mAtm).build(); final Task task = new TaskBuilder(mAtm.mStackSupervisor) - .setStack(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createStack( + .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) .setUserId(10) .build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 567610018fc1..031165727207 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -73,7 +73,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { /** Verify that activity is finished correctly upon request. */ @Test public void testActivityFinish() { - final Task stack = new StackBuilder(mRootWindowContainer).build(); + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); assertTrue("Activity must be finished", mAtm.finishActivity(activity.appToken, 0 /* resultCode */, null /* resultData */, @@ -87,7 +87,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { @Test public void testOnPictureInPictureRequested() throws RemoteException { - final Task stack = new StackBuilder(mRootWindowContainer).build(); + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); final ClientLifecycleManager mockLifecycleManager = mock(ClientLifecycleManager.class); doReturn(mockLifecycleManager).when(mAtm).getLifecycleManager(); @@ -106,7 +106,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { @Test(expected = IllegalStateException.class) public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException { - final Task stack = new StackBuilder(mRootWindowContainer).build(); + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager(); doReturn(false).when(activity).inPinnedWindowingMode(); @@ -120,7 +120,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { @Test(expected = IllegalStateException.class) public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException { - final Task stack = new StackBuilder(mRootWindowContainer).build(); + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); ClientLifecycleManager lifecycleManager = mAtm.getLifecycleManager(); doReturn(true).when(activity).inPinnedWindowingMode(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index f8baf8497069..7f904265de15 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -28,6 +28,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER; import static android.os.Build.VERSION_CODES.P; import static android.os.Build.VERSION_CODES.Q; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.FLAG_PRIVATE; import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; @@ -95,6 +96,7 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; +import android.view.DisplayInfo; import android.view.Gravity; import android.view.IDisplayWindowRotationCallback; import android.view.IDisplayWindowRotationController; @@ -847,17 +849,17 @@ public class DisplayContentTests extends WindowTestsBase { dc.getDisplayRotation().setFixedToUserRotation( IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); - final Task stack = - new StackBuilder(mWm.mAtmService.mRootWindowContainer) - .setDisplay(dc) - .build(); + final Task stack = new TaskBuilder(mSupervisor) + .setDisplay(dc) + .setCreateActivity(true) + .build(); doReturn(true).when(stack).isVisible(); - final Task freeformStack = - new StackBuilder(mWm.mAtmService.mRootWindowContainer) - .setDisplay(dc) - .setWindowingMode(WINDOWING_MODE_FREEFORM) - .build(); + final Task freeformStack = new TaskBuilder(mSupervisor) + .setDisplay(dc) + .setCreateActivity(true) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .build(); doReturn(true).when(freeformStack).isVisible(); freeformStack.getTopChild().setBounds(100, 100, 300, 400); @@ -879,8 +881,8 @@ public class DisplayContentTests extends WindowTestsBase { IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); final int newOrientation = getRotatedOrientation(dc); - final Task stack = new StackBuilder(mWm.mAtmService.mRootWindowContainer) - .setDisplay(dc).build(); + final Task stack = new TaskBuilder(mSupervisor) + .setDisplay(dc).setCreateActivity(true).build(); final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity(); activity.setRequestedOrientation(newOrientation); @@ -898,8 +900,8 @@ public class DisplayContentTests extends WindowTestsBase { IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); final int newOrientation = getRotatedOrientation(dc); - final Task stack = new StackBuilder(mWm.mAtmService.mRootWindowContainer) - .setDisplay(dc).build(); + final Task stack = new TaskBuilder(mSupervisor) + .setDisplay(dc).setCreateActivity(true).build(); final ActivityRecord activity = stack.getTopMostTask().getTopNonFinishingActivity(); activity.setRequestedOrientation(newOrientation); @@ -1209,8 +1211,8 @@ public class DisplayContentTests extends WindowTestsBase { verify(t, never()).setPosition(any(), eq(0), eq(0)); // Launch another activity before the transition is finished. - final ActivityRecord app2 = new StackBuilder(mWm.mRoot) - .setDisplay(mDisplayContent).build().getTopMostActivity(); + final ActivityRecord app2 = new TaskBuilder(mSupervisor) + .setDisplay(mDisplayContent).setCreateActivity(true).build().getTopMostActivity(); app2.setVisible(false); mDisplayContent.mOpeningApps.add(app2); app2.setRequestedOrientation(newOrientation); @@ -1508,7 +1510,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() { final DisplayContent dc = createNewDisplay(); - final Task stack = new StackBuilder(mWm.mAtmService.mRootWindowContainer) + final Task stack = new TaskBuilder(mSupervisor) .setDisplay(dc) .build(); doAnswer(invocation -> { @@ -1522,6 +1524,28 @@ public class DisplayContentTests extends WindowTestsBase { dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN); } + @Test + public void testForceDesktopMode() { + mWm.mForceDesktopModeOnExternalDisplays = true; + // Not applicable for default display + assertFalse(mDefaultDisplay.forceDesktopMode()); + + // Not applicable for private secondary display. + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.copyFrom(mDisplayInfo); + displayInfo.flags = FLAG_PRIVATE; + final DisplayContent privateDc = createNewDisplay(displayInfo); + assertFalse(privateDc.forceDesktopMode()); + + // Applicable for public secondary display. + final DisplayContent publicDc = createNewDisplay(); + assertTrue(publicDc.forceDesktopMode()); + + // Make sure forceDesktopMode() is false when the force config is disabled. + mWm.mForceDesktopModeOnExternalDisplays = false; + assertFalse(publicDc.forceDesktopMode()); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 2d834ac57f51..94e40413f9f8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -26,8 +26,6 @@ import static android.view.InsetsState.ITYPE_TOP_GESTURES; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; -import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; @@ -36,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -371,6 +370,24 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test + public void layoutWindowLw_insetParentFrameByIme() { + final InsetsState state = + mDisplayContent.getInsetsStateController().getRawInsetsState(); + state.getSource(InsetsState.ITYPE_IME).setVisible(true); + state.getSource(InsetsState.ITYPE_IME).setFrame( + 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT); + mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; + mWindow.mBehindIme = true; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, IME_HEIGHT); + } + + @Test public void layoutWindowLw_fitDisplayCutout() { addDisplayCutout(); @@ -475,7 +492,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; mWindow.mAttrs.setFitInsetsTypes( mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars()); addWindow(mWindow); @@ -497,9 +513,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow) .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); + mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false); addWindow(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); @@ -519,9 +535,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow) .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); + mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; addWindow(mWindow); @@ -585,7 +601,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; mWindow.mAttrs.setFitInsetsTypes( mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars()); addWindow(mWindow); @@ -626,7 +641,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; mWindow.mAttrs.setFitInsetsTypes(0 /* types */); - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; mWindow.mAttrs.setFitInsetsTypes( mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars()); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index b50530ed3059..b77d21c0f711 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -27,11 +27,9 @@ import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; @@ -39,7 +37,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; @@ -47,7 +44,6 @@ import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -235,18 +231,6 @@ public class DisplayPolicyTests extends WindowTestsBase { assertEquals(mAppWindow, policy.getTopFullscreenOpaqueWindow()); } - @Test - public void testShouldShowToastWhenScreenLocked() { - final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); - final WindowState activity = createApplicationWindow(); - final WindowState toast = createToastWindow(); - - policy.adjustWindowParamsLw(toast, toast.mAttrs, 0 /* callingPid */, 0 /* callingUid */); - - assertTrue(policy.canToastShowWhenLocked(0 /* callingUid */)); - assertNotEquals(0, toast.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED); - } - @Test(expected = RuntimeException.class) public void testMainAppWindowDisallowFitSystemWindowTypes() { final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); @@ -257,16 +241,6 @@ public class DisplayPolicyTests extends WindowTestsBase { 0 /* callingUid */); } - private WindowState createToastWindow() { - final WindowState win = createWindow(null, TYPE_TOAST, "Toast"); - final WindowManager.LayoutParams attrs = win.mAttrs; - attrs.width = WRAP_CONTENT; - attrs.height = WRAP_CONTENT; - attrs.flags = FLAG_KEEP_SCREEN_ON | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE; - attrs.format = PixelFormat.TRANSLUCENT; - return win; - } - private WindowState createApplicationWindow() { final WindowState win = createWindow(null, TYPE_APPLICATION, "Application"); final WindowManager.LayoutParams attrs = win.mAttrs; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index b4e1c375993d..af8cb02a86fe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -62,7 +62,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static final int STATUS_BAR_HEIGHT = 10; static final int NAV_BAR_HEIGHT = 15; static final int DISPLAY_CUTOUT_HEIGHT = 8; - static final int INPUT_METHOD_WINDOW_TOP = 585; + static final int IME_HEIGHT = 415; DisplayPolicy mDisplayPolicy; diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index 18a2d1337d4b..d701a9df7cb8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -116,7 +116,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { Task stack = mTestDisplay.getDefaultTaskDisplayArea() .createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); - mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setStack(stack) + mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setParentTask(stack) .build(); mTestTask.mUserId = TEST_USER_ID; mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS; @@ -342,7 +342,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor) .setComponent(ALTERNATIVE_COMPONENT) .setUserId(TEST_USER_ID) - .setStack(stack) + .setParentTask(stack) .build(); anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM); anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500); @@ -354,7 +354,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { final Task anotherTaskOfDifferentUser = new TaskBuilder(mSupervisor) .setComponent(TEST_COMPONENT) .setUserId(ALTERNATIVE_USER_ID) - .setStack(stack) + .setParentTask(stack) .build(); anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM); anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 54c7f271e81b..253fbae4579b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -28,6 +28,8 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -331,10 +333,10 @@ public class RecentTasksTest extends WindowTestsBase { // other task Task task1 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) - .setStack(mTaskContainer.getRootHomeTask()).build(); + .setParentTask(mTaskContainer.getRootHomeTask()).build(); Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) - .setStack(mStack).build(); + .setParentTask(mStack).build(); mRecentTasks.add(task1); mRecentTasks.add(task2); assertThat(mCallbacksRecorder.mAdded).hasSize(2); @@ -350,7 +352,7 @@ public class RecentTasksTest extends WindowTestsBase { // and we want to ensure that a new task will match a restored task Task task1 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setStack(mStack) + .setParentTask(mStack) .build(); setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED); assertThat(task1.getActivityType()).isEqualTo(ACTIVITY_TYPE_UNDEFINED); @@ -359,7 +361,7 @@ public class RecentTasksTest extends WindowTestsBase { Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setStack(mStack) + .setParentTask(mStack) .build(); assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType()); mRecentTasks.add(task2); @@ -374,7 +376,7 @@ public class RecentTasksTest extends WindowTestsBase { public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() { Task task1 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setStack(mStack) + .setParentTask(mStack) .setUserId(TEST_USER_0_ID) .build(); setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED); @@ -384,7 +386,7 @@ public class RecentTasksTest extends WindowTestsBase { Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setStack(mStack) + .setParentTask(mStack) .setUserId(TEST_USER_1_ID) .build(); assertEquals(ACTIVITY_TYPE_STANDARD, task2.getActivityType()); @@ -399,7 +401,7 @@ public class RecentTasksTest extends WindowTestsBase { public void testAddTaskCompatibleWindowingMode_expectRemove() { Task task1 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setStack(mStack) + .setParentTask(mStack) .build(); setTaskWindowingMode(task1, WINDOWING_MODE_UNDEFINED); assertEquals(WINDOWING_MODE_UNDEFINED, task1.getWindowingMode()); @@ -408,7 +410,7 @@ public class RecentTasksTest extends WindowTestsBase { Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setStack(mStack) + .setParentTask(mStack) .build(); setTaskWindowingMode(task2, WINDOWING_MODE_FULLSCREEN); assertEquals(WINDOWING_MODE_FULLSCREEN, task2.getWindowingMode()); @@ -425,7 +427,7 @@ public class RecentTasksTest extends WindowTestsBase { public void testAddTaskIncompatibleWindowingMode_expectNoRemove() { Task task1 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setStack(mStack) + .setParentTask(mStack) .build(); setTaskWindowingMode(task1, WINDOWING_MODE_FULLSCREEN); assertEquals(WINDOWING_MODE_FULLSCREEN, task1.getWindowingMode()); @@ -433,7 +435,7 @@ public class RecentTasksTest extends WindowTestsBase { Task task2 = createTaskBuilder(".Task1") .setFlags(FLAG_ACTIVITY_NEW_TASK) - .setStack(mStack) + .setParentTask(mStack) .build(); setTaskWindowingMode(task2, WINDOWING_MODE_PINNED); assertEquals(WINDOWING_MODE_PINNED, task2.getWindowingMode()); @@ -447,6 +449,31 @@ public class RecentTasksTest extends WindowTestsBase { } @Test + public void testRemoveAffinityTask() { + // Add task to recents + final String taskAffinity = "affinity"; + final int uid = 10123; + final Task task1 = createTaskBuilder(".Task1").setParentTask(mStack).build(); + task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE); + mRecentTasks.add(task1); + + // Add another task to recents, and make sure the previous task was removed. + final Task task2 = createTaskBuilder(".Task2").setParentTask(mStack).build(); + task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE); + mRecentTasks.add(task2); + assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, + true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size()); + + // Add another single-instance task to recents, and make sure no task is removed. + final Task task3 = createTaskBuilder(".Task3").setParentTask(mStack).build(); + task3.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, + LAUNCH_SINGLE_INSTANCE); + mRecentTasks.add(task3); + assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, + true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size()); + } + + @Test public void testAddTasksHomeClearUntrackedTasks_expectFinish() { // There may be multiple tasks with the same base intent by flags (FLAG_ACTIVITY_NEW_TASK | // FLAG_ACTIVITY_MULTIPLE_TASK). If the previous task is still active, it should be removed @@ -470,7 +497,7 @@ public class RecentTasksTest extends WindowTestsBase { // tasks because their intents are identical. mRecentTasks.add(task1); // Go home to trigger the removal of untracked tasks. - mRecentTasks.add(createTaskBuilder(".Home").setStack(mTaskContainer.getRootHomeTask()) + mRecentTasks.add(createTaskBuilder(".Home").setParentTask(mTaskContainer.getRootHomeTask()) .build()); // The task was added into recents again so it is not hidden and shouldn't be removed. @@ -856,10 +883,10 @@ public class RecentTasksTest extends WindowTestsBase { // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all // the tasks belong in stacks above the home stack - mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task1").setStack(aboveHomeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task3").setStack(aboveHomeStack).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(aboveHomeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task3").setParentTask(aboveHomeStack).build()); assertNoTasksTrimmed(); } @@ -877,11 +904,11 @@ public class RecentTasksTest extends WindowTestsBase { // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind // the home stack is trimmed once a new task is added final Task behindHomeTask = createTaskBuilder(".Task1") - .setStack(behindHomeStack) + .setParentTask(behindHomeStack) .build(); mRecentTasks.add(behindHomeTask); - mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task2").setStack(aboveHomeStack).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build()); assertTrimmed(behindHomeTask); } @@ -897,10 +924,10 @@ public class RecentTasksTest extends WindowTestsBase { // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not // removed - mRecentTasks.add(createTaskBuilder(".HomeTask1").setStack(homeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task1").setStack(otherDisplayStack).build()); - mRecentTasks.add(createTaskBuilder(".Task2").setStack(otherDisplayStack).build()); - mRecentTasks.add(createTaskBuilder(".HomeTask2").setStack(homeStack).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build()); + mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayStack).build()); + mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayStack).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeStack).build()); assertNoTasksTrimmed(); } @@ -1187,7 +1214,7 @@ public class RecentTasksTest extends WindowTestsBase { private TaskBuilder createTaskBuilder(String packageName, String className) { return new TaskBuilder(mAtm.mStackSupervisor) .setComponent(new ComponentName(packageName, className)) - .setStack(mStack) + .setParentTask(mStack) .setUserId(TEST_USER_0_ID); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index b89d16807a6e..901ed36254ea 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -195,8 +195,7 @@ public class RootActivityContainerTests extends WindowTestsBase { public void testApplySleepTokens() { final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final Task stack = new StackBuilder(mRootWindowContainer) - .setCreateActivity(false) + final Task stack = new TaskBuilder(mSupervisor) .setDisplay(display) .setOnTop(false) .build(); @@ -384,7 +383,7 @@ public class RootActivityContainerTests extends WindowTestsBase { final Task primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea() .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryStack).build(); final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build(); // Find a launch stack for the top activity in split-screen primary, while requesting @@ -404,14 +403,17 @@ public class RootActivityContainerTests extends WindowTestsBase { @Test public void testFindTaskToMoveToFrontWhenRecentsOnTop() { // Create stack/task on default display. - final Task targetStack = new StackBuilder(mRootWindowContainer) + final Task targetStack = new TaskBuilder(mSupervisor) + .setCreateActivity(true) .setOnTop(false) .build(); final Task targetTask = targetStack.getBottomMostTask(); // Create Recents on top of the display. - final Task stack = new StackBuilder(mRootWindowContainer).setActivityType( - ACTIVITY_TYPE_RECENTS).build(); + final Task stack = new TaskBuilder(mSupervisor) + .setCreateActivity(true) + .setActivityType(ACTIVITY_TYPE_RECENTS) + .build(); final String reason = "findTaskToMoveToFront"; mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, @@ -431,14 +433,14 @@ public class RootActivityContainerTests extends WindowTestsBase { final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); final Task targetStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final Task targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetStack).build(); // Create Recents on secondary display. final TestDisplayContent secondDisplay = addNewDisplayContentAt( DisplayContent.POSITION_TOP); final Task stack = secondDisplay.getDefaultTaskDisplayArea() .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); - final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); new ActivityBuilder(mAtm).setTask(task).build(); final String reason = "findTaskToMoveToFront"; @@ -458,7 +460,7 @@ public class RootActivityContainerTests extends WindowTestsBase { final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); - final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/); @@ -514,7 +516,7 @@ public class RootActivityContainerTests extends WindowTestsBase { DisplayContent.POSITION_TOP); final Task stack = secondDisplay.getDefaultTaskDisplayArea() .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); new ActivityBuilder(mAtm).setTask(task).build(); doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); @@ -538,7 +540,7 @@ public class RootActivityContainerTests extends WindowTestsBase { final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); - final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.setState(ActivityState.RESUMED, "test"); @@ -558,7 +560,7 @@ public class RootActivityContainerTests extends WindowTestsBase { final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); - final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); activity.setState(ActivityState.RESUMED, "test"); taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/); @@ -884,7 +886,7 @@ public class RootActivityContainerTests extends WindowTestsBase { // Create a root task with an activity on secondary display. final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300, 600).build(); - final Task task = new StackBuilder(mRootWindowContainer) + final Task task = new TaskBuilder(mSupervisor) .setDisplay(secondaryDisplay).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); @@ -915,24 +917,6 @@ public class RootActivityContainerTests extends WindowTestsBase { assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask()); } - @Test - public void testResumeFocusedStackOnSleepingDisplay() { - // Create an activity on secondary display. - final TestDisplayContent secondDisplay = addNewDisplayContentAt( - DisplayContent.POSITION_TOP); - final Task stack = secondDisplay.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm).setStack(stack).build(); - spyOn(activity); - spyOn(stack); - - // Cannot resumed activities on secondary display if the display should sleep. - doReturn(true).when(secondDisplay).shouldSleep(); - mRootWindowContainer.resumeFocusedStacksTopActivities(); - verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any()); - verify(activity, never()).makeActiveIfNeeded(any()); - } - /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * info for test cases. diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 191c33c61aca..3053fe6ec55f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -174,7 +174,7 @@ public class RootWindowContainerTests extends WindowTestsBase { @Test public void testForceStopPackage() { - final Task task = new StackBuilder(mWm.mRoot).build(); + final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = task.getTopMostActivity(); final WindowProcessController wpc = activity.app; final ActivityRecord[] activities = { diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index 341509310e26..8a5b13c0bdce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -62,8 +62,7 @@ public class RunningTasksTest extends WindowTestsBase { final int numStacks = 2; for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) { - final Task stack = new StackBuilder(mRootWindowContainer) - .setCreateActivity(false) + final Task stack = new TaskBuilder(mSupervisor) .setDisplay(display) .setOnTop(false) .build(); @@ -104,8 +103,7 @@ public class RunningTasksTest extends WindowTestsBase { final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500).build(); final int numTasks = 10; for (int i = 0; i < numTasks; i++) { - final Task stack = new StackBuilder(mRootWindowContainer) - .setCreateActivity(false) + final Task stack = new TaskBuilder(mSupervisor) .setDisplay(display) .setOnTop(true) .build(); @@ -135,7 +133,7 @@ public class RunningTasksTest extends WindowTestsBase { final Task task = new TaskBuilder(mAtm.mStackSupervisor) .setComponent(new ComponentName(mContext.getPackageName(), className)) .setTaskId(taskId) - .setStack(stack) + .setParentTask(stack) .build(); task.lastActiveTime = lastActiveTime; final ActivityRecord activity = new ActivityBuilder(mAtm) diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 982e469cba92..29081d389225 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -35,7 +35,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doCallRealMethod; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -70,7 +72,7 @@ public class SizeCompatTests extends WindowTestsBase { private ActivityRecord mActivity; private void setUpApp(DisplayContent display) { - mStack = new StackBuilder(mRootWindowContainer).setDisplay(display).build(); + mStack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build(); mTask = mStack.getBottomMostTask(); mActivity = mTask.getTopNonFinishingActivity(); } @@ -90,7 +92,7 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); final Rect originalOverrideBounds = new Rect(mActivity.getBounds()); - resizeDisplay(mStack.getDisplay(), 600, 1200); + resizeDisplay(mStack.mDisplayContent, 600, 1200); // The visible activity should recompute configuration according to the last parent bounds. mAtm.restartActivityProcessIfVisible(mActivity.appToken); @@ -216,22 +218,50 @@ public class SizeCompatTests extends WindowTestsBase { final Rect origBounds = new Rect(mActivity.getBounds()); final Rect currentBounds = mActivity.getWindowConfiguration().getBounds(); + final DisplayContent display = mActivity.mDisplayContent; // Change the size of current display. - resizeDisplay(mStack.getDisplay(), 1000, 2000); - + resizeDisplay(display, 1000, 2000); + // The bounds should be [100, 0 - 1100, 2500]. assertEquals(origBounds.width(), currentBounds.width()); assertEquals(origBounds.height(), currentBounds.height()); assertScaled(); + // The scale is 2000/2500=0.8. The horizontal centered offset is (1000-(1000*0.8))/2=100. + final float scale = (float) display.mBaseDisplayHeight / currentBounds.height(); + final int offsetX = (int) (display.mBaseDisplayWidth - (origBounds.width() * scale)) / 2; + assertEquals(offsetX, currentBounds.left); + // The position of configuration bounds should be the same as compat bounds. assertEquals(mActivity.getBounds().left, currentBounds.left); assertEquals(mActivity.getBounds().top, currentBounds.top); // Change display size to a different orientation - resizeDisplay(mStack.getDisplay(), 2000, 1000); + resizeDisplay(display, 2000, 1000); + // The bounds should be [800, 0 - 1800, 2500]. assertEquals(origBounds.width(), currentBounds.width()); assertEquals(origBounds.height(), currentBounds.height()); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); + assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation); + + // The previous resize operation doesn't consider the rotation change after size changed. + // These setups apply the requested orientation to rotation as real case that the top fixed + // portrait activity will determine the display rotation. + final DisplayRotation displayRotation = display.getDisplayRotation(); + doCallRealMethod().when(displayRotation).updateRotationUnchecked(anyBoolean()); + // Skip unrelated layout procedures. + mAtm.deferWindowLayout(); + display.reconfigureDisplayLocked(); + displayRotation.updateOrientation(display.getOrientation(), true /* forceUpdate */); + display.sendNewConfiguration(); + + assertEquals(Configuration.ORIENTATION_PORTRAIT, display.getConfiguration().orientation); + assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation); + // The size should still be in portrait [100, 0 - 1100, 2500] = 1000x2500. + assertEquals(origBounds.width(), currentBounds.width()); + assertEquals(origBounds.height(), currentBounds.height()); + assertEquals(offsetX, currentBounds.left); + assertScaled(); } @Test @@ -412,7 +442,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testResetNonVisibleActivity() { setUpDisplaySizeWithApp(1000, 2500); prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED); - final DisplayContent display = mStack.getDisplay(); + final DisplayContent display = mStack.mDisplayContent; // Resize the display so the activity is in size compatibility mode. resizeDisplay(display, 900, 1800); @@ -464,7 +494,7 @@ public class SizeCompatTests extends WindowTestsBase { }); // Resize the display so that the activity exercises size-compat mode. - resizeDisplay(mStack.getDisplay(), 1000, 2500); + resizeDisplay(mStack.mDisplayContent, 1000, 2500); // Expect the exact token when the activity is in size compatibility mode. assertEquals(1, compatTokens.size()); @@ -477,7 +507,7 @@ public class SizeCompatTests extends WindowTestsBase { activity.restartProcessIfVisible(); // The full lifecycle isn't hooked up so manually set state to resumed activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode"); - mStack.getDisplay().handleActivitySizeCompatModeIfNeeded(activity); + mStack.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity); // Expect null token when switching to non-size-compat mode activity. assertEquals(1, compatTokens.size()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 260f1e9a9259..bc3b3a4d7ad1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -218,7 +218,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final Task rootHomeTask = defaultTaskDisplayArea.getRootHomeTask(); rootHomeTask.mResizeMode = RESIZE_MODE_UNRESIZEABLE; - final Task primarySplitTask = new StackBuilder(rootWindowContainer) + final Task primarySplitTask = new TaskBuilder(mSupervisor) .setTaskDisplayArea(defaultTaskDisplayArea) .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) .setActivityType(ACTIVITY_TYPE_STANDARD) diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 42de5e6b429d..1d32e17dd5e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -1358,7 +1358,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { final Task stack = display.getDefaultTaskDisplayArea() .createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); stack.setWindowingMode(WINDOWING_MODE_FREEFORM); - final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); // Just work around the unnecessary adjustments for bounds. task.getWindowConfiguration().setBounds(bounds); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 08537a4ea9c1..a908bfef98de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -176,7 +176,7 @@ public class TaskRecordTests extends WindowTestsBase { TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); Task stack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); - Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); final Configuration parentConfig = stack.getConfiguration(); parentConfig.windowConfiguration.setBounds(parentBounds); parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT; @@ -212,7 +212,7 @@ public class TaskRecordTests extends WindowTestsBase { @Test public void testBoundsOnModeChangeFreeformToFullscreen() { DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay(); - Task stack = new StackBuilder(mRootWindowContainer).setDisplay(display) + Task stack = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); Task task = stack.getBottomMostTask(); task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); @@ -253,7 +253,7 @@ public class TaskRecordTests extends WindowTestsBase { dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); - final Task stack = new StackBuilder(mRootWindowContainer) + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); final Task task = stack.getBottomMostTask(); final ActivityRecord root = task.getTopNonFinishingActivity(); @@ -317,7 +317,7 @@ public class TaskRecordTests extends WindowTestsBase { dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); - final Task stack = new StackBuilder(mRootWindowContainer) + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); final Task task = stack.getBottomMostTask(); ActivityRecord root = task.getTopNonFinishingActivity(); @@ -341,7 +341,7 @@ public class TaskRecordTests extends WindowTestsBase { Configuration.ORIENTATION_LANDSCAPE; display.onRequestedOverrideConfigurationChanged( display.getRequestedOverrideConfiguration()); - Task stack = new StackBuilder(mRootWindowContainer) + Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); Task task = stack.getBottomMostTask(); ActivityRecord root = task.getTopNonFinishingActivity(); @@ -497,7 +497,7 @@ public class TaskRecordTests extends WindowTestsBase { DisplayInfo displayInfo = new DisplayInfo(); mAtm.mContext.getDisplay().getDisplayInfo(displayInfo); final int displayHeight = displayInfo.logicalHeight; - final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); final Configuration inOutConfig = new Configuration(); final Configuration parentConfig = new Configuration(); final int longSide = 1200; @@ -1029,7 +1029,7 @@ public class TaskRecordTests extends WindowTestsBase { } private Task getTestTask() { - final Task stack = new StackBuilder(mRootWindowContainer).build(); + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); return stack.getBottomMostTask(); } @@ -1039,7 +1039,7 @@ public class TaskRecordTests extends WindowTestsBase { TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); Task stack = taskDisplayArea.createStack(windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */); - Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); + Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); final Configuration parentConfig = stack.getConfiguration(); parentConfig.windowConfiguration.setAppBounds(parentBounds); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index bce1142c99be..ca3f815698e8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -53,7 +53,6 @@ import androidx.test.filters.FlakyTest; import androidx.test.filters.MediumTest; import com.android.compatibility.common.util.SystemUtil; -import com.android.internal.annotations.GuardedBy; import org.junit.After; import org.junit.Before; @@ -76,14 +75,10 @@ public class TaskStackChangedListenerTest { private static final int WAIT_TIMEOUT_MS = 5000; private static final Object sLock = new Object(); - @GuardedBy("sLock") - private static boolean sTaskStackChangedCalled; - private static boolean sActivityBResumed; @Before public void setUp() throws Exception { mService = ActivityManager.getService(); - sTaskStackChangedCalled = false; } @After @@ -94,47 +89,33 @@ public class TaskStackChangedListenerTest { @Test @Presubmit - @FlakyTest(bugId = 130388819) public void testTaskStackChanged_afterFinish() throws Exception { + final TestActivity activity = startTestActivity(ActivityA.class); + final CountDownLatch latch = new CountDownLatch(1); registerTaskStackChangedListener(new TaskStackListener() { @Override public void onTaskStackChanged() throws RemoteException { - synchronized (sLock) { - sTaskStackChangedCalled = true; - } + latch.countDown(); } }); - Context context = getInstrumentation().getContext(); - context.startActivity( - new Intent(context, ActivityA.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - UiDevice.getInstance(getInstrumentation()).waitForIdle(); - synchronized (sLock) { - assertTrue(sTaskStackChangedCalled); - } - assertTrue(sActivityBResumed); + activity.finish(); + waitForCallback(latch); } @Test @Presubmit public void testTaskStackChanged_resumeWhilePausing() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); registerTaskStackChangedListener(new TaskStackListener() { @Override public void onTaskStackChanged() throws RemoteException { - synchronized (sLock) { - sTaskStackChangedCalled = true; - } + latch.countDown(); } }); - final Context context = getInstrumentation().getContext(); - context.startActivity(new Intent(context, ResumeWhilePausingActivity.class).addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK)); - UiDevice.getInstance(getInstrumentation()).waitForIdle(); - - synchronized (sLock) { - assertTrue(sTaskStackChangedCalled); - } + startTestActivity(ResumeWhilePausingActivity.class); + waitForCallback(latch); } @Test @@ -512,7 +493,7 @@ public class TaskStackChangedListenerTest { try { final boolean result = latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS); if (!result) { - throw new RuntimeException("Timed out waiting for task stack change notification"); + throw new AssertionError("Timed out waiting for task stack change notification"); } } catch (InterruptedException e) { } @@ -569,19 +550,6 @@ public class TaskStackChangedListenerTest { } public static class ActivityA extends TestActivity { - - private boolean mActivityBLaunched = false; - - @Override - protected void onPostResume() { - super.onPostResume(); - if (mActivityBLaunched) { - return; - } - mActivityBLaunched = true; - finish(); - startActivity(new Intent(this, ActivityB.class)); - } } public static class ActivityB extends TestActivity { @@ -589,10 +557,6 @@ public class TaskStackChangedListenerTest { @Override protected void onPostResume() { super.onPostResume(); - synchronized (sLock) { - sTaskStackChangedCalled = false; - } - sActivityBResumed = true; finish(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 4163a9a546a0..36f3a21e38f1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -829,7 +829,7 @@ public class WindowContainerTests extends WindowTestsBase { final DisplayContent displayContent = createNewDisplay(); // Do not reparent activity to default display when removing the display. doReturn(true).when(displayContent).shouldDestroyContentOnRemove(); - final ActivityRecord r = new StackBuilder(mWm.mRoot) + final ActivityRecord r = new TaskBuilder(mSupervisor).setCreateActivity(true) .setDisplay(displayContent).build().getTopMostActivity(); // Add a window and make the activity animating so the removal of activity is deferred. createWindow(null, TYPE_BASE_APPLICATION, r, "win"); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index f97dff3162c4..2510385f4580 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -114,11 +114,11 @@ public class WindowManagerServiceTests extends WindowTestsBase { Task tappedStack = createTaskStackOnDisplay( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, display); Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */); - spyOn(mWm.mActivityTaskManager); + spyOn(mWm.mAtmService); mWm.handleTaskFocusChange(tappedTask); - verify(mWm.mActivityTaskManager).setFocusedTask(tappedTask.mTaskId); + verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId); } @Test @@ -135,11 +135,11 @@ public class WindowManagerServiceTests extends WindowTestsBase { Task tappedStack = createTaskStackOnDisplay( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, display); Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */); - spyOn(mWm.mActivityTaskManager); + spyOn(mWm.mAtmService); mWm.handleTaskFocusChange(tappedTask); - verify(mWm.mActivityTaskManager, never()).setFocusedTask(tappedTask.mTaskId); + verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId); } @Test @@ -158,10 +158,10 @@ public class WindowManagerServiceTests extends WindowTestsBase { Task tappedStack = createTaskStackOnTaskDisplayArea( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, secondTda); Task tappedTask = createTaskInStack(tappedStack, 0 /* userId */); - spyOn(mWm.mActivityTaskManager); + spyOn(mWm.mAtmService); mWm.handleTaskFocusChange(tappedTask); - verify(mWm.mActivityTaskManager).setFocusedTask(tappedTask.mTaskId); + verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 289d54e967f5..38909f6a28d4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -290,7 +290,7 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testTaskTransaction() { removeGlobalMinSizeRestriction(); - final Task stack = new StackBuilder(mWm.mRoot) + final Task stack = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); final Task task = stack.getTopMostTask(); testTransaction(task); @@ -299,7 +299,7 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testStackTransaction() { removeGlobalMinSizeRestriction(); - final Task stack = new StackBuilder(mWm.mRoot) + final Task stack = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); StackInfo info = mWm.mAtmService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); @@ -324,7 +324,7 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testSetWindowingMode() { - final Task stack = new StackBuilder(mWm.mRoot) + final Task stack = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); testSetWindowingMode(stack); @@ -358,7 +358,7 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testContainerFocusableChanges() { removeGlobalMinSizeRestriction(); - final Task stack = new StackBuilder(mWm.mRoot) + final Task stack = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); final Task task = stack.getTopMostTask(); WindowContainerTransaction t = new WindowContainerTransaction(); @@ -374,7 +374,7 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testContainerHiddenChanges() { removeGlobalMinSizeRestriction(); - final Task stack = new StackBuilder(mWm.mRoot) + final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); WindowContainerTransaction t = new WindowContainerTransaction(); assertTrue(stack.shouldBeVisible(null)); @@ -389,7 +389,7 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testOverrideConfigSize() { removeGlobalMinSizeRestriction(); - final Task stack = new StackBuilder(mWm.mRoot) + final Task stack = new TaskBuilder(mSupervisor) .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); final Task task = stack.getTopMostTask(); WindowContainerTransaction t = new WindowContainerTransaction(); @@ -930,23 +930,36 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task stack = createStack(); final Task task = createTask(stack); final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task); + final Task stack2 = createStack(); + final Task task2 = createTask(stack2); + final ActivityRecord activity2 = createActivityRecordInTask(stack.mDisplayContent, task2); final ITaskOrganizer organizer = registerMockOrganizer(); // Setup the task to be controlled by the MW mode organizer stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); assertTrue(stack.isOrganized()); + assertTrue(stack2.isOrganized()); // Verify a back pressed does not call the organizer mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); verify(organizer, never()).onBackPressedOnTaskRoot(any()); // Enable intercepting back - mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(organizer, - true); + mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( + stack.mRemoteToken.toWindowContainerToken(), true); // Verify now that the back press does call the organizer mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); + + // Disable intercepting back + mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( + stack.mRemoteToken.toWindowContainerToken(), false); + + // Verify now that the back press no longer calls the organizer + mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); + verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 9603d28c286b..3106ca26c8a1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -661,14 +661,14 @@ public class WindowStateTests extends WindowTestsBase { RecentsAnimationController recentsController = mock(RecentsAnimationController.class); when(recentsController.shouldApplyInputConsumer(win0.mActivityRecord)).thenReturn(true); mWm.setRecentsAnimationController(recentsController); - assertTrue(win0.cantReceiveTouchInput()); + assertFalse(win0.canReceiveTouchInput()); } @Test public void testCantReceiveTouchWhenAppTokenHiddenRequested() { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); win0.mActivityRecord.mVisibleRequested = false; - assertTrue(win0.cantReceiveTouchInput()); + assertFalse(win0.canReceiveTouchInput()); } @Test @@ -676,7 +676,7 @@ public class WindowStateTests extends WindowTestsBase { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); win0.mActivityRecord.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); win0.mActivityRecord.getStack().setFocusable(false); - assertTrue(win0.cantReceiveTouchInput()); + assertFalse(win0.canReceiveTouchInput()); } @UseTestDisplay(addWindows = W_ACTIVITY) diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 38c4e0a7de02..7daddd8720ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -90,6 +90,7 @@ import com.android.server.AttributeCache; import org.junit.Before; import org.junit.BeforeClass; import org.junit.runner.Description; +import org.mockito.Mockito; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -398,22 +399,20 @@ class WindowTestsBase extends SystemServiceTestsBase { } Task createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) { - return new StackBuilder(dc.mWmService.mRoot) + return new TaskBuilder(dc.mAtmService.mStackSupervisor) .setDisplay(dc) .setWindowingMode(windowingMode) .setActivityType(activityType) - .setCreateActivity(false) .setIntent(new Intent()) .build(); } Task createTaskStackOnTaskDisplayArea(int windowingMode, int activityType, TaskDisplayArea tda) { - return new StackBuilder(tda.mWmService.mRoot) + return new TaskBuilder(tda.mDisplayContent.mAtmService.mStackSupervisor) .setTaskDisplayArea(tda) .setWindowingMode(windowingMode) .setActivityType(activityType) - .setCreateActivity(false) .setIntent(new Intent()) .build(); } @@ -422,7 +421,7 @@ class WindowTestsBase extends SystemServiceTestsBase { Task createTaskInStack(Task stack, int userId) { final Task task = new TaskBuilder(stack.mStackSupervisor) .setUserId(userId) - .setStack(stack) + .setParentTask(stack) .build(); return task; } @@ -733,7 +732,7 @@ class WindowTestsBase extends SystemServiceTestsBase { if (mCreateTask) { mTask = new TaskBuilder(mService.mStackSupervisor) .setComponent(mComponent) - .setStack(mStack).build(); + .setParentTask(mStack).build(); } else if (mTask == null && mStack != null && DisplayContent.alwaysCreateStack( mStack.getWindowingMode(), mStack.getActivityType())) { // The stack can be the task root. @@ -813,43 +812,51 @@ class WindowTestsBase extends SystemServiceTestsBase { protected static class TaskBuilder { private final ActivityStackSupervisor mSupervisor; + private TaskDisplayArea mTaskDisplayArea; private ComponentName mComponent; private String mPackage; private int mFlags = 0; - // Task id 0 is reserved in ARC for the home app. - private int mTaskId = SystemServicesTestRule.sNextTaskId++; + private int mTaskId = -1; private int mUserId = 0; + private int mWindowingMode = WINDOWING_MODE_UNDEFINED; + private int mActivityType = ACTIVITY_TYPE_STANDARD; + private ActivityInfo mActivityInfo; + private Intent mIntent; + private boolean mOnTop = true; private IVoiceInteractionSession mVoiceSession; - private boolean mCreateStack = true; - private Task mStack; - private TaskDisplayArea mTaskDisplayArea; + private boolean mCreateParentTask = false; + private Task mParentTask; + + private boolean mCreateActivity = false; TaskBuilder(ActivityStackSupervisor supervisor) { mSupervisor = supervisor; + mTaskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); } - TaskBuilder setComponent(ComponentName component) { - mComponent = component; + /** + * Set the parent {@link DisplayContent} and use the default task display area. Overrides + * the task display area, if was set before. + */ + TaskBuilder setDisplay(DisplayContent display) { + mTaskDisplayArea = display.getDefaultTaskDisplayArea(); return this; } - TaskBuilder setPackage(String packageName) { - mPackage = packageName; + /** Set the parent {@link TaskDisplayArea}. Overrides the display, if was set before. */ + TaskBuilder setTaskDisplayArea(TaskDisplayArea taskDisplayArea) { + mTaskDisplayArea = taskDisplayArea; return this; } - /** - * Set to {@code true} by default, set to {@code false} to prevent the task from - * automatically creating a parent stack. - */ - TaskBuilder setCreateStack(boolean createStack) { - mCreateStack = createStack; + TaskBuilder setComponent(ComponentName component) { + mComponent = component; return this; } - TaskBuilder setVoiceSession(IVoiceInteractionSession session) { - mVoiceSession = session; + TaskBuilder setPackage(String packageName) { + mPackage = packageName; return this; } @@ -868,156 +875,117 @@ class WindowTestsBase extends SystemServiceTestsBase { return this; } - TaskBuilder setStack(Task stack) { - mStack = stack; - return this; - } - - TaskBuilder setDisplay(DisplayContent display) { - mTaskDisplayArea = display.getDefaultTaskDisplayArea(); - return this; - } - - Task build() { - SystemServicesTestRule.checkHoldsLock(mSupervisor.mService.mGlobalLock); - - if (mStack == null && mCreateStack) { - TaskDisplayArea displayArea = mTaskDisplayArea != null ? mTaskDisplayArea - : mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); - mStack = displayArea.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - spyOn(mStack); - } - - final ActivityInfo aInfo = new ActivityInfo(); - aInfo.applicationInfo = new ApplicationInfo(); - aInfo.applicationInfo.packageName = mPackage; - - Intent intent = new Intent(); - if (mComponent == null) { - mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, - DEFAULT_COMPONENT_CLASS_NAME); - } - - intent.setComponent(mComponent); - intent.setFlags(mFlags); - - final Task task = new Task(mSupervisor.mService, mTaskId, aInfo, - intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/, - null /*taskDescription*/, mStack); - spyOn(task); - task.mUserId = mUserId; - - if (mStack != null) { - mStack.moveToFront("test"); - mStack.addChild(task, true, true); - } - - return task; - } - } - - static class StackBuilder { - private final RootWindowContainer mRootWindowContainer; - private DisplayContent mDisplay; - private TaskDisplayArea mTaskDisplayArea; - private int mStackId = -1; - private int mWindowingMode = WINDOWING_MODE_UNDEFINED; - private int mActivityType = ACTIVITY_TYPE_STANDARD; - private boolean mOnTop = true; - private boolean mCreateActivity = true; - private ActivityInfo mInfo; - private Intent mIntent; - - StackBuilder(RootWindowContainer root) { - mRootWindowContainer = root; - mDisplay = mRootWindowContainer.getDefaultDisplay(); - mTaskDisplayArea = mDisplay.getDefaultTaskDisplayArea(); - } - - StackBuilder setWindowingMode(int windowingMode) { + TaskBuilder setWindowingMode(int windowingMode) { mWindowingMode = windowingMode; return this; } - StackBuilder setActivityType(int activityType) { + TaskBuilder setActivityType(int activityType) { mActivityType = activityType; return this; } - StackBuilder setStackId(int stackId) { - mStackId = stackId; + TaskBuilder setActivityInfo(ActivityInfo info) { + mActivityInfo = info; return this; } - /** - * Set the parent {@link DisplayContent} and use the default task display area. Overrides - * the task display area, if was set before. - */ - StackBuilder setDisplay(DisplayContent display) { - mDisplay = display; - mTaskDisplayArea = mDisplay.getDefaultTaskDisplayArea(); + TaskBuilder setIntent(Intent intent) { + mIntent = intent; return this; } - /** Set the parent {@link TaskDisplayArea}. Overrides the display, if was set before. */ - StackBuilder setTaskDisplayArea(TaskDisplayArea taskDisplayArea) { - mTaskDisplayArea = taskDisplayArea; - mDisplay = mTaskDisplayArea.mDisplayContent; + TaskBuilder setOnTop(boolean onTop) { + mOnTop = onTop; return this; } - StackBuilder setOnTop(boolean onTop) { - mOnTop = onTop; + TaskBuilder setVoiceSession(IVoiceInteractionSession session) { + mVoiceSession = session; return this; } - StackBuilder setCreateActivity(boolean createActivity) { - mCreateActivity = createActivity; + TaskBuilder setCreateParentTask(boolean createParentTask) { + mCreateParentTask = createParentTask; return this; } - StackBuilder setActivityInfo(ActivityInfo info) { - mInfo = info; + TaskBuilder setParentTask(Task parentTask) { + mParentTask = parentTask; return this; } - StackBuilder setIntent(Intent intent) { - mIntent = intent; + TaskBuilder setCreateActivity(boolean createActivity) { + mCreateActivity = createActivity; return this; } Task build() { - SystemServicesTestRule.checkHoldsLock(mRootWindowContainer.mWmService.mGlobalLock); + SystemServicesTestRule.checkHoldsLock(mSupervisor.mService.mGlobalLock); + + // Create parent task. + if (mParentTask == null && mCreateParentTask) { + mParentTask = mTaskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + } + if (mParentTask != null && !Mockito.mockingDetails(mParentTask).isSpy()) { + spyOn(mParentTask); + } + + // Create task. + if (mActivityInfo == null) { + mActivityInfo = new ActivityInfo(); + mActivityInfo.applicationInfo = new ApplicationInfo(); + mActivityInfo.applicationInfo.packageName = mPackage; + } - final int stackId = mStackId >= 0 ? mStackId : mTaskDisplayArea.getNextStackId(); - final Task stack = mTaskDisplayArea.createStackUnchecked( - mWindowingMode, mActivityType, stackId, mOnTop, mInfo, mIntent, - false /* createdByOrganizer */); - final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor; + if (mIntent == null) { + mIntent = new Intent(); + if (mComponent == null) { + mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, + DEFAULT_COMPONENT_CLASS_NAME); + } + mIntent.setComponent(mComponent); + mIntent.setFlags(mFlags); + } + Task task; + final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextStackId(); + if (mParentTask == null) { + task = mTaskDisplayArea.createStackUnchecked( + mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo, + mIntent, false /* createdByOrganizer */); + } else { + task = new Task(mSupervisor.mService, taskId, mActivityInfo, + mIntent /*intent*/, mVoiceSession, null /*_voiceInteractor*/, + null /*taskDescription*/, mParentTask); + mParentTask.moveToFront("build-task"); + mParentTask.addChild(task, true, true); + } + spyOn(task); + task.mUserId = mUserId; + Task rootTask = task.getRootTask(); + doNothing().when(rootTask).startActivityLocked( + any(), any(), anyBoolean(), anyBoolean(), any()); + + // Create child task with activity. if (mCreateActivity) { - new ActivityBuilder(supervisor.mService) + new ActivityBuilder(mSupervisor.mService) .setCreateTask(true) - .setStack(stack) + .setStack(task) .build(); if (mOnTop) { // We move the task to front again in order to regain focus after activity - // added to the stack. Or {@link DisplayContent#mPreferredTopFocusableStack} + // added to the stack. Or {@link TaskDisplayArea#mPreferredTopFocusableStack} // could be other stacks (e.g. home stack). - stack.moveToFront("createActivityStack"); + task.moveToFront("createActivityTask"); } else { - stack.moveToBack("createActivityStack", null); + task.moveToBack("createActivityTask", null); } } - spyOn(stack); - - doNothing().when(stack).startActivityLocked( - any(), any(), anyBoolean(), anyBoolean(), any()); - return stack; + return task; } - } static class TestSplitOrganizer extends ITaskOrganizer.Stub { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 81aad972898e..f151d9ca2420 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -183,6 +183,7 @@ public class UsageStatsService extends SystemService implements private static class ActivityData { private final String mTaskRootPackage; private final String mTaskRootClass; + public int lastEvent = Event.NONE; private ActivityData(String taskRootPackage, String taskRootClass) { mTaskRootPackage = taskRootPackage; mTaskRootClass = taskRootClass; @@ -785,6 +786,7 @@ public class UsageStatsService extends SystemService implements switch (event.mEventType) { case Event.ACTIVITY_RESUMED: case Event.ACTIVITY_PAUSED: + case Event.ACTIVITY_STOPPED: uid = mPackageManagerInternal.getPackageUid(event.mPackage, 0, userId); break; default: @@ -817,8 +819,10 @@ public class UsageStatsService extends SystemService implements .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND); // check if this activity has already been resumed if (mVisibleActivities.get(event.mInstanceId) != null) break; - mVisibleActivities.put(event.mInstanceId, - new ActivityData(event.mTaskRootPackage, event.mTaskRootClass)); + final ActivityData resumedData = new ActivityData(event.mTaskRootPackage, + event.mTaskRootClass); + resumedData.lastEvent = Event.ACTIVITY_RESUMED; + mVisibleActivities.put(event.mInstanceId, resumedData); try { switch(mUsageSource) { case USAGE_SOURCE_CURRENT_ACTIVITY: @@ -834,16 +838,17 @@ public class UsageStatsService extends SystemService implements } break; case Event.ACTIVITY_PAUSED: - if (event.mTaskRootPackage == null) { - // Task Root info is missing. Repair the event based on previous data - final ActivityData prevData = mVisibleActivities.get(event.mInstanceId); - if (prevData == null) { - Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage - + "/" + event.mClass + " event : " + event.mEventType - + " instanceId : " + event.mInstanceId + ")"); - } else { - event.mTaskRootPackage = prevData.mTaskRootPackage; - event.mTaskRootClass = prevData.mTaskRootClass; + final ActivityData pausedData = mVisibleActivities.get(event.mInstanceId); + if (pausedData == null) { + Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage + + "/" + event.mClass + " event : " + event.mEventType + + " instanceId : " + event.mInstanceId + ")"); + } else { + pausedData.lastEvent = Event.ACTIVITY_PAUSED; + if (event.mTaskRootPackage == null) { + // Task Root info is missing. Repair the event based on previous data + event.mTaskRootPackage = pausedData.mTaskRootPackage; + event.mTaskRootClass = pausedData.mTaskRootClass; } } FrameworkStatsLog.write( @@ -866,6 +871,16 @@ public class UsageStatsService extends SystemService implements return; } + if (prevData.lastEvent != Event.ACTIVITY_PAUSED) { + FrameworkStatsLog.write( + FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED, + uid, + event.mPackage, + event.mClass, + FrameworkStatsLog + .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND); + } + ArraySet<String> tokens; synchronized (mUsageReporters) { tokens = mUsageReporters.removeReturnOld(event.mInstanceId); |