diff options
251 files changed, 5352 insertions, 1489 deletions
diff --git a/api/current.txt b/api/current.txt index 09de005c008c..b70103b931d4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11900,6 +11900,7 @@ package android.content.pm { field public static final int INVALID_ID = -1; // 0xffffffff field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2 field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 + field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4 field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 36ff20f45ec2..9c796128df84 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -417,7 +417,7 @@ status_t BootAnimation::readyToRun() { // this guest property specifies multi-display IDs to show the boot animation // multiple ids can be set with comma (,) as separator, for example: // setprop persist.boot.animation.displays 19260422155234049,19261083906282754 - Vector<uint64_t> physicalDisplayIds; + Vector<PhysicalDisplayId> physicalDisplayIds; char displayValue[PROPERTY_VALUE_MAX] = ""; property_get(DISPLAYS_PROP_NAME, displayValue, ""); bool isValid = displayValue[0] != '\0'; @@ -435,7 +435,7 @@ status_t BootAnimation::readyToRun() { } if (isValid) { std::istringstream stream(displayValue); - for (PhysicalDisplayId id; stream >> id; ) { + for (PhysicalDisplayId id; stream >> id.value; ) { physicalDisplayIds.add(id); if (stream.peek() == ',') stream.ignore(); diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index a46a54cc2063..dec4a567fc81 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -51,12 +51,11 @@ static void usage(const char* pname, PhysicalDisplayId displayId) "usage: %s [-hp] [-d display-id] [FILENAME]\n" " -h: this message\n" " -p: save the file as a png.\n" - " -d: specify the physical display ID to capture (default: %" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ")\n" + " -d: specify the physical display ID to capture (default: %s)\n" " see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n" "If FILENAME ends with .png it will be saved as a png.\n" "If FILENAME is not given, the results will be printed to stdout.\n", - pname, displayId); + pname, to_string(displayId).c_str()); } static int32_t flinger2bitmapFormat(PixelFormat f) @@ -137,7 +136,7 @@ int main(int argc, char** argv) png = true; break; case 'd': - displayId = atoll(optarg); + displayId = PhysicalDisplayId(atoll(optarg)); break; case '?': case 'h': @@ -183,7 +182,7 @@ int main(int argc, char** argv) ProcessState::self()->startThreadPool(); ScreenCaptureResults captureResults; - status_t result = ScreenshotClient::captureDisplay(*displayId, captureResults); + status_t result = ScreenshotClient::captureDisplay(displayId->value, captureResults); if (result != NO_ERROR) { close(fd); return 1; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2be92826cf38..522b8cc5a46f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2880,7 +2880,7 @@ public class Activity extends ContextThemeWrapper * Return the number of actions that will be displayed in the picture-in-picture UI when the * user interacts with the activity currently in picture-in-picture mode. This number may change * if the global configuration changes (ie. if the device is plugged into an external display), - * but will always be larger than three. + * but will always be at least three. */ public int getMaxNumPictureInPictureActions() { try { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 9b13d256aea6..1e8d19bd59c4 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -420,6 +420,8 @@ public final class ActivityThread extends ClientTransactionHandler { private static final class ProviderKey { final String authority; final int userId; + ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter + int mWaiters; // Number of threads waiting on the publishing of the provider public ProviderKey(String authority, int userId) { this.authority = authority; @@ -437,7 +439,11 @@ public final class ActivityThread extends ClientTransactionHandler { @Override public int hashCode() { - return ((authority != null) ? authority.hashCode() : 0) ^ userId; + return hashCode(authority, userId); + } + + public static int hashCode(final String auth, final int userIdent) { + return ((auth != null) ? auth.hashCode() : 0) ^ userIdent; } } @@ -458,9 +464,8 @@ public final class ActivityThread extends ClientTransactionHandler { // Mitigation for b/74523247: Used to serialize calls to AM.getContentProvider(). // Note we never removes items from this map but that's okay because there are only so many // users and so many authorities. - // TODO Remove it once we move CPR.wait() from AMS to the client side. - @GuardedBy("mGetProviderLocks") - final ArrayMap<ProviderKey, Object> mGetProviderLocks = new ArrayMap<>(); + @GuardedBy("mGetProviderKeys") + final SparseArray<ProviderKey> mGetProviderKeys = new SparseArray<>(); final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>(); @@ -1751,6 +1756,16 @@ public final class ActivityThread extends ClientTransactionHandler { ActivityThread.this, activityToken, actionId, arguments, cancellationSignal, resultCallback)); } + + @Override + public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder, + @NonNull String auth, int userId, boolean published) { + final ProviderKey key = getGetProviderKey(auth, userId); + synchronized (key) { + key.mHolder = holder; + key.notifyAll(); + } + } } private @NonNull SafeCancellationTransport createSafeCancellationTransport( @@ -6796,13 +6811,40 @@ public final class ActivityThread extends ClientTransactionHandler { // provider since it might take a long time to run and it could also potentially // be re-entrant in the case where the provider is in the same process. ContentProviderHolder holder = null; - try { - synchronized (getGetProviderLock(auth, userId)) { - holder = ActivityManager.getService().getContentProvider( - getApplicationThread(), c.getOpPackageName(), auth, userId, stable); + final ProviderKey key = getGetProviderKey(auth, userId); + synchronized (key) { + boolean wasWaiting = false; + try { + if (key.mWaiters == 0) { + // No other thread is waiting for this provider, let's fetch one by ourselves. + // If the returned holder is non-null but its provider is null and it's not + // local, we'll need to wait for the publishing of the provider. + holder = ActivityManager.getService().getContentProvider( + getApplicationThread(), c.getOpPackageName(), auth, userId, stable); + } + if ((holder != null && holder.provider == null && !holder.mLocal) + || (key.mWaiters > 0 && (holder = key.mHolder) == null)) { + try { + key.mWaiters++; + wasWaiting = true; + key.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS); + holder = key.mHolder; + if (holder != null && holder.provider == null) { + // probably timed out + holder = null; + } + } catch (InterruptedException e) { + holder = null; + } + } + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } finally { + if (wasWaiting && --key.mWaiters == 0) { + // Clear the holder from the key since the key itself is never cleared. + key.mHolder = null; + } } - } catch (RemoteException ex) { - throw ex.rethrowFromSystemServer(); } if (holder == null) { if (UserManager.get(c).isUserUnlocked(userId)) { @@ -6820,13 +6862,13 @@ public final class ActivityThread extends ClientTransactionHandler { return holder.provider; } - private Object getGetProviderLock(String auth, int userId) { - final ProviderKey key = new ProviderKey(auth, userId); - synchronized (mGetProviderLocks) { - Object lock = mGetProviderLocks.get(key); + private ProviderKey getGetProviderKey(String auth, int userId) { + final int key = ProviderKey.hashCode(auth, userId); + synchronized (mGetProviderKeys) { + ProviderKey lock = mGetProviderKeys.get(key); if (lock == null) { - lock = key; - mGetProviderLocks.put(key, lock); + lock = new ProviderKey(auth, userId); + mGetProviderKeys.put(key, lock); } return lock; } diff --git a/core/java/android/app/ContentProviderHolder.java b/core/java/android/app/ContentProviderHolder.java index 3d745831ce1c..e330a30de7b0 100644 --- a/core/java/android/app/ContentProviderHolder.java +++ b/core/java/android/app/ContentProviderHolder.java @@ -39,6 +39,11 @@ public class ContentProviderHolder implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public boolean noReleaseNeeded; + /** + * Whether the provider here is a local provider or not. + */ + public boolean mLocal; + @UnsupportedAppUsage public ContentProviderHolder(ProviderInfo _info) { info = _info; @@ -59,6 +64,7 @@ public class ContentProviderHolder implements Parcelable { } dest.writeStrongBinder(connection); dest.writeInt(noReleaseNeeded ? 1 : 0); + dest.writeInt(mLocal ? 1 : 0); } public static final @android.annotation.NonNull Parcelable.Creator<ContentProviderHolder> CREATOR @@ -81,5 +87,6 @@ public class ContentProviderHolder implements Parcelable { source.readStrongBinder()); connection = source.readStrongBinder(); noReleaseNeeded = source.readInt() != 0; + mLocal = source.readInt() != 0; } -}
\ No newline at end of file +} diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 6e9157e2a8c3..e5d4a766a89d 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -16,6 +16,7 @@ package android.app; +import android.app.ContentProviderHolder; import android.app.IInstrumentationWatcher; import android.app.IUiAutomationConnection; import android.app.ProfilerInfo; @@ -147,4 +148,6 @@ oneway interface IApplicationThread { void performDirectAction(IBinder activityToken, String actionId, in Bundle arguments, in RemoteCallback cancellationCallback, in RemoteCallback resultCallback); + void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth, + int userId, boolean published); } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index bd02210259b8..31c77eeb5424 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -963,7 +963,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { /** @hide */ public static final int LOCK_TASK_LAUNCH_MODE_ALWAYS = 2; /** @hide */ - public static final int LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED = 3; + public static final int LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED = 3; /** @hide */ public static final String lockTaskLaunchModeToString(int lockTaskLaunchMode) { @@ -974,8 +974,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { return "LOCK_TASK_LAUNCH_MODE_NEVER"; case LOCK_TASK_LAUNCH_MODE_ALWAYS: return "LOCK_TASK_LAUNCH_MODE_ALWAYS"; - case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - return "LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED"; + case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED: + return "LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED"; default: return "unknown=" + lockTaskLaunchMode; } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index df9db278e095..bed7b26034e5 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2075,7 +2075,8 @@ public class PackageInstaller { STAGED_SESSION_NO_ERROR, STAGED_SESSION_VERIFICATION_FAILED, STAGED_SESSION_ACTIVATION_FAILED, - STAGED_SESSION_UNKNOWN}) + STAGED_SESSION_UNKNOWN, + STAGED_SESSION_OTHER_ERROR}) @Retention(RetentionPolicy.SOURCE) public @interface StagedSessionErrorCode{} /** @@ -2101,6 +2102,12 @@ public class PackageInstaller { */ public static final int STAGED_SESSION_UNKNOWN = 3; + /** + * Constant indicating that a known error occurred while processing this staged session, but + * the error could not be matched to other categories. + */ + public static final int STAGED_SESSION_OTHER_ERROR = 4; + /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int sessionId; diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index fbe6a5052f3d..b0d449769be4 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -743,6 +743,12 @@ public abstract class BatteryStats implements Parcelable { @UnsupportedAppUsage public abstract ArrayMap<String, ? extends Pkg> getPackageStats(); + /** + * Returns the proportion of power consumed by the System Service + * calls made by this UID. + */ + public abstract double getProportionalSystemServiceUsage(); + public abstract ControllerActivityCounter getWifiControllerActivity(); public abstract ControllerActivityCounter getBluetoothControllerActivity(); public abstract ControllerActivityCounter getModemControllerActivity(); @@ -2882,6 +2888,17 @@ public abstract class BatteryStats implements Parcelable { public abstract int getDischargeAmountScreenDozeSinceCharge(); /** + * Returns the approximate CPU time (in microseconds) spent by the system server handling + * incoming service calls from apps. + * + * @param cluster the index of the CPU cluster. + * @param step the index of the CPU speed. This is not the actual speed of the CPU. + * @see com.android.internal.os.PowerProfile#getNumCpuClusters() + * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int) + */ + public abstract long getSystemServiceTimeAtCpuSpeed(int cluster, int step); + + /** * Returns the total, last, or current battery uptime in microseconds. * * @param curTime the elapsed realtime in microseconds. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0818abeff923..89178217366f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -23358,7 +23358,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * displaying, else return the result of calling through to the * super class. * - * @return boolean If true than the Drawable is being displayed in the + * @return boolean If true then the Drawable is being displayed in the * view; else false and it is not allowed to animate. * * @see #unscheduleDrawable(android.graphics.drawable.Drawable) diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 64ddb2f4d9d9..3f02d701f71f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1814,19 +1814,13 @@ public final class ViewRootImpl implements ViewParent, /** * Called after window layout to update the bounds surface. If the surface insets have changed * or the surface has resized, update the bounds surface. - * - * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl. */ - private void updateBoundsLayer(boolean shouldReparent) { + private void updateBoundsLayer() { if (mBoundsLayer != null) { setBoundsLayerCrop(); - mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(), - mSurface.getNextFrameNumber()); - - if (shouldReparent) { - mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl()); - } - mTransaction.apply(); + mTransaction.deferTransactionUntil(mBoundsLayer, + getRenderSurfaceControl(), mSurface.getNextFrameNumber()) + .apply(); } } @@ -2905,16 +2899,7 @@ public final class ViewRootImpl implements ViewParent, } if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) { - // If the surface has been replaced, there's a chance the bounds layer is not parented - // to the new layer. When updating bounds layer, also reparent to the main VRI - // SurfaceControl to ensure it's correctly placed in the hierarchy. - // - // This needs to be done on the client side since WMS won't reparent the children to the - // new surface if it thinks the app is closing. WMS gets the signal that the app is - // stopping, but on the client side it doesn't get stopped since it's restarted quick - // enough. WMS doesn't want to keep around old children since they will leak when the - // client creates new children. - updateBoundsLayer(surfaceReplaced); + updateBoundsLayer(); } final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl index e814ec649087..eb67191e5f54 100644 --- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl +++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl @@ -29,7 +29,7 @@ import android.view.accessibility.IWindowMagnificationConnectionCallback; oneway interface IWindowMagnificationConnection { /** - * Enables window magnification on specifed display with specified center and scale. + * Enables window magnification on specified display with given center and scale and animation. * * @param displayId The logical display id. * @param scale magnification scale. @@ -41,7 +41,7 @@ oneway interface IWindowMagnificationConnection { void enableWindowMagnification(int displayId, float scale, float centerX, float centerY); /** - * Sets the scale of the window magnifier on specifed display. + * Sets the scale of the window magnifier on specified display. * * @param displayId The logical display id. * @param scale magnification scale. @@ -49,14 +49,14 @@ oneway interface IWindowMagnificationConnection { void setScale(int displayId, float scale); /** - * Disables window magnification on specifed display. + * Disables window magnification on specified display with animation. * * @param displayId The logical display id. */ void disableWindowMagnification(int displayId); /** - * Moves the window magnifier on the specifed display. + * Moves the window magnifier on the specified display. It has no effect while animating. * * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in * current screen pixels. diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index c4eb39626d8b..7683067958d8 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -385,6 +385,7 @@ public class Editor { private final SuggestionHelper mSuggestionHelper = new SuggestionHelper(); private boolean mFlagCursorDragFromAnywhereEnabled; + private float mCursorDragDirectionMinXYRatio; private boolean mFlagInsertionHandleGesturesEnabled; // Specifies whether the new magnifier (with fish-eye effect) is enabled. @@ -425,6 +426,11 @@ public class Editor { mFlagCursorDragFromAnywhereEnabled = AppGlobals.getIntCoreSetting( WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT ? 1 : 0) != 0; + final int cursorDragMinAngleFromVertical = AppGlobals.getIntCoreSetting( + WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, + WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT); + mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio( + cursorDragMinAngleFromVertical); mFlagInsertionHandleGesturesEnabled = AppGlobals.getIntCoreSetting( WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT ? 1 : 0) != 0; @@ -437,6 +443,8 @@ public class Editor { if (TextView.DEBUG_CURSOR) { logCursor("Editor", "Cursor drag from anywhere is %s.", mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled"); + logCursor("Editor", "Cursor drag min angle from vertical is %d (= %f x/y ratio)", + cursorDragMinAngleFromVertical, mCursorDragDirectionMinXYRatio); logCursor("Editor", "Insertion handle gestures is %s.", mFlagInsertionHandleGesturesEnabled ? "enabled" : "disabled"); logCursor("Editor", "New magnifier is %s.", @@ -463,6 +471,11 @@ public class Editor { } @VisibleForTesting + public void setCursorDragMinAngleFromVertical(int degreesFromVertical) { + mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(degreesFromVertical); + } + + @VisibleForTesting public boolean getFlagInsertionHandleGesturesEnabled() { return mFlagInsertionHandleGesturesEnabled; } @@ -6127,10 +6140,11 @@ public class Editor { if (mIsDraggingCursor) { performCursorDrag(event); } else if (mFlagCursorDragFromAnywhereEnabled - && mTextView.getLayout() != null - && mTextView.isFocused() - && mTouchState.isMovedEnoughForDrag() - && !mTouchState.isDragCloseToVertical()) { + && mTextView.getLayout() != null + && mTextView.isFocused() + && mTouchState.isMovedEnoughForDrag() + && (mTouchState.getInitialDragDirectionXYRatio() + > mCursorDragDirectionMinXYRatio || mTouchState.isOnHandle())) { startCursorDrag(event); } break; diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java index 9eb63087a66e..751436865ff5 100644 --- a/core/java/android/widget/EditorTouchState.java +++ b/core/java/android/widget/EditorTouchState.java @@ -59,7 +59,7 @@ public class EditorTouchState { private boolean mMultiTapInSameArea; private boolean mMovedEnoughForDrag; - private boolean mIsDragCloseToVertical; + private float mInitialDragDirectionXYRatio; public float getLastDownX() { return mLastDownX; @@ -98,8 +98,23 @@ public class EditorTouchState { return mMovedEnoughForDrag; } - public boolean isDragCloseToVertical() { - return mIsDragCloseToVertical && !mIsOnHandle; + /** + * When {@link #isMovedEnoughForDrag()} is {@code true}, this function returns the x/y ratio for + * the initial drag direction. Smaller values indicate that the direction is closer to vertical, + * while larger values indicate that the direction is closer to horizontal. For example: + * <ul> + * <li>if the drag direction is exactly vertical, this returns 0 + * <li>if the drag direction is exactly horizontal, this returns {@link Float#MAX_VALUE} + * <li>if the drag direction is 45 deg from vertical, this returns 1 + * <li>if the drag direction is 30 deg from vertical, this returns 0.58 (x delta is smaller + * than y delta) + * <li>if the drag direction is 60 deg from vertical, this returns 1.73 (x delta is bigger + * than y delta) + * </ul> + * This function never returns negative values, regardless of the direction of the drag. + */ + public float getInitialDragDirectionXYRatio() { + return mInitialDragDirectionXYRatio; } public void setIsOnHandle(boolean onHandle) { @@ -155,7 +170,7 @@ public class EditorTouchState { mLastDownY = event.getY(); mLastDownMillis = event.getEventTime(); mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } else if (action == MotionEvent.ACTION_UP) { if (TextView.DEBUG_CURSOR) { logCursor("EditorTouchState", "ACTION_UP"); @@ -164,7 +179,7 @@ public class EditorTouchState { mLastUpY = event.getY(); mLastUpMillis = event.getEventTime(); mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } else if (action == MotionEvent.ACTION_MOVE) { if (!mMovedEnoughForDrag) { float deltaX = event.getX() - mLastDownX; @@ -174,9 +189,8 @@ public class EditorTouchState { int touchSlop = config.getScaledTouchSlop(); mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop; if (mMovedEnoughForDrag) { - // If the direction of the swipe motion is within 45 degrees of vertical, it is - // considered a vertical drag. - mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY); + mInitialDragDirectionXYRatio = (deltaY == 0) ? Float.MAX_VALUE : + Math.abs(deltaX / deltaY); } } } else if (action == MotionEvent.ACTION_CANCEL) { @@ -185,7 +199,7 @@ public class EditorTouchState { mMultiTapStatus = MultiTapStatus.NONE; mMultiTapInSameArea = false; mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } } @@ -201,4 +215,27 @@ public class EditorTouchState { float distanceSquared = (deltaX * deltaX) + (deltaY * deltaY); return distanceSquared <= maxDistance * maxDistance; } + + /** + * Returns the x/y ratio corresponding to the given angle relative to vertical. Smaller angle + * values (ie, closer to vertical) will result in a smaller x/y ratio. For example: + * <ul> + * <li>if the angle is 45 deg, the ratio is 1 + * <li>if the angle is 30 deg, the ratio is 0.58 (x delta is smaller than y delta) + * <li>if the angle is 60 deg, the ratio is 1.73 (x delta is bigger than y delta) + * </ul> + * If the passed-in value is <= 0, this function returns 0. If the passed-in value is >= 90, + * this function returns {@link Float#MAX_VALUE}. + * + * @see #getInitialDragDirectionXYRatio() + */ + public static float getXYRatio(int angleFromVerticalInDegrees) { + if (angleFromVerticalInDegrees <= 0) { + return 0.0f; + } + if (angleFromVerticalInDegrees >= 90) { + return Float.MAX_VALUE; + } + return (float) Math.tan(Math.toRadians(angleFromVerticalInDegrees)); + } } diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java index 832dd5190d37..1a493653d811 100644 --- a/core/java/android/widget/WidgetFlags.java +++ b/core/java/android/widget/WidgetFlags.java @@ -41,6 +41,28 @@ public final class WidgetFlags { public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true; /** + * Threshold for the direction of a swipe gesture in order for it to be handled as a cursor drag + * rather than a scroll. The direction angle of the swipe gesture must exceed this value in + * order to trigger cursor drag; otherwise, the swipe will be assumed to be a scroll gesture. + * The value units for this flag is degrees and the valid range is [0,90] inclusive. If a value + * < 0 is set, 0 will be used instead; if a value > 90 is set, 90 will be used instead. + */ + public static final String CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL = + "CursorControlFeature__min_angle_from_vertical_to_start_cursor_drag"; + + /** + * The key used in app core settings for the flag + * {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}. + */ + public static final String KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL = + "widget__min_angle_from_vertical_to_start_cursor_drag"; + + /** + * Default value for the flag {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}. + */ + public static final int CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT = 45; + + /** * The flag of finger-to-cursor distance in DP for cursor dragging. * The value unit is DP and the range is {0..100}. If the value is out of range, the legacy * value, which is based on handle size, will be used. diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 3a89dcd96487..49ad81b2bbc8 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -3134,7 +3134,9 @@ public class ChooserActivity extends ResolverActivity implements // ends up disabled. That's because at some point the old tab's vertical scrolling is // disabled and the new tab's is enabled. For context, see b/159997845 setVerticalScrollEnabled(true); - mResolverDrawerLayout.scrollNestedScrollableChildBackToTop(); + if (mResolverDrawerLayout != null) { + mResolverDrawerLayout.scrollNestedScrollableChildBackToTop(); + } } @Override diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index ea3d2de13ce6..eb59f0f59be1 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -123,10 +123,15 @@ public final class SystemUiDeviceConfigFlags { // Flag related to Privacy Indicators /** - * Whether the Permissions Hub is showing. + * Whether to show the complete ongoing app ops chip. */ public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled"; + /** + * Whether to show app ops chip for just microphone + camera. + */ + public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled"; + // Flags related to Assistant /** diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index b3ea118d9c73..2620ba0749a9 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -129,6 +129,7 @@ public class BatterySipper implements Comparable<BatterySipper> { public double videoPowerMah; public double wakeLockPowerMah; public double wifiPowerMah; + public double systemServiceCpuPowerMah; // **************** // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto) @@ -242,6 +243,7 @@ public class BatterySipper implements Comparable<BatterySipper> { videoPowerMah += other.videoPowerMah; proportionalSmearMah += other.proportionalSmearMah; totalSmearedPowerMah += other.totalSmearedPowerMah; + systemServiceCpuPowerMah += other.systemServiceCpuPowerMah; } /** @@ -253,7 +255,8 @@ public class BatterySipper implements Comparable<BatterySipper> { public double sumPower() { totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah + - flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah; + flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah + + systemServiceCpuPowerMah; totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah; return totalPowerMah; diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index b131ab83cc79..3dfa3c3f6906 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -133,6 +133,7 @@ public class BatteryStatsHelper { private double mMaxDrainedPower; PowerCalculator mCpuPowerCalculator; + SystemServicePowerCalculator mSystemServicePowerCalculator; PowerCalculator mWakelockPowerCalculator; MobileRadioPowerCalculator mMobileRadioPowerCalculator; PowerCalculator mWifiPowerCalculator; @@ -396,6 +397,11 @@ public class BatteryStatsHelper { } mCpuPowerCalculator.reset(); + if (mSystemServicePowerCalculator == null) { + mSystemServicePowerCalculator = new SystemServicePowerCalculator(mPowerProfile, mStats); + } + mSystemServicePowerCalculator.reset(); + if (mMemoryPowerCalculator == null) { mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile); } @@ -588,6 +594,8 @@ public class BatteryStatsHelper { mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); + mSystemServicePowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, + mStatsType); final double totalPower = app.sumPower(); if (DEBUG && totalPower != 0) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 58ba16bc61dd..84981515e133 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -144,6 +144,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final boolean DEBUG = false; public static final boolean DEBUG_ENERGY = false; private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY; + private static final boolean DEBUG_BINDER_STATS = true; private static final boolean DEBUG_MEMORY = false; private static final boolean DEBUG_HISTORY = false; private static final boolean USE_OLD_HISTORY = false; // for debugging. @@ -154,7 +155,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 186 + (USE_OLD_HISTORY ? 1000 : 0); + static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0); // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -218,10 +219,13 @@ public class BatteryStatsImpl extends BatteryStats { new KernelCpuUidClusterTimeReader(true); @VisibleForTesting protected KernelSingleUidTimeReader mKernelSingleUidTimeReader; + @VisibleForTesting + protected SystemServerCpuThreadReader mSystemServerCpuThreadReader; private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats(); private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>(); + public LongSparseArray<SamplingTimer> getKernelMemoryStats() { return mKernelMemoryStats; } @@ -267,6 +271,7 @@ public class BatteryStatsImpl extends BatteryStats { /** Container for Rail Energy Data stats. */ private final RailStats mTmpRailStats = new RailStats(); + /** * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader}, * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader}, @@ -1007,6 +1012,16 @@ public class BatteryStatsImpl extends BatteryStats { private long[] mCpuFreqs; + /** + * Times spent by the system server threads grouped by cluster and CPU speed. + */ + private LongSamplingCounter[][] mSystemServerThreadCpuTimesUs; + + /** + * Times spent by the system server threads handling incoming binder requests. + */ + private LongSamplingCounter[][] mBinderThreadCpuTimesUs; + @VisibleForTesting protected PowerProfile mPowerProfile; @@ -6131,10 +6146,77 @@ public class BatteryStatsImpl extends BatteryStats { * the power consumption to the calling app. */ public void noteBinderCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats) { + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) { synchronized (this) { getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount, callStats); + mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids); + } + } + + /** + * Estimates the proportion of system server CPU activity handling incoming binder calls + * that can be attributed to each app + */ + @VisibleForTesting + public void updateSystemServiceCallStats() { + // Start off by computing the average duration of recorded binder calls, + // regardless of which binder or transaction. We will use this as a fallback + // for calls that were not sampled at all. + int totalRecordedCallCount = 0; + long totalRecordedCallTimeMicros = 0; + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats; + for (int j = binderCallStats.size() - 1; j >= 0; j--) { + BinderCallStats stats = binderCallStats.valueAt(j); + totalRecordedCallCount += stats.recordedCallCount; + totalRecordedCallTimeMicros += stats.recordedCpuTimeMicros; + } + } + + long totalSystemServiceTimeMicros = 0; + + // For every UID, use recorded durations of sampled binder calls to estimate + // the total time the system server spent handling requests from this UID. + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + + long totalTimeForUid = 0; + int totalCallCountForUid = 0; + ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats; + for (int j = binderCallStats.size() - 1; j >= 0; j--) { + BinderCallStats stats = binderCallStats.valueAt(j); + totalCallCountForUid += stats.callCount; + if (stats.recordedCallCount > 0) { + totalTimeForUid += + stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount; + } else if (totalRecordedCallCount > 0) { + totalTimeForUid += + stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount; + } + } + + if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) { + // Estimate remaining calls, which were not tracked because of binder call + // stats sampling + totalTimeForUid += + (uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros + / totalRecordedCallCount; + } + + uid.mSystemServiceTimeUs = totalTimeForUid; + totalSystemServiceTimeMicros += totalTimeForUid; + } + + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + if (totalSystemServiceTimeMicros > 0) { + uid.mProportionalSystemServiceUsage = + (double) uid.mSystemServiceTimeUs / totalSystemServiceTimeMicros; + } else { + uid.mProportionalSystemServiceUsage = 0; + } } } @@ -6583,7 +6665,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * Accumulates stats for a specific binder transaction. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @VisibleForTesting protected static class BinderCallStats { static final Comparator<BinderCallStats> COMPARATOR = Comparator.comparing(BinderCallStats::getClassName) @@ -6822,6 +6904,16 @@ public class BatteryStatsImpl extends BatteryStats { */ private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>(); + /** + * Estimated total time spent by the system server handling requests from this uid. + */ + private long mSystemServiceTimeUs; + + /** + * Estimated proportion of system server binder call CPU cost for this uid. + */ + private double mProportionalSystemServiceUsage; + public Uid(BatteryStatsImpl bsi, int uid) { mBsi = bsi; mUid = uid; @@ -6899,7 +6991,6 @@ public class BatteryStatsImpl extends BatteryStats { return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED); } - @Override public long[] getCpuFreqTimes(int which, int procState) { if (which < 0 || which >= NUM_PROCESS_STATE) { @@ -6934,10 +7025,16 @@ public class BatteryStatsImpl extends BatteryStats { return mBinderCallCount; } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public ArraySet<BinderCallStats> getBinderCallStats() { return mBinderCallStats; } + @Override + public double getProportionalSystemServiceUsage() { + return mProportionalSystemServiceUsage; + } + public void addIsolatedUid(int isolatedUid) { if (mChildUids == null) { mChildUids = new IntArray(); @@ -8029,9 +8126,12 @@ public class BatteryStatsImpl extends BatteryStats { mBinderCallCount = 0; mBinderCallStats.clear(); + mProportionalSystemServiceUsage = 0; + mLastStepUserTime = mLastStepSystemTime = 0; mCurStepUserTime = mCurStepSystemTime = 0; + return !active; } @@ -8373,28 +8473,7 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime.writeToParcel(out); mSystemCpuTime.writeToParcel(out); - if (mCpuClusterSpeedTimesUs != null) { - out.writeInt(1); - out.writeInt(mCpuClusterSpeedTimesUs.length); - for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) { - if (cpuSpeeds != null) { - out.writeInt(1); - out.writeInt(cpuSpeeds.length); - for (LongSamplingCounter c : cpuSpeeds) { - if (c != null) { - out.writeInt(1); - c.writeToParcel(out); - } else { - out.writeInt(0); - } - } - } else { - out.writeInt(0); - } - } - } else { - out.writeInt(0); - } + mBsi.writeCpuSpeedCountersToParcel(out, mCpuClusterSpeedTimesUs); LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs); LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs); @@ -8432,6 +8511,7 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + out.writeDouble(mProportionalSystemServiceUsage); } void readJobCompletionsFromParcelLocked(Parcel in) { @@ -8692,36 +8772,7 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); - if (in.readInt() != 0) { - int numCpuClusters = in.readInt(); - if (mBsi.mPowerProfile != null && mBsi.mPowerProfile.getNumCpuClusters() != numCpuClusters) { - throw new ParcelFormatException("Incompatible number of cpu clusters"); - } - - mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][]; - for (int cluster = 0; cluster < numCpuClusters; cluster++) { - if (in.readInt() != 0) { - int numSpeeds = in.readInt(); - if (mBsi.mPowerProfile != null && - mBsi.mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) { - throw new ParcelFormatException("Incompatible number of cpu speeds"); - } - - final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds]; - mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds; - for (int speed = 0; speed < numSpeeds; speed++) { - if (in.readInt() != 0) { - cpuSpeeds[speed] = new LongSamplingCounter( - mBsi.mOnBatteryTimeBase, in); - } - } - } else { - mCpuClusterSpeedTimesUs[cluster] = null; - } - } - } else { - mCpuClusterSpeedTimesUs = null; - } + mCpuClusterSpeedTimesUs = mBsi.readCpuSpeedCountersFromParcel(in); mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase); mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel( @@ -8762,6 +8813,8 @@ public class BatteryStatsImpl extends BatteryStats { } else { mWifiRadioApWakeupCount = null; } + + mProportionalSystemServiceUsage = in.readDouble(); } public void noteJobsDeferredLocked(int numDeferred, long sinceLast) { @@ -9904,7 +9957,6 @@ public class BatteryStatsImpl extends BatteryStats { UserInfoProvider userInfoProvider) { init(clocks); - if (systemDir == null) { mStatsFile = null; mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); @@ -10046,6 +10098,8 @@ public class BatteryStatsImpl extends BatteryStats { firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i); } + mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create(); + if (mEstimatedBatteryCapacity == -1) { // Initialize the estimated battery capacity to a known preset one. mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity(); @@ -10726,6 +10780,9 @@ public class BatteryStatsImpl extends BatteryStats { mTmpRailStats.reset(); + resetIfNotNull(mSystemServerThreadCpuTimesUs, false); + resetIfNotNull(mBinderThreadCpuTimesUs, false); + mLastHistoryStepDetails = null; mLastStepCpuUserTime = mLastStepCpuSystemTime = 0; mCurStepCpuUserTime = mCurStepCpuSystemTime = 0; @@ -10853,7 +10910,7 @@ public class BatteryStatsImpl extends BatteryStats { return null; } - /** + /** * Distribute WiFi energy info and network traffic to apps. * @param info The energy information from the WiFi controller. */ @@ -11772,6 +11829,7 @@ public class BatteryStatsImpl extends BatteryStats { for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) { mKernelCpuSpeedReaders[cluster].readDelta(); } + mSystemServerCpuThreadReader.readDelta(); return; } @@ -11791,6 +11849,87 @@ public class BatteryStatsImpl extends BatteryStats { readKernelUidCpuClusterTimesLocked(onBattery); mNumAllUidCpuTimeReads += 2; } + + updateSystemServerThreadStats(); + } + + /** + * Estimates the proportion of the System Server CPU activity (per cluster per speed) + * spent on handling incoming binder calls. + */ + @VisibleForTesting + public void updateSystemServerThreadStats() { + // There are some simplifying assumptions made in this algorithm + // 1) We assume that if a thread handles incoming binder calls, all of its activity + // is spent doing that. Most incoming calls are handled by threads allocated + // by the native layer in the binder thread pool, so this assumption is reasonable. + // 2) We use the aggregate CPU time spent in different threads as a proxy for the CPU + // cost. In reality, in multi-core CPUs, the CPU cost may not be linearly + // affected by additional threads. + + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + mSystemServerCpuThreadReader.readDelta(); + + int index = 0; + int numCpuClusters = mPowerProfile.getNumCpuClusters(); + if (mSystemServerThreadCpuTimesUs == null) { + mSystemServerThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][]; + mBinderThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][]; + } + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + if (mSystemServerThreadCpuTimesUs[cluster] == null) { + mSystemServerThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds]; + mBinderThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds]; + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServerThreadCpuTimesUs[cluster][speed] = + new LongSamplingCounter(mOnBatteryTimeBase); + mBinderThreadCpuTimesUs[cluster][speed] = + new LongSamplingCounter(mOnBatteryTimeBase); + } + } + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServerThreadCpuTimesUs[cluster][speed].addCountLocked( + systemServiceCpuThreadTimes.threadCpuTimesUs[index]); + mBinderThreadCpuTimesUs[cluster][speed].addCountLocked( + systemServiceCpuThreadTimes.binderThreadCpuTimesUs[index]); + index++; + } + } + if (DEBUG_BINDER_STATS) { + Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)"); + long binderThreadTime = 0; + long totalThreadTime = 0; + int cpuIndex = 0; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + StringBuilder sb = new StringBuilder(); + sb.append("cpu").append(cpuIndex).append(": ["); + int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < numSpeeds; speed++) { + if (speed != 0) { + sb.append(", "); + } + long totalCount = mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked( + 0) / 1000; + long binderCount = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0) + / 1000; + sb.append(String.format("%d/%d(%.1f%%)", + binderCount, + totalCount, + totalCount != 0 ? (double) binderCount * 100 / totalCount : 0)); + + totalThreadTime += totalCount; + binderThreadTime += binderCount; + index++; + } + cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster); + Slog.d(TAG, sb.toString()); + } + Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTime); + Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)", + binderThreadTime, + binderThreadTime != 0 ? (double) binderThreadTime * 100 / totalThreadTime : 0)); + } } /** @@ -12998,6 +13137,75 @@ public class BatteryStatsImpl extends BatteryStats { } } + + @Override + public long getSystemServiceTimeAtCpuSpeed(int cluster, int step) { + // Estimates the time spent by the system server handling incoming binder requests. + // + // The data that we can get from the kernel is this: + // - CPU duration for a (thread - cluster - CPU speed) combination + // - CPU duration for a (UID - cluster - CPU speed) combination + // + // The configuration we have in the Power Profile is this: + // - Average CPU power for a (cluster - CPU speed) combination. + // + // The model used by BatteryStats can be illustrated with this example: + // + // - Let's say the system server has 10 threads. + // - These 10 threads spent 1000 ms of CPU time in aggregate + // - Of the 10 threads 4 were execute exclusively incoming binder calls. + // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate + // - The real time spent by the system server UID doing all of this is, say, 200 ms. + // + // We will assume that power consumption is proportional to the time spent by the CPU + // across all threads. This is a crude assumption, but we don't have more detailed data. + // Thus, + // binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime + // + // In our example, + // binderRealTime = 200 * 600 / 1000 = 120ms + // + // We can then multiply this estimated time by the average power to obtain an estimate + // of the total power consumed by incoming binder calls for the given cluster/speed + // combination. + + if (mSystemServerThreadCpuTimesUs == null) { + return 0; + } + + if (cluster < 0 || cluster >= mSystemServerThreadCpuTimesUs.length) { + return 0; + } + + final LongSamplingCounter[] threadTimesForCluster = mSystemServerThreadCpuTimesUs[cluster]; + + if (step < 0 || step >= threadTimesForCluster.length) { + return 0; + } + + Uid systemUid = mUidStats.get(Process.SYSTEM_UID); + if (systemUid == null) { + return 0; + } + + final long uidTimeAtCpuSpeed = systemUid.getTimeAtCpuSpeed(cluster, step, + BatteryStats.STATS_SINCE_CHARGED); + if (uidTimeAtCpuSpeed == 0) { + return 0; + } + + final long uidThreadTime = + threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); + + if (uidThreadTime == 0) { + return 0; + } + + final long binderThreadTime = mBinderThreadCpuTimesUs[cluster][step].getCountLocked( + BatteryStats.STATS_SINCE_CHARGED); + return uidTimeAtCpuSpeed * binderThreadTime / uidThreadTime; + } + /** * Retrieve the statistics object for a particular uid, creating if needed. */ @@ -13327,45 +13535,7 @@ public class BatteryStatsImpl extends BatteryStats { pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" "); pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000); } - pw.println("Per UID system service calls:"); - BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver(); - for (int i = 0; i < size; i++) { - int u = mUidStats.keyAt(i); - Uid uid = mUidStats.get(u); - long binderCallCount = uid.getBinderCallCount(); - if (binderCallCount != 0) { - pw.print(" "); - pw.print(u); - pw.print(" system service calls: "); - pw.print(binderCallCount); - ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats(); - if (!binderCallStats.isEmpty()) { - pw.println(", including"); - BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()]; - binderCallStats.toArray(bcss); - for (BinderCallStats bcs : bcss) { - bcs.ensureMethodName(nameResolver); - } - Arrays.sort(bcss, BinderCallStats.COMPARATOR); - for (BinderCallStats callStats : bcss) { - pw.print(" "); - pw.print(callStats.getClassName()); - pw.print('#'); - pw.print(callStats.getMethodName()); - pw.print(" calls: "); - pw.print(callStats.callCount); - if (callStats.recordedCallCount != 0) { - pw.print(" time: "); - pw.print(callStats.callCount * callStats.recordedCpuTimeMicros - / callStats.recordedCallCount / 1000); - } - pw.println(); - } - } else { - pw.println(); - } - } - } + pw.println("Per UID CPU active time in ms:"); for (int i = 0; i < size; i++) { int u = mUidStats.keyAt(i); @@ -13390,6 +13560,30 @@ public class BatteryStatsImpl extends BatteryStats { pw.print(" "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times)); } } + + updateSystemServiceCallStats(); + if (mSystemServerThreadCpuTimesUs != null) { + pw.println("Per UID System server binder time in ms:"); + for (int i = 0; i < size; i++) { + int u = mUidStats.keyAt(i); + Uid uid = mUidStats.get(u); + double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage(); + + long time = 0; + for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) { + int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length; + for (int speed = 0; speed < numSpeeds; speed++) { + time += getSystemServiceTimeAtCpuSpeed(cluster, speed) + * proportionalSystemServiceUsage; + } + } + + pw.print(" "); + pw.print(u); + pw.print(": "); + pw.println(time); + } + } } final ReentrantLock mWriteLock = new ReentrantLock(); @@ -14908,6 +15102,9 @@ public class BatteryStatsImpl extends BatteryStats { u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in); mUidStats.append(uid, u); } + + mSystemServerThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in); + mBinderThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in); } public void writeToParcel(Parcel out, int flags) { @@ -14923,6 +15120,8 @@ public class BatteryStatsImpl extends BatteryStats { // Need to update with current kernel wake lock counts. pullPendingStateUpdatesLocked(); + updateSystemServiceCallStats(); + // Pull the clock time. This may update the time and make a new history entry // if we had originally pulled a time before the RTC was set. getStartClockTime(); @@ -15105,6 +15304,73 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + writeCpuSpeedCountersToParcel(out, mSystemServerThreadCpuTimesUs); + writeCpuSpeedCountersToParcel(out, mBinderThreadCpuTimesUs); + } + + private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) { + if (counters == null) { + out.writeInt(0); + return; + } + + out.writeInt(1); + out.writeInt(counters.length); + for (int i = 0; i < counters.length; i++) { + LongSamplingCounter[] counterArray = counters[i]; + if (counterArray == null) { + out.writeInt(0); + continue; + } + + out.writeInt(1); + out.writeInt(counterArray.length); + for (int j = 0; j < counterArray.length; j++) { + LongSamplingCounter c = counterArray[j]; + if (c != null) { + out.writeInt(1); + c.writeToParcel(out); + } else { + out.writeInt(0); + } + } + } + } + + private LongSamplingCounter[][] readCpuSpeedCountersFromParcel(Parcel in) { + LongSamplingCounter[][] counters; + if (in.readInt() != 0) { + int numCpuClusters = in.readInt(); + if (mPowerProfile != null + && mPowerProfile.getNumCpuClusters() != numCpuClusters) { + throw new ParcelFormatException("Incompatible number of cpu clusters"); + } + + counters = new LongSamplingCounter[numCpuClusters][]; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + if (in.readInt() != 0) { + int numSpeeds = in.readInt(); + if (mPowerProfile != null + && mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) { + throw new ParcelFormatException("Incompatible number of cpu speeds"); + } + + final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds]; + counters[cluster] = cpuSpeeds; + for (int speed = 0; speed < numSpeeds; speed++) { + if (in.readInt() != 0) { + cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in); + } + } + } else { + counters[cluster] = null; + } + } + } else { + counters = null; + } + + return counters; } @UnsupportedAppUsage diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index e09ef49acd10..201626abd820 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -115,7 +115,8 @@ public class BinderCallsStats implements BinderInternal.Observer { if (uidEntry != null) { ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats; mCallStatsObserver.noteCallStats(uidEntry.workSourceUid, - uidEntry.incrementalCallCount, callStats.values()); + uidEntry.incrementalCallCount, callStats.values(), + mNativeTids.toArray()); uidEntry.incrementalCallCount = 0; for (int j = callStats.size() - 1; j >= 0; j--) { callStats.valueAt(j).incrementalCallCount = 0; diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index feb5aab94adc..f14d5f2bbbeb 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -142,7 +142,8 @@ public class BinderInternal { * Notes incoming binder call stats associated with this work source UID. */ void noteCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats); + Collection<BinderCallsStats.CallStat> callStats, + int[] binderThreadNativeTids); } /** diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 34076700cd95..2ba372a47cf3 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -225,19 +225,22 @@ public class KernelCpuThreadReader { /** Set the number of frequency buckets to use */ void setNumBuckets(int numBuckets) { - if (numBuckets < 1) { - Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets); - return; - } // If `numBuckets` hasn't changed since the last set, do nothing if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) { return; } - mFrequencyBucketCreator = - new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets); - mFrequenciesKhz = - mFrequencyBucketCreator.bucketFrequencies( - mProcTimeInStateReader.getFrequenciesKhz()); + + final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz(); + if (numBuckets != 0) { + mFrequencyBucketCreator = new FrequencyBucketCreator(frequenciesKhz, numBuckets); + mFrequenciesKhz = mFrequencyBucketCreator.bucketFrequencies(frequenciesKhz); + } else { + mFrequencyBucketCreator = null; + mFrequenciesKhz = new int[frequenciesKhz.length]; + for (int i = 0; i < frequenciesKhz.length; i++) { + mFrequenciesKhz[i] = (int) frequenciesKhz[i]; + } + } } /** Set the UID predicate for {@link #getProcessCpuUsage} */ @@ -320,8 +323,15 @@ public class KernelCpuThreadReader { if (cpuUsagesLong == null) { return null; } - int[] cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong); - + final int[] cpuUsages; + if (mFrequencyBucketCreator != null) { + cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong); + } else { + cpuUsages = new int[cpuUsagesLong.length]; + for (int i = 0; i < cpuUsagesLong.length; i++) { + cpuUsages[i] = (int) cpuUsagesLong[i]; + } + } return new ThreadCpuUsage(threadId, threadName, cpuUsages); } diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java new file mode 100644 index 000000000000..1cdd42c7403e --- /dev/null +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -0,0 +1,151 @@ +/* + * 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.internal.os; + +import android.os.Process; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage + * by various threads of the System Server. + */ +public class SystemServerCpuThreadReader { + private KernelCpuThreadReader mKernelCpuThreadReader; + private int[] mBinderThreadNativeTids; + + private int[] mThreadCpuTimesUs; + private int[] mBinderThreadCpuTimesUs; + private long[] mLastThreadCpuTimesUs; + private long[] mLastBinderThreadCpuTimesUs; + + /** + * Times (in microseconds) spent by the system server UID. + */ + public static class SystemServiceCpuThreadTimes { + // All threads + public long[] threadCpuTimesUs; + // Just the threads handling incoming binder calls + public long[] binderThreadCpuTimesUs; + } + + private SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = new SystemServiceCpuThreadTimes(); + + /** + * Creates a configured instance of SystemServerCpuThreadReader. + */ + public static SystemServerCpuThreadReader create() { + return new SystemServerCpuThreadReader( + KernelCpuThreadReader.create(0, uid -> uid == Process.myUid())); + } + + @VisibleForTesting + public SystemServerCpuThreadReader(Path procPath, int systemServerUid) throws IOException { + this(new KernelCpuThreadReader(0, uid -> uid == systemServerUid, null, null, + new KernelCpuThreadReader.Injector() { + @Override + public int getUidForPid(int pid) { + return systemServerUid; + } + })); + } + + @VisibleForTesting + public SystemServerCpuThreadReader(KernelCpuThreadReader kernelCpuThreadReader) { + mKernelCpuThreadReader = kernelCpuThreadReader; + } + + public void setBinderThreadNativeTids(int[] nativeTids) { + mBinderThreadNativeTids = nativeTids; + } + + /** + * Returns delta of CPU times, per thread, since the previous call to this method. + */ + public SystemServiceCpuThreadTimes readDelta() { + if (mBinderThreadCpuTimesUs == null) { + int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz().length; + mThreadCpuTimesUs = new int[numCpuFrequencies]; + mBinderThreadCpuTimesUs = new int[numCpuFrequencies]; + + mLastThreadCpuTimesUs = new long[numCpuFrequencies]; + mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies]; + + mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies]; + } + + Arrays.fill(mThreadCpuTimesUs, 0); + Arrays.fill(mBinderThreadCpuTimesUs, 0); + + ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsage = + mKernelCpuThreadReader.getProcessCpuUsage(); + int processCpuUsageSize = processCpuUsage.size(); + for (int i = 0; i < processCpuUsageSize; i++) { + KernelCpuThreadReader.ProcessCpuUsage pcu = processCpuUsage.get(i); + ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = pcu.threadCpuUsages; + if (threadCpuUsages != null) { + int threadCpuUsagesSize = threadCpuUsages.size(); + for (int j = 0; j < threadCpuUsagesSize; j++) { + KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j); + boolean isBinderThread = isBinderThread(tcu.threadId); + + final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length); + for (int k = 0; k < len; k++) { + int usageTimeUs = tcu.usageTimesMillis[k] * 1000; + mThreadCpuTimesUs[k] += usageTimeUs; + if (isBinderThread) { + mBinderThreadCpuTimesUs[k] += usageTimeUs; + } + } + } + } + } + + for (int i = 0; i < mThreadCpuTimesUs.length; i++) { + if (mThreadCpuTimesUs[i] < mLastThreadCpuTimesUs[i]) { + mDeltaCpuThreadTimes.threadCpuTimesUs[i] = mThreadCpuTimesUs[i]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i]; + } else { + mDeltaCpuThreadTimes.threadCpuTimesUs[i] = + mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = + mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i]; + } + mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i]; + mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i]; + } + + return mDeltaCpuThreadTimes; + } + + private boolean isBinderThread(int threadId) { + if (mBinderThreadNativeTids != null) { + for (int i = 0; i < mBinderThreadNativeTids.length; i++) { + if (threadId == mBinderThreadNativeTids[i]) { + return true; + } + } + } + return false; + } +} diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java new file mode 100644 index 000000000000..481b901b3c69 --- /dev/null +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -0,0 +1,91 @@ +/* + * 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.internal.os; + +import android.os.BatteryStats; +import android.util.Log; + +import java.util.Arrays; + +/** + * Estimates the amount of power consumed by the System Server handling requests from + * a given app. + */ +public class SystemServicePowerCalculator extends PowerCalculator { + private static final boolean DEBUG = false; + private static final String TAG = "SystemServicePowerCalc"; + + private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000; + + private final PowerProfile mPowerProfile; + private final BatteryStats mBatteryStats; + // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds + private double[][] mSystemServicePowerMaUs; + + public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) { + mPowerProfile = powerProfile; + mBatteryStats = batteryStats; + } + + @Override + public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { + final double proportionalUsage = u.getProportionalSystemServiceUsage(); + if (proportionalUsage > 0) { + if (mSystemServicePowerMaUs == null) { + updateSystemServicePower(); + } + + double cpuPowerMaUs = 0; + int numCpuClusters = mPowerProfile.getNumCpuClusters(); + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < numSpeeds; speed++) { + cpuPowerMaUs += mSystemServicePowerMaUs[cluster][speed] * proportionalUsage; + } + } + + app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; + } + } + + private void updateSystemServicePower() { + final int numCpuClusters = mPowerProfile.getNumCpuClusters(); + mSystemServicePowerMaUs = new double[numCpuClusters][]; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + mSystemServicePowerMaUs[cluster] = new double[numSpeeds]; + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServicePowerMaUs[cluster][speed] = + mBatteryStats.getSystemServiceTimeAtCpuSpeed(cluster, speed) + * mPowerProfile.getAveragePowerForCpuCore(cluster, speed); + } + } + if (DEBUG) { + Log.d(TAG, "System service power per CPU cluster and frequency"); + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + Log.d(TAG, "Cluster[" + cluster + "]: " + + Arrays.toString(mSystemServicePowerMaUs[cluster])); + } + } + } + + @Override + public void reset() { + mSystemServicePowerMaUs = null; + } +} diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java index ba60fa590792..b42ea7d0b769 100644 --- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java +++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java @@ -16,14 +16,8 @@ package com.android.internal.os.logging; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.util.Pair; import android.view.WindowManager.LayoutParams; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; /** @@ -32,81 +26,6 @@ import com.android.internal.util.FrameworkStatsLog; */ public class MetricsLoggerWrapper { - private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0; - private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1; - - public static void logPictureInPictureDismissByTap(Context context, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED, - METRIC_VALUE_DISMISSED_BY_TAP); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED); - } - - public static void logPictureInPictureDismissByDrag(Context context, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, - MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED, - METRIC_VALUE_DISMISSED_BY_DRAG); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED); - } - - public static void logPictureInPictureMinimize(Context context, boolean isMinimized, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED, - isMinimized); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED); - } - - /** - * Get uid from component name and user Id - * @return uid. -1 if not found. - */ - private static int getUid(Context context, ComponentName componentName, int userId) { - int uid = -1; - if (componentName == null) { - return uid; - } - try { - uid = context.getPackageManager().getApplicationInfoAsUser( - componentName.getPackageName(), 0, userId).uid; - } catch (NameNotFoundException e) { - } - return uid; - } - - public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) { - MetricsLogger.visibility(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU, - menuStateFull); - } - - public static void logPictureInPictureEnter(Context context, - int uid, String shortComponentName, boolean supportsEnterPipOnTaskSwitch) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED, - supportsEnterPipOnTaskSwitch); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, uid, - shortComponentName, - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED); - } - - public static void logPictureInPictureFullScreen(Context context, int uid, - String shortComponentName) { - MetricsLogger.action(context, - MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - uid, - shortComponentName, - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN); - } - public static void logAppOverlayEnter(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) { if (changed) { if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) { diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 7daefd3e6544..e715be21f146 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -95,8 +95,8 @@ void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDispla ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking vsync handler.", this); - env->CallVoidMethod(receiverObj.get(), - gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count); + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync, + timestamp, displayId.value, count); ALOGV("receiver %p ~ Returned from vsync handler.", this); } @@ -110,8 +110,8 @@ void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisp ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking hotplug handler.", this); - env->CallVoidMethod(receiverObj.get(), - gDisplayEventReceiverClassInfo.dispatchHotplug, timestamp, displayId, connected); + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchHotplug, + timestamp, displayId.value, connected); ALOGV("receiver %p ~ Returned from hotplug handler.", this); } @@ -126,9 +126,8 @@ void NativeDisplayEventReceiver::dispatchConfigChanged( jniGetReferent(env, mReceiverWeakGlobal)); if (receiverObj.get()) { ALOGV("receiver %p ~ Invoking config changed handler.", this); - env->CallVoidMethod(receiverObj.get(), - gDisplayEventReceiverClassInfo.dispatchConfigChanged, - timestamp, displayId, configId); + env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged, + timestamp, displayId.value, configId); ALOGV("receiver %p ~ Returned from config changed handler.", this); } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 814a07e7f2df..d6a773fb91e0 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -703,7 +703,7 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { jlong* values = env->GetLongArrayElements(array, 0); for (size_t i = 0; i < displayIds.size(); ++i) { - values[i] = static_cast<jlong>(displayIds[i]); + values[i] = static_cast<jlong>(displayIds[i].value); } env->ReleaseLongArrayElements(array, values, 0); @@ -711,7 +711,8 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { } static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) { - sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(physicalDisplayId); + sp<IBinder> token = + SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId)); return javaObjectForIBinder(env, token); } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index f11adef81793..ea8df831fd24 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -24,6 +24,7 @@ import static android.app.servertransaction.TestUtils.resultInfoList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.app.ContentProviderHolder; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.app.IUiAutomationConnection; @@ -664,5 +665,10 @@ public class TransactionParcelTests { public void performDirectAction(IBinder activityToken, String actionId, Bundle arguments, RemoteCallback cancellationCallback, RemoteCallback resultCallback) { } + + @Override + public void notifyContentProviderPublishStatus(ContentProviderHolder holder, String auth, + int userId, boolean published) { + } } } diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index df2946c97d20..c37a34a68549 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -201,6 +201,38 @@ public class EditorCursorDragTest { } @Test + public void testCursorDrag_diagonal_thresholdConfig() throws Throwable { + TextView tv = mActivity.findViewById(R.id.textview); + Editor editor = tv.getEditorForTesting(); + + StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= 9; i++) { + sb.append("here is some text").append(i).append("\n"); + } + sb.append(Strings.repeat("abcdefghij\n", 400)).append("Last"); + String text = sb.toString(); + onView(withId(R.id.textview)).perform(replaceText(text)); + + int index = text.indexOf("text9"); + onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index)); + + // Configure the drag direction threshold to require the drag to be exactly horizontal. With + // this set, a swipe that is slightly off horizontal should not trigger cursor drag. + editor.setCursorDragMinAngleFromVertical(90); + int startIdx = text.indexOf("5"); + int endIdx = text.indexOf("here is some text3"); + onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index)); + + // Configure the drag direction threshold to require the drag to be 45 degrees or more from + // vertical. With this set, the same swipe gesture as above should now trigger cursor drag. + editor.setCursorDragMinAngleFromVertical(45); + onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(endIdx)); + } + + @Test public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable { String text = "012345_aaa\n" + "0123456789\n" diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index 35fd4bd7dc14..94f43def240d 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -165,7 +165,7 @@ public class EditorTouchStateTest { long event2Time = 1001; MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, 180f); // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout. long event3Time = 5000; @@ -280,7 +280,7 @@ public class EditorTouchStateTest { long event3Time = 1002; MotionEvent event3 = moveEvent(event3Time, event3Time, newX, newY); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE); // Simulate an ACTION_UP event. long event4Time = 1003; @@ -301,15 +301,15 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 174f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, true); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f); // Simulate another ACTION_MOVE event that is horizontal from the original down event. - // The value of `isDragCloseToVertical` should NOT change since it should only reflect the - // initial direction of movement. + // The drag direction ratio should NOT change since it should only reflect the initial + // direction of movement. long event3Time = 1003; MotionEvent event3 = moveEvent(event1Time, event3Time, 200f, 0f); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, true); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f); // Simulate an ACTION_UP event. long event4Time = 1004; @@ -330,15 +330,15 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, false); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f); // Simulate another ACTION_MOVE event that is vertical from the original down event. - // The value of `isDragCloseToVertical` should NOT change since it should only reflect the - // initial direction of movement. + // The drag direction ratio should NOT change since it should only reflect the initial + // direction of movement. long event3Time = 1003; MotionEvent event3 = moveEvent(event1Time, event3Time, 0f, 200f); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, false); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f); // Simulate an ACTION_UP event. long event4Time = 1004; @@ -374,7 +374,7 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event2Time, event2Time, 200f, 30f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE); // Simulate an ACTION_CANCEL event. long event3Time = 1003; @@ -411,6 +411,84 @@ public class EditorTouchStateTest { assertSingleTap(mTouchState, 22f, 33f, 20f, 30f); } + @Test + public void testGetXYRatio() throws Exception { + doTestGetXYRatio(-1, 0.0f); + doTestGetXYRatio(0, 0.0f); + doTestGetXYRatio(30, 0.58f); + doTestGetXYRatio(45, 1.0f); + doTestGetXYRatio(60, 1.73f); + doTestGetXYRatio(90, Float.MAX_VALUE); + doTestGetXYRatio(91, Float.MAX_VALUE); + } + + private void doTestGetXYRatio(int angleFromVerticalInDegrees, float expectedXYRatioRounded) { + float result = EditorTouchState.getXYRatio(angleFromVerticalInDegrees); + String msg = String.format( + "%d deg should give an x/y ratio of %f; actual unrounded result is %f", + angleFromVerticalInDegrees, expectedXYRatioRounded, result); + float roundedResult = (result == 0.0f || result == Float.MAX_VALUE) ? result : + Math.round(result * 100) / 100f; + assertThat(msg, roundedResult, is(expectedXYRatioRounded)); + } + + @Test + public void testUpdate_dragDirection() throws Exception { + // Simulate moving straight up. + doTestDragDirection(100f, 100f, 100f, 50f, 0f); + + // Simulate moving straight down. + doTestDragDirection(100f, 100f, 100f, 150f, 0f); + + // Simulate moving straight left. + doTestDragDirection(100f, 100f, 50f, 100f, Float.MAX_VALUE); + + // Simulate moving straight right. + doTestDragDirection(100f, 100f, 150f, 100f, Float.MAX_VALUE); + + // Simulate moving up and right, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 110f, 50f, 10f / 50f); + + // Simulate moving up and right, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 150f, 90f, 50f / 10f); + + // Simulate moving down and right, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 110f, 150f, 10f / 50f); + + // Simulate moving down and right, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 150f, 110f, 50f / 10f); + + // Simulate moving down and left, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 90f, 150f, 10f / 50f); + + // Simulate moving down and left, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 50f, 110f, 50f / 10f); + + // Simulate moving up and left, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 90f, 50f, 10f / 50f); + + // Simulate moving up and left, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 50f, 90f, 50f / 10f); + } + + private void doTestDragDirection(float downX, float downY, float moveX, float moveY, + float expectedInitialDragDirectionXYRatio) { + EditorTouchState touchState = new EditorTouchState(); + + // Simulate an ACTION_DOWN event. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, downX, downY); + touchState.update(event1, mConfig); + + // Simulate an ACTION_MOVE event. + long event2Time = 1002; + MotionEvent event2 = moveEvent(event1Time, event2Time, moveX, moveY); + touchState.update(event2, mConfig); + String msg = String.format("(%.0f,%.0f)=>(%.0f,%.0f)", downX, downY, moveX, moveY); + assertThat(msg, touchState.getInitialDragDirectionXYRatio(), + is(expectedInitialDragDirectionXYRatio)); + } + private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) { return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0); } @@ -441,7 +519,7 @@ public class EditorTouchStateTest { } private static void assertDrag(EditorTouchState touchState, float lastDownX, - float lastDownY, float lastUpX, float lastUpY, boolean isDragCloseToVertical) { + float lastDownY, float lastUpX, float lastUpY, float initialDragDirectionXYRatio) { assertThat(touchState.getLastDownX(), is(lastDownX)); assertThat(touchState.getLastDownY(), is(lastDownY)); assertThat(touchState.getLastUpX(), is(lastUpX)); @@ -451,7 +529,7 @@ public class EditorTouchStateTest { assertThat(touchState.isMultiTap(), is(false)); assertThat(touchState.isMultiTapInSameArea(), is(false)); assertThat(touchState.isMovedEnoughForDrag(), is(true)); - assertThat(touchState.isDragCloseToVertical(), is(isDragCloseToVertical)); + assertThat(touchState.getInitialDragDirectionXYRatio(), is(initialDragDirectionXYRatio)); } private static void assertMultiTap(EditorTouchState touchState, @@ -467,6 +545,5 @@ public class EditorTouchStateTest { || multiTapStatus == MultiTapStatus.TRIPLE_CLICK)); assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea)); assertThat(touchState.isMovedEnoughForDrag(), is(false)); - assertThat(touchState.isDragCloseToVertical(), is(false)); } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java index 3e67b8bffa63..22c41f3c9622 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java @@ -38,7 +38,8 @@ import java.util.Collection; @SmallTest public class BatteryStatsBinderCallStatsTest extends TestCase { - private static final int TRANSACTION_CODE = 100; + private static final int TRANSACTION_CODE1 = 100; + private static final int TRANSACTION_CODE2 = 101; /** * Test BatteryStatsImpl.Uid.noteBinderCallStats. @@ -53,23 +54,23 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid, - MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */); + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); stat1.incrementalCallCount = 21; stat1.recordedCallCount = 5; stat1.cpuTimeMicros = 1000; callStats.add(stat1); - bi.noteBinderCallStats(workSourceUid, 42, callStats); + bi.noteBinderCallStats(workSourceUid, 42, callStats, null); callStats.clear(); BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid, - MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */); + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); stat2.incrementalCallCount = 9; stat2.recordedCallCount = 8; stat2.cpuTimeMicros = 500; callStats.add(stat2); - bi.noteBinderCallStats(workSourceUid, 8, callStats); + bi.noteBinderCallStats(workSourceUid, 8, callStats, null); BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid); assertEquals(42 + 8, uid.getBinderCallCount()); @@ -85,9 +86,62 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { assertEquals(500, value.recordedCpuTimeMicros); } + + @Test + public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + + int callingUid = Process.FIRST_APPLICATION_UID + 1; + int workSourceUid1 = Process.FIRST_APPLICATION_UID + 1; + int workSourceUid2 = Process.FIRST_APPLICATION_UID + 2; + int workSourceUid3 = Process.FIRST_APPLICATION_UID + 3; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1a = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); + stat1a.incrementalCallCount = 10; + stat1a.recordedCallCount = 5; + stat1a.cpuTimeMicros = 1000; + callStats.add(stat1a); + + BinderCallsStats.CallStat stat1b = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE2, true /*screenInteractive */); + stat1b.incrementalCallCount = 30; + stat1b.recordedCallCount = 15; + stat1b.cpuTimeMicros = 1500; + callStats.add(stat1b); + + bi.noteBinderCallStats(workSourceUid1, 65, callStats, null); + + // No recorded stats for some methods. Must use the global average. + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); + stat2.incrementalCallCount = 10; + callStats.add(stat2); + + bi.noteBinderCallStats(workSourceUid2, 40, callStats, null); + + // No stats for any calls. Must use the global average + callStats.clear(); + bi.noteBinderCallStats(workSourceUid3, 50, callStats, null); + + bi.updateSystemServiceCallStats(); + + double prop1 = bi.getUidStatsLocked(workSourceUid1).getProportionalSystemServiceUsage(); + double prop2 = bi.getUidStatsLocked(workSourceUid2).getProportionalSystemServiceUsage(); + double prop3 = bi.getUidStatsLocked(workSourceUid3).getProportionalSystemServiceUsage(); + + assertEquals(0.419, prop1, 0.01); + assertEquals(0.258, prop2, 0.01); + assertEquals(0.323, prop3, 0.01); + assertEquals(1.000, prop1 + prop2 + prop3, 0.01); + } + private static class MockBinder extends Binder { public static String getDefaultTransactionName(int txCode) { - return txCode == TRANSACTION_CODE ? "testMethod" : "unknown"; + return txCode == TRANSACTION_CODE1 ? "testMethod" : "unknown"; } } } diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index a5117a3e7cc3..188ba9e0ca0e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -768,8 +768,8 @@ public class BinderCallsStatsTest { final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>(); bcs.setCallStatsObserver( - (workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll( - callStats)); + (workSourceUid, incrementalCallCount, callStats, binderThreadIds) -> + callStatsList.addAll(callStats)); Binder binder = new Binder(); diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index bc0e0a496d80..75dd7fb82f30 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -133,6 +133,12 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return this; } + public MockBatteryStatsImpl setSystemServerCpuThreadReader( + SystemServerCpuThreadReader systemServerCpuThreadReader) { + mSystemServerCpuThreadReader = systemServerCpuThreadReader; + return this; + } + public MockBatteryStatsImpl setUserInfoProvider(UserInfoProvider provider) { mUserInfoProvider = provider; return this; diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java new file mode 100644 index 000000000000..10ba54865dbe --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java @@ -0,0 +1,136 @@ +/* + * 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.internal.os; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.os.FileUtils; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SystemServerCpuThreadReaderTest { + private File mProcDirectory; + + @Before + public void setUp() { + Context context = InstrumentationRegistry.getContext(); + mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE); + } + + @After + public void tearDown() throws Exception { + FileUtils.deleteContents(mProcDirectory); + } + + @Test + public void testReaderDelta_firstTime() throws IOException { + int uid = 42; + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + // Units are 10ms aka 10000Us + new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}}); + + SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( + mProcDirectory.toPath(), uid); + reader.setBinderThreadNativeTids(new int[]{1, 3}); + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + reader.readDelta(); + assertArrayEquals(new long[]{100 * 10000, 1100 * 10000}, + systemServiceCpuThreadTimes.threadCpuTimesUs); + assertArrayEquals(new long[]{0, 600 * 10000}, + systemServiceCpuThreadTimes.binderThreadCpuTimesUs); + } + + @Test + public void testReaderDelta_nextTime() throws IOException { + int uid = 42; + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}}); + + SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( + mProcDirectory.toPath(), uid); + reader.setBinderThreadNativeTids(new int[]{1, 3}); + + // First time, populate "last" snapshot + reader.readDelta(); + + FileUtils.deleteContents(mProcDirectory); + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + new int[][]{{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}}); + + // Second time, get the actual delta + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + reader.readDelta(); + + assertArrayEquals(new long[]{3100 * 10000, 2500 * 10000}, + systemServiceCpuThreadTimes.threadCpuTimesUs); + assertArrayEquals(new long[]{1800 * 10000, 1400 * 10000}, + systemServiceCpuThreadTimes.binderThreadCpuTimesUs); + } + + private void setupDirectory(Path processPath, int[] threadIds, int[] cpuFrequencies, + int[][] cpuTimes) throws IOException { + // Make /proc/$PID + assertTrue(processPath.toFile().mkdirs()); + + // Make /proc/$PID/task + final Path selfThreadsPath = processPath.resolve("task"); + assertTrue(selfThreadsPath.toFile().mkdirs()); + + // Make thread directories in reverse order, as they are read in order of creation by + // CpuThreadProcReader + for (int i = 0; i < threadIds.length; i++) { + // Make /proc/$PID/task/$TID + final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i])); + assertTrue(threadPath.toFile().mkdirs()); + + // Make /proc/$PID/task/$TID/time_in_state + final OutputStream timeInStateStream = + Files.newOutputStream(threadPath.resolve("time_in_state")); + for (int j = 0; j < cpuFrequencies.length; j++) { + final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n"; + timeInStateStream.write(line.getBytes()); + } + timeInStateStream.close(); + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java new file mode 100644 index 000000000000..ac5443e1c7ce --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -0,0 +1,173 @@ +/* + * 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.internal.os; + +import static org.junit.Assert.assertEquals; + +import android.content.Context; +import android.os.BatteryStats; +import android.os.Binder; +import android.os.Process; + +import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SystemServicePowerCalculatorTest { + + private PowerProfile mProfile; + private MockBatteryStatsImpl mMockBatteryStats; + private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; + private MockServerCpuThreadReader mMockServerCpuThreadReader; + private SystemServicePowerCalculator mSystemServicePowerCalculator; + + @Before + public void setUp() throws IOException { + Context context = InstrumentationRegistry.getContext(); + mProfile = new PowerProfile(context, true /* forTest */); + mMockServerCpuThreadReader = new MockServerCpuThreadReader(); + mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader(); + mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks()) + .setPowerProfile(mProfile) + .setSystemServerCpuThreadReader(mMockServerCpuThreadReader) + .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) + .setUserInfoProvider(new MockUserInfoProvider()); + mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); + mSystemServicePowerCalculator = + new SystemServicePowerCalculator(mProfile, mMockBatteryStats); + } + + @Test + public void testCalculateApp() { + // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total + mMockServerCpuThreadReader.setThreadTimes( + new long[]{30000, 40000, 50000, 60000, 70000, 80000, 90000}, + new long[]{20000, 30000, 40000, 50000, 60000, 70000, 80000}); + + mMockCpuUidFreqTimeReader.setSystemServerCpuTimes( + new long[]{10000, 20000, 30000, 40000, 50000, 60000, 70000} + ); + + mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false); + + int workSourceUid1 = 100; + int workSourceUid2 = 200; + int transactionCode = 42; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1, + Binder.class, transactionCode, true /*screenInteractive */); + stat1.incrementalCallCount = 100; + stat1.recordedCallCount = 100; + stat1.cpuTimeMicros = 1000000; + callStats.add(stat1); + + mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null); + + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2, + Binder.class, transactionCode, true /*screenInteractive */); + stat2.incrementalCallCount = 100; + stat2.recordedCallCount = 100; + stat2.cpuTimeMicros = 9000000; + callStats.add(stat2); + + mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null); + + mMockBatteryStats.updateSystemServiceCallStats(); + mMockBatteryStats.updateSystemServerThreadStats(); + + BatterySipper app1 = new BatterySipper(BatterySipper.DrainType.APP, + mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0); + mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0, + BatteryStats.STATS_SINCE_CHARGED); + assertEquals(0.27162, app1.systemServiceCpuPowerMah, 0.00001); + + BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP, + mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0); + mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0, + BatteryStats.STATS_SINCE_CHARGED); + assertEquals(2.44458, app2.systemServiceCpuPowerMah, 0.00001); + } + + private static class MockKernelCpuUidFreqTimeReader extends + KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader { + private long[] mSystemServerCpuTimes; + + MockKernelCpuUidFreqTimeReader() { + super(/*throttle */false); + } + + void setSystemServerCpuTimes(long[] systemServerCpuTimes) { + mSystemServerCpuTimes = systemServerCpuTimes; + } + + @Override + public boolean perClusterTimesAvailable() { + return true; + } + + @Override + public void readDelta(@Nullable Callback<long[]> cb) { + if (cb != null) { + cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes); + } + } + } + + private static class MockServerCpuThreadReader extends SystemServerCpuThreadReader { + private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes(); + + MockServerCpuThreadReader() { + super(null); + } + + public void setThreadTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { + mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; + mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; + } + + @Override + public SystemServiceCpuThreadTimes readDelta() { + return mThreadTimes; + } + } + + private static class MockUserInfoProvider extends BatteryStatsImpl.UserInfoProvider { + @Nullable + @Override + protected int[] getUserIds() { + return new int[0]; + } + + @Override + public boolean exists(int userId) { + return true; + } + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index f834ce798a27..b3c82631011a 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -432,18 +432,6 @@ applications that come with the platform <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" /> <!-- Permissions required for CTS test - AdbManagerTest --> <permission name="android.permission.MANAGE_DEBUGGING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp --> - <permission name="android.car.permission.CAR_DRIVING_STATE" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases --> - <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <permission name="android.car.permission.CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <permission name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <permission name="android.car.permission.CONTROL_APP_BLOCKING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java index 3ffb8ea40789..a2ee065cd1cc 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java @@ -23,12 +23,15 @@ import com.google.errorprone.BugPattern; import com.google.errorprone.VisitorState; import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; +import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher; import com.google.errorprone.matchers.Description; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; import com.sun.tools.javac.code.Symbol.VarSymbol; import java.util.List; @@ -44,11 +47,20 @@ import java.util.regex.Pattern; name = "AndroidFrameworkUid", summary = "Verifies that PID, UID and user ID arguments aren't crossed", severity = WARNING) -public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher { +public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher, + NewClassTreeMatcher { @Override public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { - final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params(); - final List<? extends ExpressionTree> args = tree.getArguments(); + return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree); + } + + @Override + public Description matchNewClass(NewClassTree tree, VisitorState state) { + return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree); + } + + private Description matchArguments(List<VarSymbol> vars, + List<? extends ExpressionTree> args, Tree tree) { for (int i = 0; i < Math.min(vars.size(), args.size()); i++) { final Flavor varFlavor = getFlavor(vars.get(i)); final Flavor argFlavor = getFlavor(args.get(i)); diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java index 74da94731092..75341fd1f317 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java @@ -34,7 +34,7 @@ public class UidCheckerTest { } @Test - public void testTypical() { + public void testTypical_methodInvocation() { compilationHelper .addSourceLines("Example.java", "public abstract class Example {", @@ -57,7 +57,30 @@ public class UidCheckerTest { } @Test - public void testCallingUid() { + public void testTypical_newClass() { + compilationHelper + .addSourceLines("Example.java", + "public abstract class Example {", + " class Bar { Bar(int pid, int uid, int userId) {} }", + " abstract int getUserId();", + " void foo(int pid, int uid, int userId, int unrelated) {", + " new Bar(0, 0, 0);", + " new Bar(pid, uid, userId);", + " new Bar(pid, uid, getUserId());", + " new Bar(unrelated, unrelated, unrelated);", + " // BUG: Diagnostic contains:", + " new Bar(uid, pid, userId);", + " // BUG: Diagnostic contains:", + " new Bar(pid, userId, uid);", + " // BUG: Diagnostic contains:", + " new Bar(getUserId(), 0, 0);", + " }", + "}") + .doTest(); + } + + @Test + public void testCallingUid_methodInvocation() { compilationHelper .addSourceFile("/android/os/Binder.java") .addSourceFile("/android/os/UserHandle.java") @@ -98,4 +121,48 @@ public class UidCheckerTest { "}") .doTest(); } + + + @Test + public void testCallingUid_newClass() { + compilationHelper + .addSourceFile("/android/os/Binder.java") + .addSourceFile("/android/os/UserHandle.java") + .addSourceLines("Example.java", + "import android.os.Binder;", + "import android.os.UserHandle;", + "public abstract class Example {", + " int callingUserId;", + " int callingUid;", + " class Foo { Foo(int callingUserId) {} }", + " class Bar { Bar(int callingUid) {} }", + " void doUserId(int callingUserId) {", + " new Foo(UserHandle.getUserId(Binder.getCallingUid()));", + " new Foo(this.callingUserId);", + " new Foo(callingUserId);", + " // BUG: Diagnostic contains:", + " new Foo(Binder.getCallingUid());", + " // BUG: Diagnostic contains:", + " new Foo(this.callingUid);", + " // BUG: Diagnostic contains:", + " new Foo(callingUid);", + " }", + " void doUid(int callingUserId) {", + " new Bar(Binder.getCallingUid());", + " new Bar(this.callingUid);", + " new Bar(callingUid);", + " // BUG: Diagnostic contains:", + " new Bar(UserHandle.getUserId(Binder.getCallingUid()));", + " // BUG: Diagnostic contains:", + " new Bar(this.callingUserId);", + " // BUG: Diagnostic contains:", + " new Bar(callingUserId);", + " }", + " void doInner() {", + " // BUG: Diagnostic contains:", + " new Foo(UserHandle.getUserId(callingUserId));", + " }", + "}") + .doTest(); + } } diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 54618a5da3a3..70ef69d8ce72 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -11900,6 +11900,7 @@ package android.content.pm { field public static final int INVALID_ID = -1; // 0xffffffff field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2 field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 + field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4 field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java index 5dcb9de4755e..a2cd0446d1e8 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java @@ -39,7 +39,7 @@ import javax.inject.Singleton; /** * A class that detects unsafe apps. - * An app is considered safe if is a system app or installed through whitelisted sources. + * An app is considered safe if is a system app or installed through allowed sources. */ @Singleton public class SideLoadedAppDetector { diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java index 31f1170c9603..f77294e37b98 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java @@ -19,6 +19,8 @@ package com.android.systemui.car.voicerecognition; import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE; import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -40,11 +42,13 @@ import com.android.systemui.car.CarSystemUiTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; @CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest +// TODO(b/162866441): Refactor to use the Executor pattern instead. public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; @@ -52,13 +56,15 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier; private TestableLooper mTestableLooper; + private Handler mHandler; private Handler mTestHandler; private BluetoothDevice mBluetoothDevice; @Before public void setUp() throws Exception { mTestableLooper = TestableLooper.get(this); - mTestHandler = spy(new Handler(mTestableLooper.getLooper())); + mHandler = new Handler(mTestableLooper.getLooper()); + mTestHandler = spy(mHandler); mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( BLUETOOTH_REMOTE_ADDRESS); mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier( @@ -74,8 +80,14 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); - - verify(mTestHandler).post(any()); + waitForIdleSync(); + + mHandler.post(() -> { + ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(mTestHandler).post(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue()).isNotNull(); + assertThat(argumentCaptor.getValue()).isNotEqualTo(this); + }); } @Test @@ -86,8 +98,11 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } @Test @@ -97,8 +112,11 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } @Test @@ -108,7 +126,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 704d264d1983..6751fa4583c5 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Wys Bluetooth-toestelle sonder name"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiveer absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiveer Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde konnektiwiteit"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-weergawe"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Kies Bluetooth AVRCP-weergawe"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-weergawe"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 585924d7efb9..470e780fc049 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"የብሉቱዝ መሣሪያዎችን ያለ ስሞች አሳይ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ፍጹማዊ ድምፅን አሰናክል"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheን አንቃ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"የተሻሻለ ተገናኝነት"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"የብሉቱዝ AVRCP ስሪት"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"የብሉቱዝ AVRCP ስሪት ይምረጡ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"የብሉቱዝ MAP ስሪት"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 39777cd5f881..9acfa0da7747 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -152,7 +152,7 @@ <string name="user_guest" msgid="6939192779649870792">"ضيف"</string> <string name="unknown" msgid="3544487229740637809">"غير معروف"</string> <string name="running_process_item_user_label" msgid="3988506293099805796">"المستخدم: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string> - <string name="launch_defaults_some" msgid="3631650616557252926">"تم تعيين بعض الإعدادات التلقائية"</string> + <string name="launch_defaults_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم تعيين إعدادات تلقائية"</string> <string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string> <string name="tts_settings_title" msgid="7602210956640483039">"تحويل النص إلى كلام"</string> @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"عرض أجهزة البلوتوث بدون أسماء"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"إيقاف مستوى الصوت المطلق"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"تفعيل Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"إمكانية اتصال محسّن"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"إصدار Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"اختيار إصدار Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"إصدار Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index e0455cb5f19c..f993dba39476 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামবিহীন ব্লুটুথ ডিভাইচসমূহ দেখুৱাওক"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"পূৰ্ণ মাত্ৰাৰ ভলিউম অক্ষম কৰক"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche সক্ষম কৰক"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"উন্নত সংযোগ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP সংস্কৰণ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP সংস্কৰণ বাছনি কৰক"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP সংস্কৰণ"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 6565d530f6a1..6a506614f970 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth cihazlarını adsız göstərin"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mütləq səs həcmi deaktiv edin"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'ni aktiv edin"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Təkmilləşdirilmiş Bağlantı"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Versiya"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Versiyasını seçin"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Versiyası"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 3ced29b47dd6..cf988ab3589f 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući glavno podešavanje jačine zvuka"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšano povezivanje"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija Bluetooth AVRCP-a"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izaberite verziju Bluetooth AVRCP-a"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija Bluetooth MAP-a"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 01d7682416fa..8f71509772fc 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Паказваць прылады Bluetooth без назваў"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Адключыць абсалютны гук"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Уключыць Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Палепшанае падключэнне"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выбраць версію Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index d042c0f89b1e..747cb266b6de 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показване на устройствата с Bluetooth без имена"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Деактивиране на пълната сила на звука"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Активиране на Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена свързаност"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия на AVRCP за Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Избиране на версия на AVRCP за Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP версия за Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 2db23f73e683..87f3b7a4f2e8 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামহীন ব্লুটুথ ডিভাইসগুলি দেখুন"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"চূড়ান্ত ভলিউম অক্ষম করুন"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ফিচার চালু করুন"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"কানেক্টিভিটি উন্নত করা হয়েছে"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP ভার্সন"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP ভার্সন বেছে নিন"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP ভার্সন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index f26fe9d58533..e329c993eedb 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu jačinu zvuka"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP verzija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite Bluetooth AVRCP verziju"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP verzija"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 3ca9d5272d48..5ffdacd19c22 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra els dispositius Bluetooth sense el nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactiva el volum absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activa Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivitat millorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versió AVRCP de Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versió AVRCP de Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versió MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index a5532e0bff02..0aef99feb04f 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovat zařízení Bluetooth bez názvů"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázat absolutní hlasitost"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Zapnout funkci Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepší připojování"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verze profilu Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vyberte verzi profilu Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verze MAP pro Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 8ca22d7ba797..98068cb172fa 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheder uden navne"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiver absolut lydstyrke"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivér Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version for Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vælg AVRCP-version for Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version for Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 6b0ae2e24bb6..0837ad33ab06 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-Geräte ohne Namen anzeigen"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absolute Lautstärkeregelung deaktivieren"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bluetooth-Gabeldorsche aktivieren"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbesserte Konnektivität"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-Version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP-Version auswählen"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-Version"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 4d7c88287f72..1f9d97764016 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Εμφάνιση συσκευών Bluetooth χωρίς ονόματα"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Απενεργοποίηση απόλυτης έντασης"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ενεργοποίηση Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Βελτιωμένη συνδεσιμότητα"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Έκδοση AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Επιλογή έκδοσης AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Έκδοση MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index a9f039a6522c..959ad46bcf61 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 41c20e0c915f..738dd2a2ba31 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Version"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 287a1aca2fbd..d1e4fb5607a8 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de AVRCP del Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión de AVRCP del Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 9d3455766f6b..9ad71e2766a3 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión AVRCP de Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión AVRCP de Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index d003ef0b9c71..14d3b5782862 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Kuva ilma nimedeta Bluetoothi seadmed"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Keela absoluutne helitugevus"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Luba Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Täiustatud ühenduvus"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothi AVRCP versioon"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valige Bluetoothi AVRCP versioon"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothi MAP-i versioon"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 0042321a3654..dcdadbdf3454 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Erakutsi Bluetooth bidezko gailuak izenik gabe"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desgaitu bolumen absolutua"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gaitu Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Konexio hobeak"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP bertsioa"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Hautatu Bluetooth AVRCP bertsioa"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAParen bertsioa"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 1c0881531f89..f3b22d355b77 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"نمایش دستگاههای بلوتوث بدون نام"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"غیرفعال کردن میزان صدای مطلق"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"فعال کردن Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"اتصال بهبودیافته"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"نسخه AVRCP بلوتوث"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"انتخاب نسخه AVRCP بلوتوث"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"نسخه MAP بلوتوث"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 3945e5542eb7..3d28f1d0a1f5 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Näytä nimettömät Bluetooth-laitteet"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Poista yleinen äänenvoimakkuuden säätö käytöstä"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ota Gabeldorsche käyttöön"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Parannetut yhteydet"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothin AVRCP-versio"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valitse Bluetoothin AVRCP-versio"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothin MAP-versio"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 140d4cee6ad7..87d3de16f216 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer le Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version du profil Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version du profil Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version du profil Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 1b1ae8ed55d4..ddf2bc0991a4 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -251,13 +251,12 @@ <string name="wifi_display_certification" msgid="1805579519992520381">"Certification affichage sans fil"</string> <string name="wifi_verbose_logging" msgid="1785910450009679371">"Autoriser l\'enregistrement d\'infos Wi-Fi détaillées"</string> <string name="wifi_scan_throttling" msgid="2985624788509913617">"Limiter la recherche Wi‑Fi"</string> - <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC sur Wi-Fi"</string> + <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC en Wi-Fi"</string> <string name="mobile_data_always_on" msgid="8275958101875563572">"Données mobiles toujours actives"</string> <string name="tethering_hardware_offload" msgid="4116053719006939161">"Accélération matérielle pour le partage de connexion"</string> <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version Bluetooth MAP"</string> @@ -284,7 +283,7 @@ <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afficher les options pour la certification de l\'affichage sans fil"</string> <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Détailler les infos Wi-Fi, afficher par RSSI de SSID dans l\'outil de sélection Wi-Fi"</string> <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit la décharge de la batterie et améliore les performances du réseau"</string> - <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse e-mail MAC de cet appareil peut changer lors de chaque connexion à un réseau pour lequel le changement aléatoire d\'adresse MAC est activé."</string> + <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse MAC de cet appareil peut changer lors de chaque connexion à un réseau Wi-Fi pour lequel le changement aléatoire d\'adresse MAC est activé"</string> <string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string> <string name="wifi_unmetered_label" msgid="6174142840934095093">"Non facturé à l\'usage"</string> <string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des tampons de l\'enregistreur"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index f9d57c453f69..af430991083d 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sen nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactivar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade mellorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona a versión de Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index aa1f9605b07d..3261f69b1105 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"નામ વિનાના બ્લૂટૂથ ઉપકરણો બતાવો"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ચોક્કસ વૉલ્યૂમને અક્ષમ કરો"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ચાલુ કરો"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"વિસ્તૃત કનેક્ટિવિટી"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"બ્લૂટૂથ AVRCP સંસ્કરણ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"બ્લૂટૂથ AVRCP સંસ્કરણ પસંદ કરો"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"બ્લૂટૂથ MAP વર્ઝન"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 9b6a27ae8620..904a70e73958 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"बिना नाम वाले ब्लूटूथ डिवाइस दिखाएं"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ब्लूटूथ से आवाज़ के नियंत्रण की सुविधा रोकें"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche चालू करें"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"कनेक्टिविटी बेहतर बनाएं"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ एवीआरसीपी वर्शन"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP वर्शन चुनें"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ का MAP वर्शन"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 14e3330a1670..3edc4527fb01 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu glasnoću"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija AVRCP-a za Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite verziju AVRCP-a za Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija MAP-a za Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index d16ff03b0903..fec2dd6d523f 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Név nélküli Bluetooth-eszközök megjelenítése"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Abszolút hangerő funkció letiltása"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"A Gabeldorsche engedélyezése"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"A Bluetooth AVRCP-verziója"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"A Bluetooth AVRCP-verziójának kiválasztása"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"A Bluetooth MAP-verziója"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index b010b504b00c..f219d248e641 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ցուցադրել Bluetooth սարքերն առանց անունների"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Անջատել ձայնի բացարձակ ուժգնությունը"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Միացնել Gabeldorsche-ը"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Տվյալների լավացված փոխանակում"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP տարբերակը"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Ընտրել Bluetooth AVRCP տարբերակը"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ի տարբերակ"</string> diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index 37cf189f26c0..3ab50cc948eb 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -31,7 +31,7 @@ <item msgid="7852381437933824454">"Memutus sambungan..."</item> <item msgid="5046795712175415059">"Sambungan terputus"</item> <item msgid="2473654476624070462">"Gagal"</item> - <item msgid="9146847076036105115">"Dicekal"</item> + <item msgid="9146847076036105115">"Diblokir"</item> <item msgid="4543924085816294893">"Menghindari sambungan buruk untuk sementara"</item> </string-array> <string-array name="wifi_status_with_ssid"> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 42ccd5310913..a6f88465a673 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -180,8 +180,8 @@ <string name="tts_engine_settings_button" msgid="477155276199968948">"Luncurkan setelan mesin"</string> <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mesin yang dipilih"</string> <string name="tts_general_section_title" msgid="8919671529502364567">"Umum"</string> - <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Setel ulang tinggi nada ucapan"</string> - <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Setel ulang tinggi nada diucapkannya teks menjadi default."</string> + <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Reset tinggi nada ucapan"</string> + <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Reset tinggi nada diucapkannya teks menjadi default."</string> <string-array name="tts_rate_entries"> <item msgid="9004239613505400644">"Sangat lambat"</item> <item msgid="1815382991399815061">"Lambat"</item> @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tampilkan perangkat Bluetooth tanpa nama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Nonaktifkan volume absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktifkan Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Konektivitas Yang Disempurnakan"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 0ebc341a9541..caf2323813ca 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Sýna Bluetooth-tæki án heita"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slökkva á samstillingu hljóðstyrks"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Virkja Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Aukin tengigeta"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-útgáfa"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velja Bluetooth AVRCP-útgáfu"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-útgáfa"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 50fdfc9a9136..8d18727e384c 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra dispositivi Bluetooth senza nome"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disattiva volume assoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Attiva Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connettività migliorata"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versione Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Seleziona versione Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versione Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index fb7d00f866b1..fff881c46433 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"הצגת מכשירי Bluetooth ללא שמות"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"השבת עוצמת קול מוחלטת"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"הפעלת Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"קישוריות משופרת"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth גרסה AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"בחר Bluetooth גרסה AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"גרסת Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 3537ceaf8204..5e579b758f09 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth デバイスを名前なしで表示"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"絶対音量を無効にする"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche を有効にする"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"接続強化"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP バージョン"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP バージョンを選択する"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP バージョン"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 9b671a864e15..1b5fae9781b9 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-მოწყობილობების ჩვენება სახელების გარეშე"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ხმის აბსოლუტური სიძლიერის გათიშვა"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-ის ჩართვა"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"კავშირის გაძლიერებული შესაძლებლობა"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-ის AVRCP-ის ვერსია"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"აირჩიეთ Bluetooth-ის AVRCP-ის ვერსია"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ის ვერსია"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 279aca0731f7..9c290e90dd8d 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth құрылғыларын атаусыз көрсету"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Абсолютті дыбыс деңгейін өшіру"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын іске қосу"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Жетілдірілген байланыс"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP нұсқасы"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP нұсқасын таңдау"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP нұсқасы"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 03065e896da2..2878db192d18 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"បង្ហាញឧបករណ៍ប្ល៊ូធូសគ្មានឈ្មោះ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"បិទកម្រិតសំឡេងលឺខ្លាំង"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"បើក Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ការតភ្ជាប់ដែលបានធ្វើឱ្យប្រសើរឡើង"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"កំណែប្ល៊ូធូស AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ជ្រើសរើសកំណែប្ល៊ូធូស AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"កំណែប៊្លូធូស MAP"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 71c5e491d927..7b010564effa 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ಹೆಸರುಗಳಿಲ್ಲದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ತೋರಿಸಿ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ಸಂಪೂರ್ಣ ವಾಲ್ಯೂಮ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ವರ್ಧಿತ ಸಂಪರ್ಕ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿಯನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ಬ್ಲೂಟೂತ್ MAP ಆವೃತ್ತಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 5d82eae2fcba..696ed2955daa 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"이름이 없는 블루투스 기기 표시"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"절대 볼륨 사용 안함"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche 사용 설정"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"향상된 연결"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"블루투스 AVRCP 버전"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"블루투스 AVRCP 버전 선택"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"블루투스 MAP 버전"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 2702392c108f..c4b5f7e7477f 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Аталышсыз Bluetooth түзмөктөрү көрсөтүлсүн"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үндүн абсолюттук деңгээли өчүрүлсүн"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын иштетүү"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Жакшыртылган туташуу"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP версиясы"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP версиясын тандоо"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP версиясы"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 7a2c338ef008..a72861ee6aac 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ສະແດງອຸປະກອນ Bluetooth ທີ່ບໍ່ມີຊື່"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ປິດໃຊ້ລະດັບສຽງສົມບູນ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ເປີດໃຊ້ Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ການເຊື່ອມຕໍ່ທີ່ເສີມແຕ່ງແລ້ວ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ເວີຊັນ Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ເລືອກເວີຊັນ Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ເວີຊັນ Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index b73aa66ae54b..c72bf21d8b29 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rodyti „Bluetooth“ įrenginius be pavadinimų"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Išjungti didžiausią garsą"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Įgalinti „Gabeldorsche“"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Patobulintas ryšys"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"„Bluetooth“ AVRCP versija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pasirinkite „Bluetooth“ AVRCP versiją"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"„Bluetooth“ MRK versija"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 8e5d24cfa81a..d95e57df320e 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rādīt Bluetooth ierīces bez nosaukumiem"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Atspējot absolūto skaļumu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Iespējot Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Uzlabota savienojamība"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Atlasiet Bluetooth AVRCP versiju"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versija"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 34299d801946..fb7b63410a5f 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажувај уреди со Bluetooth без имиња"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Оневозможете апсолутна јачина на звук"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Овозможи Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена поврзливост"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изберете верзија Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија на Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index c95f8bf2fe39..3c281c8d972f 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"പേരില്ലാത്ത Bluetooth ഉപകരണങ്ങൾ കാണിക്കുക"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"അബ്സൊല്യൂട്ട് വോളിയം പ്രവർത്തനരഹിതമാക്കുക"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche പ്രവർത്തനക്ഷമമാക്കുക"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"മെച്ചപ്പെടുത്തിയ കണക്റ്റിവിറ്റി"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP പതിപ്പ്"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP പതിപ്പ് തിരഞ്ഞെടുക്കുക"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP പതിപ്പ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 8407db6d3a08..37fc5b47619c 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Нэргүй Bluetooth төхөөрөмжийг харуулах"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үнэмлэхүй дууны түвшинг идэвхгүй болгох"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-г идэвхжүүлэх"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Сайжруулсан холболт"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP хувилбар"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP хувилбарыг сонгох"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP хувилбар"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index c50f365f68e3..360f15897ebe 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नावांशिवाय ब्लूटूथ डिव्हाइस दाखवा"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"संपूर्ण आवाज बंद करा"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"गाबलडॉर्ष सुरू करा"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"वर्धित कनेक्टिव्हिटी"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ AVRCP आवृत्ती"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP आवृत्ती निवडा"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ MAP आवृत्ती"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index a0a434f05959..68356df25ef9 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tunjukkan peranti Bluetooth tanpa nama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Lumpuhkan kelantangan mutlak"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Dayakan Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kesambungan Dipertingkat"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index fa499293ceab..3729a8352abd 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"အမည်မရှိသော ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသရန်"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ပကတိ အသံနှုန်း သတ်မှတ်ချက် ပိတ်ရန်"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ကို ဖွင့်ရန်"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"အရည်အသွေးမြှင့်တင်ထားသော ချိတ်ဆက်နိုင်မှု"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ဘလူးတုသ် AVRCP ဗားရှင်း"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ဘလူးတုသ် AVRCP ဗားရှင်းကို ရွေးပါ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ဘလူးတုသ် MAP ဗားရှင်း"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index aeaba31691f9..0e0e7617b06d 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheter uten navn"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slå av funksjonen for absolutt volum"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiver Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Forbedret tilkobling"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-versjon"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velg Bluetooth AVRCP-versjon"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-versjon"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 4a2c1719aeb7..762d8304dd64 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू देखाउनुहोस्"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"निरपेक्ष आवाज असक्षम गर्नुहोस्"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche सक्षम पार्नुहोस्"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"परिष्कृत जडान"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लुटुथको AVRCP संस्करण"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लुटुथको AVRCP संस्करण चयन गर्नुहोस्"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लुटुथको MAP संस्करण"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 32cc39ec5b4f..83b72e925fe1 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-apparaten zonder namen weergeven"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absoluut volume uitschakelen"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche inschakelen"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde connectiviteit"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-AVRCP-versie"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth-AVRCP-versie selecteren"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-versie voor bluetooth"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 8e5bf25a19ca..d200f502879e 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ବ୍ଲୁଟୂଥ୍ ଡିଭାଇସ୍ଗୁଡ଼ିକୁ ନାମ ବିନା ଦେଖନ୍ତୁ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ପୂର୍ଣ୍ଣ ଭଲ୍ୟୁମ୍ ଅକ୍ଷମ କରନ୍ତୁ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ଗାବେଲ୍ଡୋର୍ସ ସକ୍ରିୟ କରନ୍ତୁ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ଏନହାନ୍ସଡ୍ କନେକ୍ଟିଭିଟି"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ବ୍ଲୁଟୂଥ୍ AVRCP ଭର୍ସନ୍"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ବ୍ଲୁଟୂଥ୍ AVRCP ଭର୍ସନ୍"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ବ୍ଲୁଟୁଥ୍ MAP ସଂସ୍କରଣ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 15700130cf78..354ee126d9af 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ਅਨਾਮ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਿਖਾਓ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ਪੂਰਨ ਅਵਾਜ਼ ਨੂੰ ਬੰਦ ਕਰੋ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ਵਿਸਤ੍ਰਿਤ ਕਨੈਕਟੀਵਿਟੀ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ ਚੁਣੋ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP ਵਰਜਨ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 1120c504717f..095412c94502 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Pokaż urządzenia Bluetooth bez nazw"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Wyłącz głośność bezwzględną"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Włącz Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepsza obsługa połączeń"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Wersja AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Wybierz wersję AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Wersja MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 4214a2720813..895a9872f4d4 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 508cbfccffe9..2d9f037297fb 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar o Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conetividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão de Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão de Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão do MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 4214a2720813..895a9872f4d4 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 663d3f702ae5..728db174cd7b 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afișați dispozitivele Bluetooth fără nume"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Dezactivați volumul absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activați Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectivitate îmbunătățită"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectați versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 9c0a2f5beb8b..ff2115ee3ff0 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показывать Bluetooth-устройства без названий"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Отключить абсолютный уровень громкости"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Включить Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Улучшенный обмен данными"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выберите версию Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версия Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 8d0e93e4b5f7..a883cc60d8e1 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"නම් නොමැති බ්ලූටූත් උපාංග පෙන්වන්න"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"නිරපේක්ෂ හඩ පරිමාව අබල කරන්න"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche සබල කරන්න"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"වැඩිදියුණු කළ සබැඳුම් හැකියාව"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"බ්ලූටූත් AVRCP අනුවාදය"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"බ්ලූටූත් AVRCP අනුවාදය තෝරන්න"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP අනුවාදය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index f39a741e3a4b..05c63795382b 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovať zariadenia Bluetooth bez názvov"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázať absolútnu hlasitosť"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Povoliť Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Zlepšené možnosti pripojenia"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzia rozhrania Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zvoľte verziu rozhrania Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzia profilu Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 233c8e48635a..fd216e83d927 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži naprave Bluetooth brez imen"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogočanje absolutne glasnosti"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogoči Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Izboljšana povezljivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Različica profila AVRCP za Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izberite različico profila AVRCP za Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Različica profila MAP za Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 6af1062a98f8..002c7fcda363 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Shfaq pajisjet me Bluetooth pa emra"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Çaktivizo volumin absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivizo Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lidhshmëria e përmirësuar"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versioni AVRCP i Bluetooth-it"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zgjidh versionin AVRCP të Bluetooth-it"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versioni MAP i Bluetooth-it"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 74c2aec7c174..25a1beb7af68 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажи Bluetooth уређаје без назива"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Онемогући главно подешавање јачине звука"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Омогући Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Побољшано повезивање"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP-а"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изаберите верзију Bluetooth AVRCP-а"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија Bluetooth MAP-а"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index fe1b0a856802..352cb0ab7d19 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Visa namnlösa Bluetooth-enheter"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inaktivera Absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivera Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Förbättrad anslutning"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version för Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Välj AVRCP-version för Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version för Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 5c80627003cd..d2891a06402a 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Onyesha vifaa vya Bluetooth visivyo na majina"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zima sauti kamili"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Washa Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Muunganisho Ulioboreshwa"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Toleo la Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chagua Toleo la Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Toleo la Ramani ya Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 241644f631d3..7837dd8d46df 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"பெயர்கள் இல்லாத புளூடூத் சாதனங்களைக் காட்டு"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"அப்சல்யூட் ஒலியளவு அம்சத்தை முடக்கு"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheவை இயக்கு"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"மேம்படுத்தப்பட்ட இணைப்புநிலை"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"புளூடூத் AVRCP பதிப்பு"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"புளூடூத் AVRCP பதிப்பைத் தேர்ந்தெடு"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"புளூடூத்தின் MAP பதிப்பு"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index a9ec2ea95ac5..60001a0b0ecd 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"పేర్లు లేని బ్లూటూత్ పరికరాలు చూపించు"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"సంపూర్ణ వాల్యూమ్ను నిలిపివేయి"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheను ఎనేబుల్ చేయి"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"మెరుగైన కనెక్టివిటీ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"బ్లూటూత్ AVRCP వెర్షన్"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"బ్లూటూత్ AVRCP సంస్కరణను ఎంచుకోండి"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"బ్లూటూత్ MAP వెర్షన్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index b8343c6a82e7..defc33ee9233 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"แสดงอุปกรณ์บลูทูธที่ไม่มีชื่อ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ปิดใช้การควบคุมระดับเสียงของอุปกรณ์อื่น"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"เปิดใช้ Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"การเชื่อมต่อที่ปรับปรุงแล้ว"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"เวอร์ชันของบลูทูธ AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"เลือกเวอร์ชันของบลูทูธ AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"เวอร์ชัน MAP ของบลูทูธ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 8aeb39256748..5d4e9752fd93 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ipakita ang mga Bluetooth device na walang pangalan"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"I-disable ang absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"I-enable ang Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Pinagandang Pagkakonekta"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bersyon ng AVRCP ng Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pumili ng Bersyon ng AVRCP ng Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bersyon ng MAP ng Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index e6d938047fcf..f01f3faed73e 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Adsız Bluetooth cihazlarını göster"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mutlak sesi iptal et"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'yi etkileştir"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Gelişmiş Bağlantı"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Sürümü"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Sürümünü seçin"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Sürümü"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index cf1fafd8f190..9ca2f062127a 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показувати пристрої Bluetooth без назв"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Вимкнути абсолютну гучність"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Увімкнути Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Покращене з\'єднання"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Виберіть версію Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index b7fbe6fad392..8953f50078f1 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"بغیر نام والے بلوٹوتھ آلات دکھائیں"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"مطلق والیوم کو غیر فعال کریں"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche فعال کریں"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"بہتر کردہ کنیکٹوٹی"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"بلوٹوتھ AVRCP ورژن"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"بلوٹوتھ AVRCP ورژن منتخب کریں"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"بلوٹوتھ MAP ورژن"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index f81731ab2723..f25b3ac3c37f 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth qurilmalarini nomlarisiz ko‘rsatish"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Tovush balandligining mutlaq darajasini faolsizlantirish"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche funksiyasini yoqish"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kuchaytirilgan aloqa"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versiyasi"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP versiyasini tanlang"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versiyasi"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index b7ccf8d85677..b5798f31d0f9 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Hiển thị các thiết bị Bluetooth không có tên"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Vô hiệu hóa âm lượng tuyệt đối"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bật tính năng Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kết nối nâng cao"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Phiên bản Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chọn phiên bản Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Phiên bản Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 75c1333c37c1..c4dcfff1b579 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"显示没有名称的蓝牙设备"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用绝对音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"启用“Gabeldorsche”"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"增强连接性"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"蓝牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"选择蓝牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"蓝牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 26ddfb13717b..e04651cac5f6 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"強化連線功能"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選擇藍牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 72ea0439b1f6..a1ae6b6c27a1 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"加強型連線"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選取藍牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 6b8739fd017a..2dafad8114d8 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bonisa amadivayisi e-Bluetooth ngaphandle kwamagama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Khubaza ivolumu ngokuphelele"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Nika amandla i-Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Ukuxhumeka Okuthuthukisiwe"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Inguqulo ye-Bluetooth ye-AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Khetha inguqulo ye-Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Inguqulo ye-Bluetooth MAP"</string> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index c1543fd91060..bfd5b1ccbc25 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -81,6 +81,7 @@ public class SettingsHelper { sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_START_TIME); sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME); sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); + sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); } private interface SettingsLookup { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index aa960875ec6f..319b44ce216f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -320,19 +320,6 @@ <!-- Permissions required for CTS test - AdbManagerTest --> <uses-permission android:name="android.permission.MANAGE_DEBUGGING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp --> - <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases --> - <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <uses-permission android:name="android.car.permission.CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <uses-permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <uses-permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" /> - <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml index 3c641afea0d6..ed870f8bb2ef 100644 --- a/packages/SystemUI/res/layout/media_view.xml +++ b/packages/SystemUI/res/layout/media_view.xml @@ -210,8 +210,98 @@ android:layout_width="@dimen/qs_media_icon_size" android:layout_height="@dimen/qs_media_icon_size" /> - <!-- Buttons to remove this view when no longer needed --> - <include - layout="@layout/qs_media_panel_options" - android:visibility="gone" /> + <!-- Constraints are set here as they are the same regardless of host --> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/qs_media_panel_outer_padding" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:id="@+id/media_text" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="@color/media_primary_text" + android:text="@string/controls_media_title" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/remove_text" + app:layout_constraintVertical_chainStyle="spread_inside"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:id="@+id/remove_text" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:singleLine="true" + android:textColor="@color/media_primary_text" + android:text="@string/controls_media_close_session" + app:layout_constraintTop_toBottomOf="@id/media_text" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/settings"/> + + <FrameLayout + android:id="@+id/settings" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/remove_text"> + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="@android:color/white" + android:text="@string/controls_media_settings_button" /> + </FrameLayout> + + <FrameLayout + android:id="@+id/cancel" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/dismiss" > + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="@android:color/white" + android:text="@string/cancel" /> + </FrameLayout> + + <FrameLayout + android:id="@+id/dismiss" + android:background="@drawable/qs_media_light_source" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" + android:minWidth="48dp" + android:minHeight="48dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + + <TextView + android:layout_gravity="bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textColor="@android:color/white" + android:text="@string/controls_media_dismiss_button" + /> + </FrameLayout> </com.android.systemui.util.animation.TransitionLayout> diff --git a/packages/SystemUI/res/layout/qs_media_panel_options.xml b/packages/SystemUI/res/layout/qs_media_panel_options.xml deleted file mode 100644 index e72c0e85fb26..000000000000 --- a/packages/SystemUI/res/layout/qs_media_panel_options.xml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2019 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 - --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/qs_media_controls_options" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center" - android:padding="16dp" - android:orientation="vertical"> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_weight="1" - android:minWidth="48dp" - android:layout_gravity="start|bottom" - android:gravity="bottom" - android:id="@+id/remove" - android:orientation="horizontal"> - <ImageView - android:layout_width="18dp" - android:layout_height="18dp" - android:id="@+id/remove_icon" - android:layout_marginEnd="16dp" - android:tint="@color/media_primary_text" - android:src="@drawable/ic_clear"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/remove_text" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:singleLine="true" - android:textColor="@color/media_primary_text" - android:text="@string/controls_media_close_session" /> - </LinearLayout> - <TextView - android:id="@+id/cancel" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_weight="1" - android:minWidth="48dp" - android:layout_gravity="end|bottom" - android:gravity="bottom" - android:fontFamily="@*android:string/config_headlineFontFamilyMedium" - android:textColor="@android:color/white" - android:text="@string/cancel" /> -</LinearLayout> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index fa620df12b87..fba43a628387 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -515,13 +515,11 @@ <!-- Whether or not to add a "people" notifications section --> <bool name="config_usePeopleFiltering">false</bool> - <!-- Defines the blacklist for system icons. That is to say, the icons in the status bar that - are part of the blacklist are never displayed. Each item in the blacklist must be a string - defined in core/res/res/config.xml to properly blacklist the icon. - - TODO: See if we can rename this config variable. + <!-- Defines system icons to be excluded from the display. That is to say, the icons in the + status bar that are part of this list are never displayed. Each item in the list must be a + string defined in core/res/res/config.xml to properly exclude the icon. --> - <string-array name="config_statusBarIconBlackList" translatable="false"> + <string-array name="config_statusBarIconsToExclude" translatable="false"> <item>@*android:string/status_bar_rotate</item> <item>@*android:string/status_bar_headset</item> </string-array> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 77d3f4513957..823c1ff2fdd0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2799,7 +2799,7 @@ <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] --> <string name="controls_media_close_session">Hide the current session.</string> <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] --> - <string name="controls_media_dismiss_button">Hide</string> + <string name="controls_media_dismiss_button">Dismiss</string> <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] --> <string name="controls_media_resume">Resume</string> <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] --> diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index 816bcf8f274b..d5f74a86fd94 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -53,8 +53,8 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION; @VisibleForTesting - protected WindowMagnificationController mWindowMagnificationController; - protected final ModeSwitchesController mModeSwitchesController; + protected WindowMagnificationAnimationController mWindowMagnificationAnimationController; + private final ModeSwitchesController mModeSwitchesController; private final Handler mHandler; private final AccessibilityManager mAccessibilityManager; private final CommandQueue mCommandQueue; @@ -72,6 +72,11 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall Context.ACCESSIBILITY_SERVICE); mCommandQueue = commandQueue; mModeSwitchesController = modeSwitchesController; + final WindowMagnificationController controller = new WindowMagnificationController(mContext, + mHandler, new SfVsyncFrameCallbackProvider(), null, + new SurfaceControl.Transaction(), this); + mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( + mContext, controller); } @Override @@ -81,9 +86,7 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall return; } mLastConfiguration.setTo(newConfig); - if (mWindowMagnificationController != null) { - mWindowMagnificationController.onConfigurationChanged(configDiff); - } + mWindowMagnificationAnimationController.onConfigurationChanged(configDiff); if (mModeSwitchesController != null) { mModeSwitchesController.onConfigurationChanged(configDiff); } @@ -97,39 +100,25 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall @MainThread void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) { //TODO: b/144080869 support multi-display. - if (mWindowMagnificationController == null) { - mWindowMagnificationController = new WindowMagnificationController(mContext, - mHandler, - new SfVsyncFrameCallbackProvider(), - null, new SurfaceControl.Transaction(), - this); - } - mWindowMagnificationController.enableWindowMagnification(scale, centerX, centerY); + mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY); } @MainThread void setScale(int displayId, float scale) { //TODO: b/144080869 support multi-display. - if (mWindowMagnificationController != null) { - mWindowMagnificationController.setScale(scale); - } + mWindowMagnificationAnimationController.setScale(scale); } @MainThread void moveWindowMagnifier(int displayId, float offsetX, float offsetY) { //TODO: b/144080869 support multi-display. - if (mWindowMagnificationController != null) { - mWindowMagnificationController.moveWindowMagnifier(offsetX, offsetY); - } + mWindowMagnificationAnimationController.moveWindowMagnifier(offsetX, offsetY); } @MainThread void disableWindowMagnification(int displayId) { //TODO: b/144080869 support multi-display. - if (mWindowMagnificationController != null) { - mWindowMagnificationController.deleteWindowMagnification(); - } - mWindowMagnificationController = null; + mWindowMagnificationAnimationController.deleteWindowMagnification(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java new file mode 100644 index 000000000000..ae51623f3dc2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java @@ -0,0 +1,272 @@ +/* + * 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.systemui.accessibility; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.IntDef; +import android.content.Context; +import android.content.res.Resources; +import android.util.Log; +import android.view.animation.AccelerateInterpolator; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Provides same functionality of {@link WindowMagnificationController}. Some methods run with + * the animation. + */ +class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUpdateListener, + Animator.AnimatorListener { + + private static final String TAG = "WindowMagnificationBridge"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + @Retention(RetentionPolicy.SOURCE) + @IntDef({STATE_DISABLED, STATE_ENABLED, STATE_DISABLING, STATE_ENABLING}) + @interface MagnificationState {} + + //The window magnification is disabled. + private static final int STATE_DISABLED = 0; + //The window magnification is enabled. + private static final int STATE_ENABLED = 1; + //The window magnification is going to be disabled when the animation is end. + private static final int STATE_DISABLING = 2; + //The animation is running for enabling the window magnification. + private static final int STATE_ENABLING = 3; + + private final WindowMagnificationController mController; + private final ValueAnimator mValueAnimator; + private final AnimationSpec mStartSpec = new AnimationSpec(); + private final AnimationSpec mEndSpec = new AnimationSpec(); + private final Context mContext; + + @MagnificationState + private int mState = STATE_DISABLED; + + WindowMagnificationAnimationController( + Context context, WindowMagnificationController controller) { + this(context, controller, newValueAnimator(context.getResources())); + } + + @VisibleForTesting + WindowMagnificationAnimationController(Context context, + WindowMagnificationController controller, ValueAnimator valueAnimator) { + mContext = context; + mController = controller; + mValueAnimator = valueAnimator; + mValueAnimator.addUpdateListener(this); + mValueAnimator.addListener(this); + } + + /** + * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)} + * with transition animation. If the window magnification is not enabled, the scale will start + * from 1.0 and the center won't be changed during the animation. If {@link #mState} is + * {@code STATE_DISABLING}, the animation runs in reverse. + * + * @param scale the target scale, or {@link Float#NaN} to leave unchanged. + * @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. + * + * @see #onAnimationUpdate(ValueAnimator) + */ + void enableWindowMagnification(float scale, float centerX, float centerY) { + if (mState == STATE_ENABLING) { + mValueAnimator.cancel(); + } + setupEnableAnimationSpecs(scale, centerX, centerY); + + if (mEndSpec.equals(mStartSpec)) { + setState(STATE_ENABLED); + } else { + if (mState == STATE_DISABLING) { + mValueAnimator.reverse(); + } else { + mValueAnimator.start(); + } + setState(STATE_ENABLING); + } + } + + private void setupEnableAnimationSpecs(float scale, float centerX, float centerY) { + final float currentScale = mController.getScale(); + final float currentCenterX = mController.getCenterX(); + final float currentCenterY = mController.getCenterY(); + + if (mState == STATE_DISABLED) { + //We don't need to offset the center during the animation. + mStartSpec.set(/* scale*/ 1.0f, centerX, centerY); + mEndSpec.set(Float.isNaN(scale) ? mContext.getResources().getInteger( + R.integer.magnification_default_scale) : scale, centerX, centerY); + } else { + mStartSpec.set(currentScale, currentCenterX, currentCenterY); + mEndSpec.set(Float.isNaN(scale) ? currentScale : scale, + Float.isNaN(centerX) ? currentCenterX : centerX, + Float.isNaN(centerY) ? currentCenterY : centerY); + } + if (DEBUG) { + Log.d(TAG, "SetupEnableAnimationSpecs : mStartSpec = " + mStartSpec + ", endSpec = " + + mEndSpec); + } + } + + /** + * Wraps {@link WindowMagnificationController#setScale(float)}. If the animation is + * running, it has no effect. + */ + void setScale(float scale) { + if (mValueAnimator.isRunning()) { + return; + } + mController.setScale(scale); + } + + /** + * Wraps {@link WindowMagnificationController#deleteWindowMagnification()}} with transition + * animation. If the window magnification is enabling, it runs the animation in reverse. + */ + void deleteWindowMagnification() { + if (mState == STATE_DISABLED || mState == STATE_DISABLING) { + return; + } + mStartSpec.set(/* scale*/ 1.0f, Float.NaN, Float.NaN); + mEndSpec.set(/* scale*/ mController.getScale(), Float.NaN, Float.NaN); + + mValueAnimator.reverse(); + setState(STATE_DISABLING); + } + + /** + * Wraps {@link WindowMagnificationController#moveWindowMagnifier(float, float)}. If the + * animation is running, it has no effect. + * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in + * current screen pixels. + * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in + * current screen pixels. + */ + void moveWindowMagnifier(float offsetX, float offsetY) { + if (mValueAnimator.isRunning()) { + return; + } + mController.moveWindowMagnifier(offsetX, offsetY); + } + + void onConfigurationChanged(int configDiff) { + mController.onConfigurationChanged(configDiff); + } + + private void setState(@MagnificationState int state) { + if (DEBUG) { + Log.d(TAG, "setState from " + mState + " to " + state); + } + mState = state; + } + + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mState == STATE_DISABLING) { + mController.deleteWindowMagnification(); + setState(STATE_DISABLED); + } else if (mState == STATE_ENABLING) { + setState(STATE_ENABLED); + } else { + Log.w(TAG, "onAnimationEnd unexpected state:" + mState); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final float fract = animation.getAnimatedFraction(); + final float sentScale = mStartSpec.mScale + (mEndSpec.mScale - mStartSpec.mScale) * fract; + final float centerX = + mStartSpec.mCenterX + (mEndSpec.mCenterX - mStartSpec.mCenterX) * fract; + final float centerY = + mStartSpec.mCenterY + (mEndSpec.mCenterY - mStartSpec.mCenterY) * fract; + mController.enableWindowMagnification(sentScale, centerX, centerY); + } + + private static ValueAnimator newValueAnimator(Resources resources) { + final ValueAnimator valueAnimator = new ValueAnimator(); + valueAnimator.setDuration( + resources.getInteger(com.android.internal.R.integer.config_longAnimTime)); + valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f)); + valueAnimator.setFloatValues(0.0f, 1.0f); + return valueAnimator; + } + + private static class AnimationSpec { + private float mScale = Float.NaN; + private float mCenterX = Float.NaN; + private float mCenterY = Float.NaN; + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other == null || getClass() != other.getClass()) { + return false; + } + + final AnimationSpec s = (AnimationSpec) other; + return mScale == s.mScale && mCenterX == s.mCenterX && mCenterY == s.mCenterY; + } + + @Override + public int hashCode() { + int result = (mScale != +0.0f ? Float.floatToIntBits(mScale) : 0); + result = 31 * result + (mCenterX != +0.0f ? Float.floatToIntBits(mCenterX) : 0); + result = 31 * result + (mCenterY != +0.0f ? Float.floatToIntBits(mCenterY) : 0); + return result; + } + + void set(float scale, float centerX, float centerY) { + mScale = scale; + mCenterX = centerX; + mCenterY = centerY; + } + + @Override + public String toString() { + return "AnimationSpec{" + + "mScale=" + mScale + + ", mCenterX=" + mCenterX + + ", mCenterY=" + mCenterY + + '}'; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 798b751c03ee..6d3e8ba68395 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -150,7 +150,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mMirrorViewGeometryVsyncCallback = l -> { - if (mMirrorView != null && mMirrorSurface != null) { + if (isWindowVisible() && mMirrorSurface != null) { calculateSourceBounds(mMagnificationFrame, mScale); // The final destination for the magnification surface should be at 0,0 // since the ViewRootImpl's position will change @@ -502,7 +502,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold /** * Enables window magnification with specified parameters. * - * @param scale the target scale + * @param scale the target scale, or {@link Float#NaN} to leave unchanged * @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, @@ -513,10 +513,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold : centerX - mMagnificationFrame.exactCenterX(); final float offsetY = Float.isNaN(centerY) ? 0 : centerY - mMagnificationFrame.exactCenterY(); - mScale = scale; + mScale = Float.isNaN(scale) ? mScale : scale; setMagnificationFrameBoundary(); updateMagnificationFramePosition((int) offsetX, (int) offsetY); - if (mMirrorView == null) { + if (!isWindowVisible()) { createMirrorWindow(); showControls(); } else { @@ -527,10 +527,10 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold /** * Sets the scale of the magnified region if it's visible. * - * @param scale the target scale + * @param scale the target scale, or {@link Float#NaN} to leave unchanged */ void setScale(float scale) { - if (mMirrorView == null || mScale == scale) { + if (!isWindowVisible() || mScale == scale) { return; } enableWindowMagnification(scale, Float.NaN, Float.NaN); @@ -552,4 +552,35 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold modifyWindowMagnification(mTransaction); } } + + /** + * Gets the scale. + * @return {@link Float#NaN} if the window is invisible. + */ + float getScale() { + return isWindowVisible() ? mScale : Float.NaN; + } + + /** + * Returns the screen-relative X coordinate of the center of the magnified bounds. + * + * @return the X coordinate. {@link Float#NaN} if the window is invisible. + */ + float getCenterX() { + return isWindowVisible() ? mMagnificationFrame.exactCenterX() : Float.NaN; + } + + /** + * Returns the screen-relative Y coordinate of the center of the magnified bounds. + * + * @return the Y coordinate. {@link Float#NaN} if the window is invisible. + */ + float getCenterY() { + return isWindowVisible() ? mMagnificationFrame.exactCenterY() : Float.NaN; + } + + //The window is visible when it is existed. + private boolean isWindowVisible() { + return mMirrorView != null; + } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 4df66602bb7e..6512624f5064 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -281,9 +281,11 @@ public class AppOpsControllerImpl implements AppOpsController, * @return {@code true} iff the app-op for should be shown to the user */ private boolean isUserVisible(int appOpCode, int uid, String packageName) { - // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission - // which may be user senstive, so for now always show it to the user. - if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) { + // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION + // does not correspond to a platform permission + // which may be user sensitive, so for now always show it to the user. + if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW + || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index de5316885b94..7f610d1a8467 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -3,7 +3,6 @@ package com.android.systemui.media import android.content.Context import android.content.Intent import android.content.res.Configuration -import android.graphics.Color import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.util.Log import android.util.MathUtils @@ -151,7 +150,7 @@ class MediaCarouselController @Inject constructor( pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator) mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator, executor, mediaManager::onSwipeToDismiss, this::updatePageIndicatorLocation, - falsingManager) + this::closeGuts, falsingManager) isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL inflateSettingsButton() mediaContent = mediaCarousel.requireViewById(R.id.media_carousel) @@ -470,6 +469,12 @@ class MediaCarouselController @Inject constructor( } } + fun closeGuts() { + mediaPlayers.values.forEach { + it.closeGuts(true) + } + } + /** * Update the size of the carousel, remeasuring it if necessary. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index 3096908aca21..77cac5023db3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -56,6 +56,7 @@ class MediaCarouselScrollHandler( private val mainExecutor: DelayableExecutor, private val dismissCallback: () -> Unit, private var translationChangedListener: () -> Unit, + private val closeGuts: () -> Unit, private val falsingManager: FalsingManager ) { /** @@ -452,6 +453,7 @@ class MediaCarouselScrollHandler( val nowScrolledIn = scrollIntoCurrentMedia != 0 if (newIndex != activeMediaIndex || wasScrolledIn != nowScrolledIn) { activeMediaIndex = newIndex + closeGuts() updatePlayerVisibilities() } val relativeLocation = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 3fc162ead6d1..e55678dc986b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -16,6 +16,8 @@ package com.android.systemui.media; +import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS; + import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -45,6 +47,7 @@ import com.android.settingslib.widget.AdaptiveIcon; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.util.animation.TransitionLayout; import java.util.List; @@ -52,6 +55,8 @@ import java.util.concurrent.Executor; import javax.inject.Inject; +import dagger.Lazy; + /** * A view controller used for Media Playback. */ @@ -59,6 +64,8 @@ public class MediaControlPanel { private static final String TAG = "MediaControlPanel"; private static final float DISABLED_ALPHA = 0.38f; + private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS); + // Button IDs for QS controls static final int[] ACTION_IDS = { R.id.action0, @@ -78,6 +85,8 @@ public class MediaControlPanel { private MediaViewController mMediaViewController; private MediaSession.Token mToken; private MediaController mController; + private KeyguardDismissUtil mKeyguardDismissUtil; + private Lazy<MediaDataManager> mMediaDataManagerLazy; private int mBackgroundColor; private int mAlbumArtSize; private int mAlbumArtRadius; @@ -93,12 +102,15 @@ public class MediaControlPanel { @Inject public MediaControlPanel(Context context, @Background Executor backgroundExecutor, ActivityStarter activityStarter, MediaViewController mediaViewController, - SeekBarViewModel seekBarViewModel) { + SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager, + KeyguardDismissUtil keyguardDismissUtil) { mContext = context; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; mSeekBarViewModel = seekBarViewModel; mMediaViewController = mediaViewController; + mMediaDataManagerLazy = lazyMediaDataManager; + mKeyguardDismissUtil = keyguardDismissUtil; loadDimens(); mViewOutlineProvider = new ViewOutlineProvider() { @@ -174,6 +186,21 @@ public class MediaControlPanel { mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver); mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar()); mMediaViewController.attach(player); + + mViewHolder.getPlayer().setOnLongClickListener(v -> { + if (!mMediaViewController.isGutsVisible()) { + mMediaViewController.openGuts(); + return true; + } else { + return false; + } + }); + mViewHolder.getCancel().setOnClickListener(v -> { + closeGuts(); + }); + mViewHolder.getSettings().setOnClickListener(v -> { + mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */); + }); } /** @@ -205,6 +232,7 @@ public class MediaControlPanel { PendingIntent clickIntent = data.getClickIntent(); if (clickIntent != null) { mViewHolder.getPlayer().setOnClickListener(v -> { + if (mMediaViewController.isGutsVisible()) return; mActivityStarter.postStartActivityDismissingKeyguard(clickIntent); }); } @@ -329,14 +357,38 @@ public class MediaControlPanel { final MediaController controller = getController(); mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller)); - // Set up long press menu - // TODO: b/156036025 bring back media guts + // Dismiss + mViewHolder.getDismiss().setOnClickListener(v -> { + if (data.getNotificationKey() != null) { + closeGuts(); + mKeyguardDismissUtil.executeWhenUnlocked(() -> { + mMediaDataManagerLazy.get().dismissMediaData(data.getNotificationKey(), + MediaViewController.GUTS_ANIMATION_DURATION + 100); + return true; + }, /* requiresShadeOpen */ true); + } else { + Log.w(TAG, "Dismiss media with null notification. Token uid=" + + data.getToken().getUid()); + } + }); // TODO: We don't need to refresh this state constantly, only if the state actually changed // to something which might impact the measurement mMediaViewController.refreshState(); } + /** + * Close the guts for this player. + * @param immediate {@code true} if it should be closed without animation + */ + public void closeGuts(boolean immediate) { + mMediaViewController.closeGuts(immediate); + } + + private void closeGuts() { + closeGuts(false); + } + @UiThread private Drawable scaleDrawable(Icon icon) { if (icon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index d82150f2346b..8a51c8515852 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.notification.MediaNotificationProcessor import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert import com.android.systemui.util.Utils +import com.android.systemui.util.concurrency.DelayableExecutor import java.io.FileDescriptor import java.io.IOException import java.io.PrintWriter @@ -90,7 +91,7 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean { class MediaDataManager( private val context: Context, @Background private val backgroundExecutor: Executor, - @Main private val foregroundExecutor: Executor, + @Main private val foregroundExecutor: DelayableExecutor, private val mediaControllerFactory: MediaControllerFactory, private val broadcastDispatcher: BroadcastDispatcher, dumpManager: DumpManager, @@ -107,7 +108,7 @@ class MediaDataManager( constructor( context: Context, @Background backgroundExecutor: Executor, - @Main foregroundExecutor: Executor, + @Main foregroundExecutor: DelayableExecutor, mediaControllerFactory: MediaControllerFactory, dumpManager: DumpManager, broadcastDispatcher: BroadcastDispatcher, @@ -183,10 +184,7 @@ class MediaDataManager( val listenersCopy = listeners.toSet() val toRemove = mediaEntries.filter { it.value.packageName == packageName } toRemove.forEach { - mediaEntries.remove(it.key) - listenersCopy.forEach { listener -> - listener.onMediaDataRemoved(it.key) - } + removeEntry(it.key, listenersCopy) } } @@ -269,6 +267,18 @@ class MediaDataManager( } } + private fun removeEntry(key: String, listenersCopy: Set<Listener>) { + mediaEntries.remove(key) + listenersCopy.forEach { + it.onMediaDataRemoved(key) + } + } + + fun dismissMediaData(key: String, delay: Long) { + val listenersCopy = listeners.toSet() + foregroundExecutor.executeDelayed({ removeEntry(key, listenersCopy) }, delay) + } + private fun loadMediaDataInBgForResumption( userId: Int, desc: MediaDescription, diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index fc33391a9ad1..70f01d576a9c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -293,6 +293,13 @@ class MediaHierarchyManager @Inject constructor( return viewHost } + /** + * Close the guts in all players in [MediaCarouselController]. + */ + fun closeGuts() { + mediaCarouselController.closeGuts() + } + private fun createUniqueObjectHost(): UniqueObjectHostView { val viewHost = UniqueObjectHostView(context) viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt index 38817d7b579e..92eeed46388d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt @@ -37,6 +37,11 @@ class MediaViewController @Inject constructor( private val mediaHostStatesManager: MediaHostStatesManager ) { + companion object { + @JvmField + val GUTS_ANIMATION_DURATION = 500L + } + /** * A listener when the current dimensions of the player change */ @@ -169,6 +174,12 @@ class MediaViewController @Inject constructor( */ val expandedLayout = ConstraintSet() + /** + * Whether the guts are visible for the associated player. + */ + var isGutsVisible = false + private set + init { collapsedLayout.load(context, R.xml.media_collapsed) expandedLayout.load(context, R.xml.media_expanded) @@ -189,6 +200,37 @@ class MediaViewController @Inject constructor( configurationController.removeCallback(configurationListener) } + /** + * Show guts with an animated transition. + */ + fun openGuts() { + if (isGutsVisible) return + isGutsVisible = true + animatePendingStateChange(GUTS_ANIMATION_DURATION, 0L) + setCurrentState(currentStartLocation, + currentEndLocation, + currentTransitionProgress, + applyImmediately = false) + } + + /** + * Close the guts for the associated player. + * + * @param immediate if `false`, it will animate the transition. + */ + @JvmOverloads + fun closeGuts(immediate: Boolean = false) { + if (!isGutsVisible) return + isGutsVisible = false + if (!immediate) { + animatePendingStateChange(GUTS_ANIMATION_DURATION, 0L) + } + setCurrentState(currentStartLocation, + currentEndLocation, + currentTransitionProgress, + applyImmediately = immediate) + } + private fun ensureAllMeasurements() { val mediaStates = mediaHostStatesManager.mediaHostStates for (entry in mediaStates) { @@ -203,6 +245,24 @@ class MediaViewController @Inject constructor( if (expansion > 0) expandedLayout else collapsedLayout /** + * Set the views to be showing/hidden based on the [isGutsVisible] for a given + * [TransitionViewState]. + */ + private fun setGutsViewState(viewState: TransitionViewState) { + PlayerViewHolder.controlsIds.forEach { id -> + viewState.widgetStates.get(id)?.let { state -> + // Make sure to use the unmodified state if guts are not visible + state.alpha = if (isGutsVisible) 0f else state.alpha + state.gone = if (isGutsVisible) true else state.gone + } + } + PlayerViewHolder.gutsIds.forEach { id -> + viewState.widgetStates.get(id)?.alpha = if (isGutsVisible) 1f else 0f + viewState.widgetStates.get(id)?.gone = !isGutsVisible + } + } + + /** * Obtain a new viewState for a given media state. This usually returns a cached state, but if * it's not available, it will recreate one by measuring, which may be expensive. */ @@ -211,7 +271,7 @@ class MediaViewController @Inject constructor( return null } // Only a subset of the state is relevant to get a valid viewState. Let's get the cachekey - var cacheKey = getKey(state, tmpKey) + var cacheKey = getKey(state, isGutsVisible, tmpKey) val viewState = viewStates[cacheKey] if (viewState != null) { // we already have cached this measurement, let's continue @@ -228,6 +288,7 @@ class MediaViewController @Inject constructor( constraintSetForExpansion(state.expansion), TransitionViewState()) + setGutsViewState(result) // We don't want to cache interpolated or null states as this could quickly fill up // our cache. We only cache the start and the end states since the interpolation // is cheap @@ -252,11 +313,12 @@ class MediaViewController @Inject constructor( return result } - private fun getKey(state: MediaHostState, result: CacheKey): CacheKey { + private fun getKey(state: MediaHostState, guts: Boolean, result: CacheKey): CacheKey { result.apply { heightMeasureSpec = state.measurementInput?.heightMeasureSpec ?: 0 widthMeasureSpec = state.measurementInput?.widthMeasureSpec ?: 0 expansion = state.expansion + gutsVisible = guts } return result } @@ -432,5 +494,6 @@ class MediaViewController @Inject constructor( private data class CacheKey( var widthMeasureSpec: Int = -1, var heightMeasureSpec: Int = -1, - var expansion: Float = 0.0f + var expansion: Float = 0.0f, + var gutsVisible: Boolean = false ) diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt index 600fdc27ef89..11551aca80f2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt @@ -23,6 +23,7 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.SeekBar import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.R import com.android.systemui.util.animation.TransitionLayout @@ -59,6 +60,11 @@ class PlayerViewHolder private constructor(itemView: View) { val action3 = itemView.requireViewById<ImageButton>(R.id.action3) val action4 = itemView.requireViewById<ImageButton>(R.id.action4) + // Settings screen + val cancel = itemView.requireViewById<View>(R.id.cancel) + val dismiss = itemView.requireViewById<View>(R.id.dismiss) + val settings = itemView.requireViewById<View>(R.id.settings) + init { (player.background as IlluminationDrawable).let { it.registerLightSource(seamless) @@ -67,6 +73,9 @@ class PlayerViewHolder private constructor(itemView: View) { it.registerLightSource(action2) it.registerLightSource(action3) it.registerLightSource(action4) + it.registerLightSource(cancel) + it.registerLightSource(dismiss) + it.registerLightSource(settings) } } @@ -83,9 +92,6 @@ class PlayerViewHolder private constructor(itemView: View) { } } - // Settings screen - val options = itemView.requireViewById<View>(R.id.qs_media_controls_options) - companion object { /** * Creates a PlayerViewHolder. @@ -105,5 +111,29 @@ class PlayerViewHolder private constructor(itemView: View) { progressTimes.layoutDirection = View.LAYOUT_DIRECTION_LTR } } + + val controlsIds = setOf( + R.id.icon, + R.id.app_name, + R.id.album_art, + R.id.header_title, + R.id.header_artist, + R.id.media_seamless, + R.id.notification_media_progress_time, + R.id.media_progress_bar, + R.id.action0, + R.id.action1, + R.id.action2, + R.id.action3, + R.id.action4, + R.id.icon + ) + val gutsIds = setOf( + R.id.media_text, + R.id.remove_text, + R.id.cancel, + R.id.dismiss, + R.id.settings + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 118e0bd5f5f8..025341cf4fba 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -99,6 +99,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements private final Handler mUpdateHandler; private final PipBoundsHandler mPipBoundsHandler; private final PipAnimationController mPipAnimationController; + private final PipUiEventLogger mPipUiEventLoggerLogger; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); private final Rect mLastReportedBounds = new Rect(); private final int mEnterExitAnimationDuration; @@ -209,7 +210,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @Nullable Divider divider, @NonNull DisplayController displayController, - @NonNull PipAnimationController pipAnimationController) { + @NonNull PipAnimationController pipAnimationController, + @NonNull PipUiEventLogger pipUiEventLogger) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsHandler = boundsHandler; @@ -217,6 +219,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; mPipAnimationController = pipAnimationController; + mPipUiEventLoggerLogger = pipUiEventLogger; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitDivider = divider; displayController.addDisplayWindowListener(this); @@ -279,6 +282,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements return; } + mPipUiEventLoggerLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); final Configuration initialConfig = mInitialState.remove(mToken.asBinder()); final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() != mPipBoundsHandler.getDisplayRotation(); @@ -381,6 +386,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; + mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); + mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); + if (mShouldDeferEnteringPip) { if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); // if deferred, hide the surface till fixed rotation is completed @@ -514,6 +522,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements mPictureInPictureParams = null; mInPip = false; mExitingPip = false; + mPipUiEventLoggerLogger.setTaskInfo(null); } @Override @@ -629,7 +638,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements * {@link PictureInPictureParams} would affect the bounds. */ private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) { - final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals( + final boolean changed = (mPictureInPictureParams == null) || !Objects.equals( mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational()); if (changed) { mPictureInPictureParams = params; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java new file mode 100644 index 000000000000..5e2cd9c111b2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -0,0 +1,97 @@ +/* + * 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.systemui.pip; + +import android.app.TaskInfo; + +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; + +import javax.inject.Inject; +import javax.inject.Singleton; + + +/** + * Helper class that ends PiP log to UiEvent, see also go/uievent + */ +@Singleton +public class PipUiEventLogger { + + private final UiEventLogger mUiEventLogger; + + private TaskInfo mTaskInfo; + + @Inject + public PipUiEventLogger(UiEventLogger uiEventLogger) { + mUiEventLogger = uiEventLogger; + } + + public void setTaskInfo(TaskInfo taskInfo) { + mTaskInfo = taskInfo; + } + + /** + * Sends log via UiEvent, reference go/uievent for how to debug locally + */ + public void log(PipUiEventEnum event) { + if (mTaskInfo == null) { + return; + } + mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName()); + } + + /** + * Enums for logging the PiP events to UiEvent + */ + public enum PipUiEventEnum implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Activity enters picture-in-picture mode") + PICTURE_IN_PICTURE_ENTER(603), + + @UiEvent(doc = "Expands from picture-in-picture to fullscreen") + PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604), + + @UiEvent(doc = "Removes picture-in-picture by tap close button") + PICTURE_IN_PICTURE_TAP_TO_REMOVE(605), + + @UiEvent(doc = "Removes picture-in-picture by drag to dismiss area") + PICTURE_IN_PICTURE_DRAG_TO_REMOVE(606), + + @UiEvent(doc = "Shows picture-in-picture menu") + PICTURE_IN_PICTURE_SHOW_MENU(607), + + @UiEvent(doc = "Hides picture-in-picture menu") + PICTURE_IN_PICTURE_HIDE_MENU(608), + + @UiEvent(doc = "Changes the aspect ratio of picture-in-picture window. This is inherited" + + " from previous Tron-based logging and currently not in use.") + PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609), + + @UiEvent(doc = "User resize of the picture-in-picture window") + PICTURE_IN_PICTURE_RESIZE(610); + + private final int mId; + + PipUiEventEnum(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 7c5054cb0544..9dfa864e2ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -47,6 +47,7 @@ import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -259,7 +260,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio PipSnapAlgorithm pipSnapAlgorithm, PipTaskOrganizer pipTaskOrganizer, SysUiState sysUiState, - ConfigurationController configController) { + ConfigurationController configController, + PipUiEventLogger pipUiEventLogger) { mContext = context; mActivityManager = ActivityManager.getService(); @@ -280,7 +282,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMediaController, mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, - floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState); + floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState, + pipUiEventLogger); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); @@ -394,12 +397,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio * Update the bounds used to save the re-entry size and snap fraction when exiting PIP. */ public void updateReentryBounds(Rect bounds) { - // On phones, the expansion animation that happens on pip tap before restoring - // to fullscreen makes it so that the last reported bounds are the expanded - // bounds. We want to restore to the unexpanded bounds when re-entering pip, - // so we use the bounds before expansion (normal) instead of the reported - // bounds. - Rect reentryBounds = mTouchHandler.getNormalBounds(); + final Rect reentryBounds = mTouchHandler.getUserResizeBounds(); float snapFraction = mPipBoundsHandler.getSnapFraction(bounds); mPipBoundsHandler.applySnapFraction(reentryBounds, snapFraction); mReentryBounds.set(reentryBounds); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index d884fa956edc..9c42f8bff378 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -52,6 +52,7 @@ import com.android.internal.policy.TaskResizingAlgorithm; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.util.DeviceConfigProxy; import com.android.wm.shell.R; @@ -88,6 +89,7 @@ public class PipResizeGestureHandler { private final Point mMaxSize = new Point(); private final Point mMinSize = new Point(); private final Rect mLastResizeBounds = new Rect(); + private final Rect mUserResizeBounds = new Rect(); private final Rect mLastDownBounds = new Rect(); private final Rect mDragCornerSize = new Rect(); private final Rect mTmpTopLeftCorner = new Rect(); @@ -109,13 +111,15 @@ public class PipResizeGestureHandler { private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; private PipTaskOrganizer mPipTaskOrganizer; + private PipUiEventLogger mPipUiEventLogger; private int mCtrlType; public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler, PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, - Runnable updateMovementBoundsRunnable, SysUiState sysUiState) { + Runnable updateMovementBoundsRunnable, SysUiState sysUiState, + PipUiEventLogger pipUiEventLogger) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -125,6 +129,7 @@ public class PipResizeGestureHandler { mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mSysUiState = sysUiState; + mPipUiEventLogger = pipUiEventLogger; context.getDisplay().getRealSize(mMaxSize); reloadResources(); @@ -181,6 +186,7 @@ public class PipResizeGestureHandler { void onActivityUnpinned() { mIsAttached = false; + mUserResizeBounds.setEmpty(); updateIsEnabled(); } @@ -329,6 +335,7 @@ public class PipResizeGestureHandler { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (!mLastResizeBounds.isEmpty()) { + mUserResizeBounds.set(mLastResizeBounds); mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, (Rect bounds) -> { new Handler(Looper.getMainLooper()).post(() -> { @@ -337,6 +344,8 @@ public class PipResizeGestureHandler { resetState(); }); }); + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE); } else { resetState(); } @@ -351,6 +360,14 @@ public class PipResizeGestureHandler { mThresholdCrossed = false; } + void setUserResizeBounds(Rect bounds) { + mUserResizeBounds.set(bounds); + } + + Rect getUserResizeBounds() { + return mUserResizeBounds; + } + void updateMaxSize(int maxX, int maxY) { mMaxSize.set(maxX, maxY); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 7fc2823fc248..b20ea4e5c836 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -34,7 +34,6 @@ import android.graphics.drawable.TransitionDrawable; import android.os.Handler; import android.os.RemoteException; import android.util.Log; -import android.util.Pair; import android.util.Size; import android.view.Gravity; import android.view.IPinnedStackController; @@ -55,13 +54,13 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.systemui.R; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DismissCircleView; @@ -94,6 +93,8 @@ public class PipTouchHandler { private final WindowManager mWindowManager; private final IActivityManager mActivityManager; private final PipBoundsHandler mPipBoundsHandler; + private final PipUiEventLogger mPipUiEventLogger; + private PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; @@ -132,9 +133,6 @@ public class PipTouchHandler { // The current movement bounds private Rect mMovementBounds = new Rect(); - // The current resized bounds, changed by user resize. - // This is used during expand/un-expand to save/restore the user's resized size. - @VisibleForTesting Rect mResizedBounds = new Rect(); // The reference inset bounds, used to determine the dismiss fraction private Rect mInsetBounds = new Rect(); @@ -198,11 +196,7 @@ public class PipTouchHandler { @Override public void onPipDismiss() { - Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext, - mActivityManager); - if (topPipActivity.first != null) { - MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, topPipActivity); - } + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE); mTouchState.removeDoubleTapTimeoutCallback(); mMotionHelper.dismissPip(); } @@ -223,7 +217,8 @@ public class PipTouchHandler { FloatingContentCoordinator floatingContentCoordinator, DeviceConfigProxy deviceConfig, PipSnapAlgorithm pipSnapAlgorithm, - SysUiState sysUiState) { + SysUiState sysUiState, + PipUiEventLogger pipUiEventLogger) { // Initialize the Pip input consumer mContext = context; mActivityManager = activityManager; @@ -238,7 +233,7 @@ public class PipTouchHandler { mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, deviceConfig, pipTaskOrganizer, this::getMovementBounds, - this::updateMovementBounds, sysUiState); + this::updateMovementBounds, sysUiState, pipUiEventLogger); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(), true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()), @@ -259,6 +254,8 @@ public class PipTouchHandler { pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler); + mPipUiEventLogger = pipUiEventLogger; + mTargetView = new DismissCircleView(context); mTargetViewContainer = new FrameLayout(context); mTargetViewContainer.setBackgroundDrawable( @@ -307,11 +304,8 @@ public class PipTouchHandler { hideDismissTarget(); }); - Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext, - mActivityManager); - if (topPipActivity.first != null) { - MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, topPipActivity); - } + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE); } }); @@ -383,7 +377,6 @@ public class PipTouchHandler { mFloatingContentCoordinator.onContentRemoved(mMotionHelper); } - mResizedBounds.setEmpty(); mPipResizeGestureHandler.onActivityUnpinned(); } @@ -393,9 +386,8 @@ public class PipTouchHandler { mMotionHelper.synchronizePinnedStackBounds(); updateMovementBounds(); if (direction == TRANSITION_DIRECTION_TO_PIP) { - // updates mResizedBounds only if it's an entering PiP animation - // mResized should be otherwise updated in setMenuState. - mResizedBounds.set(mMotionHelper.getBounds()); + // Set the initial bounds as the user resize bounds. + mPipResizeGestureHandler.setUserResizeBounds(mMotionHelper.getBounds()); } if (mShowPipMenuOnAnimationEnd) { @@ -808,9 +800,7 @@ public class PipTouchHandler { // Save the current snap fraction and if we do not drag or move the PiP, then // we store back to this snap fraction. Otherwise, we'll reset the snap // fraction and snap to the closest edge. - // Also save the current resized bounds so when the menu disappears, we can restore it. if (resize) { - mResizedBounds.set(mMotionHelper.getBounds()); Rect expandedBounds = new Rect(mExpandedBounds); mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds, mMovementBounds, mExpandedMovementBounds, callback); @@ -839,7 +829,7 @@ public class PipTouchHandler { } if (mDeferResizeToNormalBoundsUntilRotation == -1) { - Rect restoreBounds = new Rect(mResizedBounds); + Rect restoreBounds = new Rect(getUserResizeBounds()); Rect restoredMovementBounds = new Rect(); mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); @@ -856,8 +846,10 @@ public class PipTouchHandler { // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip // as well, or it can't handle a11y focus and pip menu can't perform any action. onRegistrationChanged(menuState == MENU_STATE_NONE); - if (menuState != MENU_STATE_CLOSE) { - MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL); + if (menuState == MENU_STATE_NONE) { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_HIDE_MENU); + } else if (menuState == MENU_STATE_FULL) { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_MENU); } } @@ -890,6 +882,10 @@ public class PipTouchHandler { return mNormalBounds; } + Rect getUserResizeBounds() { + return mPipResizeGestureHandler.getUserResizeBounds(); + } + /** * Gesture controlling normal movement of the PIP. */ diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index d5a14f7bef2f..affc5ee1fdf0 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -45,7 +45,6 @@ import javax.inject.Singleton @Singleton class PrivacyItemController @Inject constructor( - context: Context, private val appOpsController: AppOpsController, @Main uiExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, @@ -57,16 +56,21 @@ class PrivacyItemController @Inject constructor( @VisibleForTesting internal companion object { - val OPS = intArrayOf(AppOpsManager.OP_CAMERA, - AppOpsManager.OP_RECORD_AUDIO, + val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA, + AppOpsManager.OP_RECORD_AUDIO) + val OPS_LOCATION = intArrayOf( AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION) + val OPS = OPS_MIC_CAMERA + OPS_LOCATION val intentFilter = IntentFilter().apply { addAction(Intent.ACTION_USER_SWITCHED) addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) } const val TAG = "PrivacyItemController" + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED } @VisibleForTesting @@ -74,9 +78,14 @@ class PrivacyItemController @Inject constructor( @Synchronized get() = field.toList() // Returns a shallow copy of the list @Synchronized set - private fun isPermissionsHubEnabled(): Boolean { + private fun isAllIndicatorsEnabled(): Boolean { return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) + ALL_INDICATORS, false) + } + + private fun isMicCameraEnabled(): Boolean { + return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + MIC_CAMERA, false) } private var currentUserIds = emptyList<Int>() @@ -94,23 +103,28 @@ class PrivacyItemController @Inject constructor( uiExecutor.execute(notifyChanges) } - var indicatorsAvailable = isPermissionsHubEnabled() + var allIndicatorsAvailable = isAllIndicatorsEnabled() private set - @VisibleForTesting - internal val devicePropertiesChangedListener = + var micCameraAvailable = isMicCameraEnabled() + private set + + private val devicePropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener { override fun onPropertiesChanged(properties: DeviceConfig.Properties) { if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) && - properties.getKeyset().contains( - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) { - val flag = properties.getBoolean( - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) - if (indicatorsAvailable != flag) { - // This is happening already in the UI executor, so we can iterate in the - indicatorsAvailable = flag - callbacks.forEach { it.get()?.onFlagChanged(flag) } + (properties.keyset.contains(ALL_INDICATORS) || + properties.keyset.contains(MIC_CAMERA))) { + + // Running on the ui executor so can iterate on callbacks + if (properties.keyset.contains(ALL_INDICATORS)) { + allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false) + callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) } } + if (properties.keyset.contains(MIC_CAMERA)) { + micCameraAvailable = properties.getBoolean(MIC_CAMERA, false) + callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) } + } internalUiExecutor.updateListeningState() } } @@ -123,6 +137,10 @@ class PrivacyItemController @Inject constructor( packageName: String, active: Boolean ) { + // Check if we care about this code right now + if (!allIndicatorsAvailable && code in OPS_LOCATION) { + return + } val userId = UserHandle.getUserId(uid) if (userId in currentUserIds) { update(false) @@ -166,13 +184,16 @@ class PrivacyItemController @Inject constructor( } /** - * Updates listening status based on whether there are callbacks and the indicators are enabled + * Updates listening status based on whether there are callbacks and the indicators are enabled. + * + * Always listen to all OPS so we don't have to figure out what we should be listening to. We + * still have to filter anyway. Updates are filtered in the callback. * * This is only called from private (add/remove)Callback and from the config listener, all in * main thread. */ private fun setListeningState() { - val listen = !callbacks.isEmpty() and indicatorsAvailable + val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable) if (listening == listen) return listening = listen if (listening) { @@ -233,14 +254,19 @@ class PrivacyItemController @Inject constructor( AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } + if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid) return PrivacyItem(type, app) } interface Callback { fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) + + @JvmDefault + fun onFlagAllChanged(flag: Boolean) {} + @JvmDefault - fun onFlagChanged(flag: Boolean) {} + fun onFlagMicCameraChanged(flag: Boolean) {} } internal inner class Receiver : BroadcastReceiver() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 2dc82dd853d4..2e258d56ece0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -151,7 +151,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; private RingerModeTracker mRingerModeTracker; - private boolean mPermissionsHubEnabled; + private boolean mAllIndicatorsEnabled; + private boolean mMicCameraIndicatorsEnabled; private PrivacyItemController mPrivacyItemController; private final UiEventLogger mUiEventLogger; @@ -178,13 +179,26 @@ public class QuickStatusBarHeader extends RelativeLayout implements } @Override - public void onFlagChanged(boolean flag) { - if (mPermissionsHubEnabled != flag) { - StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); - iconContainer.setIgnoredSlots(getIgnoredIconSlots()); - setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); + public void onFlagAllChanged(boolean flag) { + if (mAllIndicatorsEnabled != flag) { + mAllIndicatorsEnabled = flag; + update(); } } + + @Override + public void onFlagMicCameraChanged(boolean flag) { + if (mMicCameraIndicatorsEnabled != flag) { + mMicCameraIndicatorsEnabled = flag; + update(); + } + } + + private void update() { + StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); + iconContainer.setIgnoredSlots(getIgnoredIconSlots()); + setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); + } }; @Inject @@ -267,7 +281,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mRingerModeTextView.setSelected(true); mNextAlarmTextView.setSelected(true); - mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); + mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); } public QuickQSPanel getHeaderQsPanel() { @@ -276,13 +291,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements private List<String> getIgnoredIconSlots() { ArrayList<String> ignored = new ArrayList<>(); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_camera)); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_microphone)); - if (mPermissionsHubEnabled) { + if (getChipEnabled()) { + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_camera)); ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_location)); + com.android.internal.R.string.status_bar_microphone)); + if (mAllIndicatorsEnabled) { + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_location)); + } } return ignored; @@ -300,7 +317,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements } private void setChipVisibility(boolean chipVisible) { - if (chipVisible && mPermissionsHubEnabled) { + if (chipVisible && getChipEnabled()) { mPrivacyChip.setVisibility(View.VISIBLE); // Makes sure that the chip is logged as viewed at most once each time QS is opened // mListening makes sure that the callback didn't return after the user closed QS @@ -607,7 +624,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mAlarmController.addCallback(this); mLifecycle.setCurrentState(Lifecycle.State.RESUMED); // Get the most up to date info - mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); + mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); mPrivacyItemController.addCallback(mPICCallback); } else { mZenController.removeCallback(this); @@ -747,4 +765,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateHeaderTextContainerAlphaAnimator(); } } + + private boolean getChipEnabled() { + return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 8c485a6a950f..d2aaaede3f4b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -451,15 +451,17 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy if (listening) { if (mListeners.add(listener) && mListeners.size() == 1) { if (DEBUG) Log.d(TAG, "handleSetListening true"); - mLifecycle.setCurrentState(RESUMED); handleSetListening(listening); - refreshState(); // Ensure we get at least one refresh after listening. + mUiHandler.post(() -> { + mLifecycle.setCurrentState(RESUMED); + refreshState(); // Ensure we get at least one refresh after listening. + }); } } else { if (mListeners.remove(listener) && mListeners.size() == 0) { if (DEBUG) Log.d(TAG, "handleSetListening false"); - mLifecycle.setCurrentState(STARTED); handleSetListening(listening); + mUiHandler.post(() -> mLifecycle.setCurrentState(STARTED)); } } updateIsFullQs(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 99cb4760a8d9..a87311a69ab5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -2620,6 +2620,7 @@ public class NotificationPanelViewController extends PanelViewController { super.onClosingFinished(); resetHorizontalPanelPosition(); setClosingWithAlphaFadeout(false); + mMediaHierarchyManager.closeGuts(); } private void setClosingWithAlphaFadeout(boolean closing) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index b2cfceae2cf6..8cb54eef01b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -662,16 +662,18 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotCamera, showCamera); mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); - mIconController.setIconVisibility(mSlotLocation, showLocation); + if (mPrivacyItemController.getAllIndicatorsAvailable()) { + mIconController.setIconVisibility(mSlotLocation, showLocation); + } } @Override public void onLocationActiveChanged(boolean active) { - if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation(); + if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController(); } // Updates the status view based on the current state of location requests. - private void updateLocation() { + private void updateLocationFromController() { if (mLocationController.isLocationActive()) { mIconController.setIconVisibility(mSlotLocation, true); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index b89cb210dea1..8ff7a41cb22f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -86,7 +86,7 @@ public interface StatusBarIconController { static ArraySet<String> getIconHideList(Context context, String hideListStr) { ArraySet<String> ret = new ArraySet<>(); String[] hideList = hideListStr == null - ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList) + ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude) : hideListStr.split(","); for (String slot : hideList) { if (!TextUtils.isEmpty(slot)) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java index fbc8e9d8de79..ac567e0ae67d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java @@ -25,6 +25,7 @@ import android.content.Context; import android.os.RemoteException; import android.provider.Settings; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.view.Display; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IWindowMagnificationConnection; @@ -47,6 +48,7 @@ import org.mockito.MockitoAnnotations; */ @SmallTest @RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class IWindowMagnificationConnectionTest extends SysuiTestCase { private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; @@ -57,7 +59,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { @Mock private IWindowMagnificationConnectionCallback mConnectionCallback; @Mock - private WindowMagnificationController mWindowMagnificationController; + private WindowMagnificationAnimationController mWindowMagnificationAnimationController; @Mock private ModeSwitchesController mModeSwitchesController; private IWindowMagnificationConnection mIWindowMagnificationConnection; @@ -74,7 +76,8 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { any(IWindowMagnificationConnection.class)); mWindowMagnification = new WindowMagnification(getContext(), getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController); - mWindowMagnification.mWindowMagnificationController = mWindowMagnificationController; + mWindowMagnification.mWindowMagnificationAnimationController = + mWindowMagnificationAnimationController; mWindowMagnification.requestWindowMagnificationConnection(true); assertNotNull(mIWindowMagnificationConnection); mIWindowMagnificationConnection.setConnectionCallback(mConnectionCallback); @@ -86,7 +89,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { Float.NaN); waitForIdleSync(); - verify(mWindowMagnificationController).enableWindowMagnification(3.0f, Float.NaN, + verify(mWindowMagnificationAnimationController).enableWindowMagnification(3.0f, Float.NaN, Float.NaN); } @@ -99,7 +102,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY); waitForIdleSync(); - verify(mWindowMagnificationController).deleteWindowMagnification(); + verify(mWindowMagnificationAnimationController).deleteWindowMagnification(); } @Test @@ -107,7 +110,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { mIWindowMagnificationConnection.setScale(TEST_DISPLAY, 3.0f); waitForIdleSync(); - verify(mWindowMagnificationController).setScale(3.0f); + verify(mWindowMagnificationAnimationController).setScale(3.0f); } @Test @@ -115,7 +118,7 @@ public class IWindowMagnificationConnectionTest extends SysuiTestCase { mIWindowMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f); waitForIdleSync(); - verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f); + verify(mWindowMagnificationAnimationController).moveWindowMagnifier(100f, 200f); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java new file mode 100644 index 000000000000..add0843c2994 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -0,0 +1,362 @@ +/* + * 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.systemui.accessibility; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.animation.ValueAnimator; +import android.app.Instrumentation; +import android.content.Context; +import android.os.Handler; +import android.os.SystemClock; +import android.testing.AndroidTestingRunner; +import android.view.SurfaceControl; +import android.view.animation.AccelerateInterpolator; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; + +import com.android.internal.graphics.SfVsyncFrameCallbackProvider; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.concurrent.atomic.AtomicReference; + + +@MediumTest +@RunWith(AndroidTestingRunner.class) +public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { + + private static final float DEFAULT_SCALE = 3.0f; + private static final float DEFAULT_CENTER_X = 400.0f; + private static final float DEFAULT_CENTER_Y = 500.0f; + private static final long ANIMATION_DURATION_MS = 100; + + private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0); + private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0); + private AtomicReference<Float> mCurrentCenterY = new AtomicReference<>((float) 0); + private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class); + private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class); + private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class); + + @Mock + Handler mHandler; + @Mock + SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; + @Mock + WindowMagnifierCallback mWindowMagnifierCallback; + + private SpyWindowMagnificationController mController; + private WindowMagnificationController mSpyController; + private WindowMagnificationAnimationController mWindowMagnificationAnimationController; + private Instrumentation mInstrumentation; + private long mWaitingAnimationPeriod; + private long mWaitIntermediateAnimationPeriod; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mWaitingAnimationPeriod = ANIMATION_DURATION_MS + 50; + mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2; + mController = new SpyWindowMagnificationController(mContext, mHandler, + mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(), + mWindowMagnifierCallback); + mSpyController = mController.getSpyController(); + mWindowMagnificationAnimationController = new WindowMagnificationAnimationController( + mContext, mController, newValueAnimator()); + } + + @Test + public void enableWindowMagnification_disabled_expectedStartAndEndValues() { + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + verify(mSpyController, atLeast(2)).enableWindowMagnification( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verifyStartValue(mScaleCaptor, 1.0f); + verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X); + verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y); + verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); + } + + @Test + public void enableWindowMagnification_enabling_expectedStartAndEndValues() { + enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod); + final float targetScale = DEFAULT_SCALE + 1.0f; + final float targetCenterX = DEFAULT_CENTER_X + 100; + final float targetCenterY = DEFAULT_CENTER_Y + 100; + + mInstrumentation.runOnMainSync(() -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + }); + + SystemClock.sleep(mWaitingAnimationPeriod); + + verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verifyStartValue(mScaleCaptor, mCurrentScale.get()); + verifyStartValue(mCenterXCaptor, mCurrentCenterX.get()); + verifyStartValue(mCenterYCaptor, mCurrentCenterY.get()); + verifyFinalSpec(targetScale, targetCenterX, targetCenterY); + } + + @Test + public void enableWindowMagnification_disabling_expectedStartAndEndValues() { + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod); + final float targetScale = DEFAULT_SCALE + 1.0f; + final float targetCenterX = DEFAULT_CENTER_X + 100; + final float targetCenterY = DEFAULT_CENTER_Y + 100; + + mInstrumentation.runOnMainSync( + () -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(targetScale, + targetCenterX, targetCenterY); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + }); + SystemClock.sleep(mWaitingAnimationPeriod); + + verify(mSpyController, atLeast(2)).enableWindowMagnification( + mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture()); + //Animating in reverse, so we only check if the start values are greater than current. + assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get()); + assertEquals(targetScale, mScaleCaptor.getValue(), 0f); + assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get()); + assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f); + assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get()); + assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f); + verifyFinalSpec(targetScale, targetCenterX, targetCenterY); + } + + @Test + public void enableWindowMagnificationWithSameScale_doNothing() { + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(), + anyFloat()); + } + + @Test + public void setScale_enabled_expectedScale() { + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1)); + + verify(mSpyController).setScale(DEFAULT_SCALE + 1); + verifyFinalSpec(DEFAULT_SCALE + 1, DEFAULT_CENTER_X, DEFAULT_CENTER_Y); + } + + @Test + public void deleteWindowMagnification_enabled_expectedStartAndEndValues() { + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController).deleteWindowMagnification(); + verifyStartValue(mScaleCaptor, DEFAULT_SCALE); + verifyStartValue(mCenterXCaptor, Float.NaN); + verifyStartValue(mCenterYCaptor, Float.NaN); + verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); + } + + @Test + public void deleteWindowMagnification_disabled_doNothing() { + deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + Mockito.verifyNoMoreInteractions(mSpyController); + } + + @Test + public void deleteWindowMagnification_enabling_checkStartAndEndValues() { + enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod); + + //It just reverse the animation, so we don't need to wait the whole duration. + mInstrumentation.runOnMainSync( + () -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.deleteWindowMagnification(); + mCurrentScale.set(mController.getScale()); + mCurrentCenterX.set(mController.getCenterX()); + mCurrentCenterY.set(mController.getCenterY()); + }); + SystemClock.sleep(mWaitingAnimationPeriod); + + verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController).deleteWindowMagnification(); + + //The animation is in verse, so we only check the start values should no be greater than + // the current one. + assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get()); + assertEquals(1.0f, mScaleCaptor.getValue(), 0f); + verifyStartValue(mCenterXCaptor, Float.NaN); + verifyStartValue(mCenterYCaptor, Float.NaN); + verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); + } + + @Test + public void deleteWindowMagnification_disabling_checkStartAndValues() { + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod); + + deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(), + mCenterXCaptor.capture(), mCenterYCaptor.capture()); + verify(mSpyController).deleteWindowMagnification(); + assertEquals(1.0f, mScaleCaptor.getValue(), 0f); + verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN); + } + + @Test + public void moveWindowMagnifier_enabled() { + enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod); + + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f)); + + verify(mSpyController).moveWindowMagnifier(100f, 200f); + verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 100f, DEFAULT_CENTER_Y + 100f); + } + + @Test + public void onConfigurationChanged_passThrough() { + mWindowMagnificationAnimationController.onConfigurationChanged(100); + + verify(mSpyController).onConfigurationChanged(100); + } + private void verifyFinalSpec(float expectedScale, float expectedCenterX, + float expectedCenterY) { + assertEquals(expectedScale, mController.getScale(), 0f); + assertEquals(expectedCenterX, mController.getCenterX(), 0f); + assertEquals(expectedCenterY, mController.getCenterY(), 0f); + } + + private void enableWindowMagnificationAndWaitAnimating(long duration) { + mInstrumentation.runOnMainSync( + () -> { + Mockito.reset(mSpyController); + mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE, + DEFAULT_CENTER_X, DEFAULT_CENTER_Y); + }); + SystemClock.sleep(duration); + } + + private void deleteWindowMagnificationAndWaitAnimating(long duration) { + mInstrumentation.runOnMainSync( + () -> { + resetMockObjects(); + mWindowMagnificationAnimationController.deleteWindowMagnification(); + }); + SystemClock.sleep(duration); + } + + private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) { + assertEquals(startValue, captor.getAllValues().get(0), 0f); + } + + private void resetMockObjects() { + Mockito.reset(mSpyController); + } + + /** + * It observes the methods in {@link WindowMagnificationController} since we couldn't spy it + * directly. + */ + private static class SpyWindowMagnificationController extends WindowMagnificationController { + private WindowMagnificationController mSpyController; + + SpyWindowMagnificationController(Context context, Handler handler, + SfVsyncFrameCallbackProvider sfVsyncFrameProvider, + MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction, + WindowMagnifierCallback callback) { + super(context, handler, sfVsyncFrameProvider, mirrorWindowControl, transaction, + callback); + mSpyController = Mockito.mock(WindowMagnificationController.class); + } + + WindowMagnificationController getSpyController() { + return mSpyController; + } + + @Override + void enableWindowMagnification(float scale, float centerX, float centerY) { + super.enableWindowMagnification(scale, centerX, centerY); + mSpyController.enableWindowMagnification(scale, centerX, centerY); + } + + @Override + void deleteWindowMagnification() { + super.deleteWindowMagnification(); + mSpyController.deleteWindowMagnification(); + } + + @Override + void moveWindowMagnifier(float offsetX, float offsetY) { + super.moveWindowMagnifier(offsetX, offsetX); + mSpyController.moveWindowMagnifier(offsetX, offsetY); + } + + @Override + void setScale(float scale) { + super.setScale(scale); + mSpyController.setScale(scale); + } + + @Override + void onConfigurationChanged(int configDiff) { + super.onConfigurationChanged(configDiff); + mSpyController.onConfigurationChanged(configDiff); + } + + } + + private static ValueAnimator newValueAnimator() { + final ValueAnimator valueAnimator = new ValueAnimator(); + valueAnimator.setDuration(ANIMATION_DURATION_MS); + valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f)); + valueAnimator.setFloatValues(0.0f, 1.0f); + return valueAnimator; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 2007fbb8fc6c..1515cec4c9e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.accessibility; import static android.view.Choreographer.FrameCallback; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeastOnce; @@ -83,9 +84,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @After public void tearDown() { - mInstrumentation.runOnMainSync(() -> { - mWindowMagnificationController.deleteWindowMagnification(); - }); + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.deleteWindowMagnification()); } @Test @@ -121,4 +121,18 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any()); } + + @Test + public void setScale_expectedValue() { + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, + Float.NaN); + }); + + mInstrumentation.runOnMainSync(() -> { + mWindowMagnificationController.setScale(3.0f); + }); + + assertEquals(3.0f, mWindowMagnificationController.getScale(), 0); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java index 41360130ac65..936558bca2d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java @@ -26,6 +26,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.RemoteException; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.view.Display; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IWindowMagnificationConnection; @@ -45,6 +46,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper public class WindowMagnificationTest extends SysuiTestCase { @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index ac8c6710e041..4c5495474018 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -88,7 +88,7 @@ import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper() +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class GlobalActionsDialogTest extends SysuiTestCase { private GlobalActionsDialog mGlobalActionsDialog; diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index c63781cb110a..8a30b00e609d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.media +import android.content.Intent import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.GradientDrawable @@ -23,6 +24,7 @@ import android.graphics.drawable.RippleDrawable import android.media.MediaMetadata import android.media.session.MediaSession import android.media.session.PlaybackState +import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View @@ -35,24 +37,31 @@ import android.widget.TextView import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.LiveData import androidx.test.filters.SmallTest -import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import dagger.Lazy import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock +import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever private const val KEY = "TEST_KEY" private const val APP = "APP" @@ -81,6 +90,8 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var seekBarViewModel: SeekBarViewModel @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress> @Mock private lateinit var mediaViewController: MediaViewController + @Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil + @Mock private lateinit var mediaDataManager: MediaDataManager @Mock private lateinit var expandedSet: ConstraintSet @Mock private lateinit var collapsedSet: ConstraintSet private lateinit var appIcon: ImageView @@ -100,6 +111,9 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var action2: ImageButton private lateinit var action3: ImageButton private lateinit var action4: ImageButton + private lateinit var settings: View + private lateinit var cancel: View + private lateinit var dismiss: View private lateinit var session: MediaSession private val device = MediaDeviceData(true, null, DEVICE_NAME) @@ -114,7 +128,7 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet) player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController, - seekBarViewModel) + seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil) whenever(seekBarViewModel.progress).thenReturn(seekBarData) // Mock out a view holder for the player to attach to. @@ -156,6 +170,12 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(holder.action3).thenReturn(action3) action4 = ImageButton(context) whenever(holder.action4).thenReturn(action4) + settings = View(context) + whenever(holder.settings).thenReturn(settings) + cancel = View(context) + whenever(holder.cancel).thenReturn(cancel) + dismiss = View(context) + whenever(holder.dismiss).thenReturn(dismiss) // Create media session val metadataBuilder = MediaMetadata.Builder().apply { @@ -254,4 +274,79 @@ public class MediaControlPanelTest : SysuiTestCase() { assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME) assertThat(seamless.isEnabled()).isFalse() } + + @Test + fun longClick_gutsClosed() { + player.attach(holder) + whenever(mediaViewController.isGutsVisible).thenReturn(false) + + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(holder.player).setOnLongClickListener(captor.capture()) + + captor.value.onLongClick(holder.player) + verify(mediaViewController).openGuts() + } + + @Test + fun longClick_gutsOpen() { + player.attach(holder) + whenever(mediaViewController.isGutsVisible).thenReturn(true) + + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(holder.player).setOnLongClickListener(captor.capture()) + + captor.value.onLongClick(holder.player) + verify(mediaViewController, never()).openGuts() + } + + @Test + fun cancelButtonClick_animation() { + player.attach(holder) + + cancel.callOnClick() + + verify(mediaViewController).closeGuts(false) + } + + @Test + fun settingsButtonClick() { + player.attach(holder) + + settings.callOnClick() + + val captor = ArgumentCaptor.forClass(Intent::class.java) + verify(activityStarter).startActivity(captor.capture(), eq(true)) + + assertThat(captor.value.action).isEqualTo(ACTION_MEDIA_CONTROLS_SETTINGS) + } + + @Test + fun dismissButtonClick() { + player.attach(holder) + val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null, + notificationKey = KEY) + player.bind(state) + + dismiss.callOnClick() + val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java) + verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean()) + + captor.value.onDismiss() + verify(mediaDataManager).dismissMediaData(eq(KEY), anyLong()) + } + + @Test + fun dismissButtonClick_nullNotificationKey() { + player.attach(holder) + val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null) + player.bind(state) + + verify(keyguardDismissUtil, never()) + .executeWhenUnlocked( + any(ActivityStarter.OnDismissAction::class.java), + ArgumentMatchers.anyBoolean() + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 59c2d0e86c56..3789e6ef1f65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -217,6 +217,20 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(data.actions).hasSize(1) } + @Test + fun testDismissMedia_listenerCalled() { + val listener = mock(MediaDataManager.Listener::class.java) + mediaDataManager.addListener(listener) + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) + mediaDataManager.dismissMediaData(KEY, 0L) + + foregroundExecutor.advanceClockToLast() + foregroundExecutor.runAllReady() + + verify(listener).onMediaDataRemoved(eq(KEY)) + } + /** * Simple implementation of [MediaDataManager.Listener] for the test. * diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt index 91c5ff8ee627..d86dfa5fa5f7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt @@ -142,4 +142,11 @@ class MediaHierarchyManagerTest : SysuiTestCase() { verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(), any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong()) } + + @Test + fun testCloseGutsRelayToCarousel() { + mediaHiearchyManager.closeGuts() + + verify(mediaCarouselController).closeGuts() + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java index 96bb521a5d5b..9f67722041aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java @@ -37,6 +37,7 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.FloatingContentCoordinator; @@ -85,6 +86,9 @@ public class PipTouchHandlerTest extends SysuiTestCase { @Mock private SysUiState mSysUiState; + @Mock + private PipUiEventLogger mPipUiEventLogger; + private PipSnapAlgorithm mPipSnapAlgorithm; private PipMotionHelper mMotionHelper; private PipResizeGestureHandler mPipResizeGestureHandler; @@ -104,7 +108,7 @@ public class PipTouchHandlerTest extends SysuiTestCase { mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, - mPipSnapAlgorithm, mSysUiState); + mPipSnapAlgorithm, mSysUiState, mPipUiEventLogger); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler()); mPipTouchHandler.setPipMotionHelper(mMotionHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt new file mode 100644 index 000000000000..4ba29e6e02a6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt @@ -0,0 +1,219 @@ +/* + * 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.systemui.privacy + +import android.os.UserManager +import android.provider.DeviceConfig +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags +import com.android.systemui.SysuiTestCase +import com.android.systemui.appops.AppOpsController +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dump.DumpManager +import com.android.systemui.util.DeviceConfigProxy +import com.android.systemui.util.DeviceConfigProxyFake +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class PrivacyItemControllerFlagsTest : SysuiTestCase() { + companion object { + fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() + fun <T> eq(value: T): T = Mockito.eq(value) ?: value + fun <T> any(): T = Mockito.any<T>() + + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED + } + + @Mock + private lateinit var appOpsController: AppOpsController + @Mock + private lateinit var callback: PrivacyItemController.Callback + @Mock + private lateinit var userManager: UserManager + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var dumpManager: DumpManager + + private lateinit var privacyItemController: PrivacyItemController + private lateinit var executor: FakeExecutor + private lateinit var deviceConfigProxy: DeviceConfigProxy + + fun PrivacyItemController(): PrivacyItemController { + return PrivacyItemController( + appOpsController, + executor, + executor, + broadcastDispatcher, + deviceConfigProxy, + userManager, + dumpManager + ) + } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + executor = FakeExecutor(FakeSystemClock()) + deviceConfigProxy = DeviceConfigProxyFake() + + privacyItemController = PrivacyItemController() + privacyItemController.addCallback(callback) + + executor.runAllReady() + } + + @Test + fun testNotListeningByDefault() { + assertFalse(privacyItemController.allIndicatorsAvailable) + assertFalse(privacyItemController.micCameraAvailable) + + verify(appOpsController, never()).addCallback(any(), any()) + } + + @Test + fun testMicCameraChanged() { + changeMicCamera(true) + executor.runAllReady() + + verify(callback).onFlagMicCameraChanged(true) + verify(callback, never()).onFlagAllChanged(anyBoolean()) + + assertTrue(privacyItemController.micCameraAvailable) + assertFalse(privacyItemController.allIndicatorsAvailable) + } + + @Test + fun testAllChanged() { + changeAll(true) + executor.runAllReady() + + verify(callback).onFlagAllChanged(true) + verify(callback, never()).onFlagMicCameraChanged(anyBoolean()) + + assertTrue(privacyItemController.allIndicatorsAvailable) + assertFalse(privacyItemController.micCameraAvailable) + } + + @Test + fun testBothChanged() { + changeAll(true) + changeMicCamera(true) + executor.runAllReady() + + verify(callback, atLeastOnce()).onFlagAllChanged(true) + verify(callback, atLeastOnce()).onFlagMicCameraChanged(true) + + assertTrue(privacyItemController.allIndicatorsAvailable) + assertTrue(privacyItemController.micCameraAvailable) + } + + @Test + fun testAll_listeningToAll() { + changeAll(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testMicCamera_listening() { + changeMicCamera(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testAll_listening() { + changeAll(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testAllFalse_notListening() { + changeAll(true) + executor.runAllReady() + changeAll(false) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + @Test + fun testSomeListening_stillListening() { + changeAll(true) + changeMicCamera(true) + executor.runAllReady() + changeAll(false) + executor.runAllReady() + + verify(appOpsController, never()).removeCallback(any(), any()) + } + + @Test + fun testAllDeleted_stopListening() { + changeAll(true) + executor.runAllReady() + changeAll(null) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + @Test + fun testMicDeleted_stopListening() { + changeMicCamera(true) + executor.runAllReady() + changeMicCamera(null) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) + private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) + + private fun changeProperty(name: String, value: Boolean?) { + deviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + name, + value?.toString(), + false + ) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index dddc35072315..fb42baaa0cb5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.privacy import android.app.ActivityManager import android.app.AppOpsManager -import android.content.Context import android.content.Intent import android.content.pm.UserInfo import android.os.UserHandle @@ -69,10 +68,11 @@ class PrivacyItemControllerTest : SysuiTestCase() { companion object { val CURRENT_USER_ID = ActivityManager.getCurrentUser() val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE - const val SYSTEM_UID = 1000 const val TEST_PACKAGE_NAME = "test" - const val DEVICE_SERVICES_STRING = "Device services" - const val TAG = "PrivacyItemControllerTest" + + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() fun <T> eq(value: T): T = Mockito.eq(value) ?: value fun <T> any(): T = Mockito.any<T>() @@ -97,9 +97,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { private lateinit var executor: FakeExecutor private lateinit var deviceConfigProxy: DeviceConfigProxy - fun PrivacyItemController(context: Context): PrivacyItemController { + fun PrivacyItemController(): PrivacyItemController { return PrivacyItemController( - context, appOpsController, executor, executor, @@ -116,11 +115,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { executor = FakeExecutor(FakeSystemClock()) deviceConfigProxy = DeviceConfigProxyFake() - appOpsController = mDependency.injectMockDependency(AppOpsController::class.java) - - deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, - "true", false) + // Listen to everything by default + changeAll(true) doReturn(listOf(object : UserInfo() { init { @@ -128,7 +124,7 @@ class PrivacyItemControllerTest : SysuiTestCase() { } })).`when`(userManager).getProfiles(anyInt()) - privacyItemController = PrivacyItemController(mContext) + privacyItemController = PrivacyItemController() } @Test @@ -276,15 +272,59 @@ class PrivacyItemControllerTest : SysuiTestCase() { @Test fun testNotListeningWhenIndicatorsDisabled() { - deviceConfigProxy.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, - "false", - false - ) + changeAll(false) privacyItemController.addCallback(callback) executor.runAllReady() verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS), any()) } + + @Test + fun testNotSendingLocationWhenOnlyMicCamera() { + changeAll(false) + changeMicCamera(true) + executor.runAllReady() + + doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0), + AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + + privacyItemController.addCallback(callback) + executor.runAllReady() + + verify(callback).onPrivacyItemsChanged(capture(argCaptor)) + + assertEquals(1, argCaptor.value.size) + assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType) + } + + @Test + fun testNotUpdated_LocationChangeWhenOnlyMicCamera() { + doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + + privacyItemController.addCallback(callback) + changeAll(false) + changeMicCamera(true) + executor.runAllReady() + reset(callback) // Clean callback + + verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) + argCaptorCallback.value.onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true) + + verify(callback, never()).onPrivacyItemsChanged(any()) + } + + private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) + private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) + + private fun changeProperty(name: String, value: Boolean?) { + deviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + name, + value?.toString(), + false + ) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index 103e5586f395..cccb65d11228 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -74,7 +74,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @SmallTest public class QSTileImplTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index 2d276bb876f3..6b54791dd143 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -47,7 +47,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper() +@TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest public class ScreenRecordTileTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index ae87eefd243c..781f875fd868 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -41,7 +41,7 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; @RunWith(AndroidTestingRunner.class) -@RunWithLooper() +@RunWithLooper(setAsMainLooper = true) @SmallTest public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java index a16fb5e8dc17..86dacc13feab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java @@ -37,7 +37,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) @SmallTest public class CallbackControllerTest extends SysuiTestCase { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java index 0e1c560c918b..b2f57d0726cc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java @@ -39,7 +39,7 @@ import com.android.systemui.SysuiBaseFragmentTest; import org.junit.Test; import org.junit.runner.RunWith; -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) @SmallTest public class LifecycleFragmentTest extends SysuiBaseFragmentTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java index 486939d1f08e..4f509eaaadde 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java @@ -49,7 +49,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner.class) @SmallTest public class SysuiLifecycleTest extends SysuiTestCase { diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index 9b9dcde910e7..5f8d2997197f 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -73,6 +73,9 @@ <!-- Use the old dnsmasq DHCP server for tethering instead of the framework implementation. --> <bool translatable="false" name="config_tether_enable_legacy_dhcp_server">false</bool> + <!-- Use legacy wifi p2p dedicated address instead of randomize address. --> + <bool translatable="false" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip">false</bool> + <!-- Dhcp range (min, max) to use for tethering purposes --> <string-array translatable="false" name="config_tether_dhcp_range"> </string-array> diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml index 6a33d55cb0de..0ee7a992ee20 100644 --- a/packages/Tethering/res/values/overlayable.xml +++ b/packages/Tethering/res/values/overlayable.xml @@ -30,6 +30,7 @@ --> <item type="bool" name="config_tether_enable_bpf_offload"/> <item type="bool" name="config_tether_enable_legacy_dhcp_server"/> + <item type="bool" name="config_tether_enable_legacy_wifi_p2p_dedicated_ip"/> <item type="integer" name="config_tether_offload_poll_interval"/> <item type="array" name="config_tether_upstream_types"/> <item type="bool" name="config_tether_upstream_automatic"/> diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java index aa58a4b6a320..fd9e36080c80 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java @@ -15,6 +15,8 @@ */ package com.android.networkstack.tethering; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; + import static java.util.Arrays.asList; import android.content.Context; @@ -58,6 +60,7 @@ public class PrivateAddressCoordinator { private static final int BYTE_MASK = 0xff; // reserved for bluetooth tethering. private static final int BLUETOOTH_RESERVED = 44; + private static final int WIFI_P2P_RESERVED = 49; private static final byte DEFAULT_ID = (byte) 42; // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream @@ -71,15 +74,18 @@ public class PrivateAddressCoordinator { // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers. private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16"; + private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24"; private final IpPrefix mTetheringPrefix; private final ConnectivityManager mConnectivityMgr; + private final TetheringConfiguration mConfig; - public PrivateAddressCoordinator(Context context) { + public PrivateAddressCoordinator(Context context, TetheringConfiguration config) { mDownstreams = new ArraySet<>(); mUpstreamPrefixMap = new ArrayMap<>(); mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX); mConnectivityMgr = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE); + mConfig = config; } /** @@ -141,12 +147,21 @@ public class PrivateAddressCoordinator { mUpstreamPrefixMap.removeAll(toBeRemoved); } + private boolean isReservedSubnet(final int subnet) { + return subnet == BLUETOOTH_RESERVED || subnet == WIFI_P2P_RESERVED; + } + /** * Pick a random available address and mark its prefix as in use for the provided IpServer, * returns null if there is no available address. */ @Nullable public LinkAddress requestDownstreamAddress(final IpServer ipServer) { + if (mConfig.shouldEnableWifiP2pDedicatedIp() + && ipServer.interfaceType() == TETHERING_WIFI_P2P) { + return new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS); + } + // Address would be 192.168.[subAddress]/24. final byte[] bytes = mTetheringPrefix.getRawAddress(); final int subAddress = getRandomSubAddr(); @@ -154,7 +169,7 @@ public class PrivateAddressCoordinator { bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff); for (int i = 0; i < MAX_UBYTE; i++) { final int newSubNet = (subNet + i) & BYTE_MASK; - if (newSubNet == BLUETOOTH_RESERVED) continue; + if (isReservedSubnet(newSubNet)) continue; bytes[2] = (byte) newSubNet; final InetAddress addr; diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java index cfc657587332..7dd5290ee83b 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -320,10 +320,13 @@ public class Tethering { mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); mNetdCallback = new NetdCallback(); - mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext); // Load tethering configuration. updateConfiguration(); + // It is OK for the configuration to be passed to the PrivateAddressCoordinator at + // construction time because the only part of the configuration it uses is + // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that. + mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig); // Must be initialized after tethering configuration is loaded because BpfCoordinator // constructor needs to use the configuration. diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index e1771a561370..5783805861a3 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -84,6 +84,9 @@ public class TetheringConfiguration { public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = "tether_enable_legacy_dhcp_server"; + public static final String USE_LEGACY_WIFI_P2P_DEDICATED_IP = + "use_legacy_wifi_p2p_dedicated_ip"; + /** * Default value that used to periodic polls tether offload stats from tethering offload HAL * to make the data warnings work. @@ -113,6 +116,7 @@ public class TetheringConfiguration { private final int mOffloadPollInterval; // TODO: Add to TetheringConfigurationParcel if required. private final boolean mEnableBpfOffload; + private final boolean mEnableWifiP2pDedicatedIp; public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); @@ -156,6 +160,10 @@ public class TetheringConfiguration { R.integer.config_tether_offload_poll_interval, DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); + mEnableWifiP2pDedicatedIp = getResourceBoolean(res, + R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip, + false /* defaultValue */); + configLog.log(toString()); } @@ -199,6 +207,11 @@ public class TetheringConfiguration { return !TextUtils.isEmpty(provisioningAppNoUi); } + /** Check whether dedicated wifi p2p address is enabled. */ + public boolean shouldEnableWifiP2pDedicatedIp() { + return mEnableWifiP2pDedicatedIp; + } + /** Does the dumping.*/ public void dump(PrintWriter pw) { pw.print("activeDataSubId: "); @@ -233,6 +246,9 @@ public class TetheringConfiguration { pw.print("enableLegacyDhcpServer: "); pw.println(enableLegacyDhcpServer); + + pw.print("enableWifiP2pDedicatedIp: "); + pw.println(mEnableWifiP2pDedicatedIp); } /** Returns the string representation of this object.*/ diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java index 2c0df6fc6327..8e93c2e447b3 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java @@ -15,6 +15,11 @@ */ package com.android.networkstack.tethering; +import static android.net.TetheringManager.TETHERING_ETHERNET; +import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.mockito.Mockito.never; @@ -54,22 +59,34 @@ public final class PrivateAddressCoordinatorTest { @Mock private IpServer mHotspotIpServer; @Mock private IpServer mUsbIpServer; @Mock private IpServer mEthernetIpServer; + @Mock private IpServer mWifiP2pIpServer; @Mock private Context mContext; @Mock private ConnectivityManager mConnectivityMgr; + @Mock private TetheringConfiguration mConfig; private PrivateAddressCoordinator mPrivateAddressCoordinator; private final IpPrefix mBluetoothPrefix = new IpPrefix("192.168.44.0/24"); + private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24"); private final Network mWifiNetwork = new Network(1); private final Network mMobileNetwork = new Network(2); private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork}; + private void setUpIpServers() throws Exception { + when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB); + when(mEthernetIpServer.interfaceType()).thenReturn(TETHERING_ETHERNET); + when(mHotspotIpServer.interfaceType()).thenReturn(TETHERING_WIFI); + when(mWifiP2pIpServer.interfaceType()).thenReturn(TETHERING_WIFI_P2P); + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr); when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks); - mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext)); + when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false); + setUpIpServers(); + mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig)); } @Test @@ -256,4 +273,38 @@ public final class PrivateAddressCoordinatorTest { final IpPrefix ethPrefix = PrefixUtils.asIpPrefix(ethAddr); assertEquals(predefinedPrefix, ethPrefix); } + + private int getSubAddress(final byte... ipv4Address) { + assertEquals(4, ipv4Address.length); + + int subnet = Byte.toUnsignedInt(ipv4Address[2]); + return (subnet << 8) + ipv4Address[3]; + } + + private void assertReseveredWifiP2pPrefix() throws Exception { + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mHotspotIpServer); + final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(address); + final IpPrefix legacyWifiP2pPrefix = PrefixUtils.asIpPrefix(mLegacyWifiP2pAddress); + assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix); + mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); + } + + @Test + public void testEnableLegacyWifiP2PAddress() throws Exception { + when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn( + getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress())); + // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix + // is resevered. + assertReseveredWifiP2pPrefix(); + + when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(true); + assertReseveredWifiP2pPrefix(); + + // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address. + LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + mWifiP2pIpServer); + assertEquals(mLegacyWifiP2pAddress, address); + mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer); + } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index a9ac4e2851f3..dc0940cc0222 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -128,6 +128,8 @@ public class TetheringConfigurationTest { .thenReturn(new String[0]); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) + .thenReturn(false); initializeBpfOffloadConfiguration(true, null /* unset */); mHasTelephonyManager = true; @@ -413,4 +415,17 @@ public class TetheringConfigurationTest { R.string.config_mobile_hotspot_provision_response)).thenReturn( PROVISIONING_APP_RESPONSE); } + + @Test + public void testEnableLegacyWifiP2PAddress() throws Exception { + final TetheringConfiguration defaultCfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertFalse(defaultCfg.shouldEnableWifiP2pDedicatedIp()); + + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) + .thenReturn(true); + final TetheringConfiguration testCfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp()); + } } diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index b0e401bdda8a..3f712dd1492f 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -3706,33 +3706,40 @@ message MetricsEvent { // OS: O BACKUP_SETTINGS = 818; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: Picture-in-picture was explicitly entered for an activity // VALUE: true if it was entered while hiding as a result of moving to // another task, false otherwise - ACTION_PICTURE_IN_PICTURE_ENTERED = 819; + ACTION_PICTURE_IN_PICTURE_ENTERED = 819 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: The activity currently in picture-in-picture was expanded back to fullscreen // PACKAGE: The package name of the activity that was expanded back to fullscreen - ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820; + ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820 [deprecated=true]; + // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent. // ACTION: The activity currently in picture-in-picture was minimized // VALUE: True if the PiP was minimized, false otherwise - ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821; + ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: Picture-in-picture was dismissed via the dismiss button // VALUE: 0 if dismissed by tap, 1 if dismissed by drag - ACTION_PICTURE_IN_PICTURE_DISMISSED = 822; + ACTION_PICTURE_IN_PICTURE_DISMISSED = 822 [deprecated=true]; - // ACTION: The visibility of the picture-in-picture meny + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. + // ACTION: The visibility of the picture-in-picture menu // VALUE: Whether or not the menu is visible - ACTION_PICTURE_IN_PICTURE_MENU = 823; + ACTION_PICTURE_IN_PICTURE_MENU = 823 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // Enclosing category for group of PICTURE_IN_PICTURE_ASPECT_RATIO_FOO events, // logged when the aspect ratio changes - ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824; + ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824 [deprecated=true]; + // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent. // The current aspect ratio of the PiP, logged when it changes. - PICTURE_IN_PICTURE_ASPECT_RATIO = 825; + PICTURE_IN_PICTURE_ASPECT_RATIO = 825 [deprecated=true]; // FIELD - length in dp of ACTION_LS_* gestures, or zero if not applicable // CATEGORY: GLOBAL_SYSTEM_UI diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 2cd4c6939fa9..b1340228ffa0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -443,7 +443,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (reboundAService || configurationChanged) { onUserStateChangedLocked(userState); } - migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName); + // Passing 0 for restoreFromSdkInt to have this migration check execute each + // time. It can make sure a11y button settings are correctly if there's an a11y + // service updated and modifies the a11y button configuration. + migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName, + /* restoreFromSdkInt = */0); } } @@ -554,7 +558,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { restoreEnabledAccessibilityServicesLocked( intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE), - intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)); + intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE), + intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, + 0)); } } else if (ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED.equals(which)) { synchronized (mLock) { @@ -563,6 +569,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub intent.getIntExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, 0)); } + } else if (Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS.equals(which)) { + synchronized (mLock) { + restoreAccessibilityButtonTargetsLocked( + intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE), + intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)); + } } } } @@ -588,7 +600,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); final Set<String> targetsFromSetting = new ArraySet<>(); readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, - userState.mUserId, targetsFromSetting, str -> str); + userState.mUserId, str -> str, targetsFromSetting); final boolean targetsContainMagnification = targetsFromSetting.contains( MAGNIFICATION_CONTROLLER_NAME); if (targetsContainMagnification == displayMagnificationNavBarEnabled) { @@ -1189,7 +1201,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // the state since the context in which the current user // state was used has changed since it was inactive. onUserStateChangedLocked(userState); - migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null); + // It's better to have this migration in SettingsProvider. Unfortunately, + // SettingsProvider migrated database in a very early stage which A11yManagerService + // haven't finished or started the initialization. We cannot get enough information from + // A11yManagerService to execute these migrations in SettingsProvider. Passing 0 for + // restoreFromSdkInt to have this migration check execute every time, because we did not + // find out a way to detect the device finished the OTA and switch the user. + migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null, + /* restoreFromSdkInt = */0); if (announceNewUser) { // Schedule announcement of the current user if needed. @@ -1234,7 +1253,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Called only during settings restore; currently supports only the owner user // TODO: http://b/22388012 - void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting) { + void restoreEnabledAccessibilityServicesLocked(String oldSetting, String newSetting, + int restoreFromSdkInt) { readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false); readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true); @@ -1246,7 +1266,32 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.mEnabledServices, UserHandle.USER_SYSTEM); onUserStateChangedLocked(userState); - migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null); + migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, null, restoreFromSdkInt); + } + + /** + * User could enable accessibility services and configure accessibility button during the SUW. + * Merges current value of accessibility button settings into the restored one to make sure + * user's preferences of accessibility button updated in SUW are not lost. + * + * Called only during settings restore; currently supports only the owner user + * TODO: http://b/22388012 + */ + void restoreAccessibilityButtonTargetsLocked(String oldSetting, String newSetting) { + final Set<String> targetsFromSetting = new ArraySet<>(); + readColonDelimitedStringToSet(oldSetting, str -> str, targetsFromSetting, + /* doMerge = */false); + readColonDelimitedStringToSet(newSetting, str -> str, targetsFromSetting, + /* doMerge = */true); + + final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); + userState.mAccessibilityButtonTargets.clear(); + userState.mAccessibilityButtonTargets.addAll(targetsFromSetting); + persistColonDelimitedSetToSettingLocked(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, + UserHandle.USER_SYSTEM, userState.mAccessibilityButtonTargets, str -> str); + + scheduleNotifyClientsOfServicesStateChangeLocked(userState); + onUserStateChangedLocked(userState); } private int getClientStateLocked(AccessibilityUserState userState) { @@ -1569,8 +1614,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ private void readComponentNamesFromSettingLocked(String settingName, int userId, Set<ComponentName> outComponentNames) { - readColonDelimitedSettingToSet(settingName, userId, outComponentNames, - str -> ComponentName.unflattenFromString(str)); + readColonDelimitedSettingToSet(settingName, userId, + str -> ComponentName.unflattenFromString(str), outComponentNames); } /** @@ -1585,8 +1630,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void readComponentNamesFromStringLocked(String names, Set<ComponentName> outComponentNames, boolean doMerge) { - readColonDelimitedStringToSet(names, outComponentNames, doMerge, - str -> ComponentName.unflattenFromString(str)); + readColonDelimitedStringToSet(names, str -> ComponentName.unflattenFromString(str), + outComponentNames, doMerge); } @Override @@ -1596,15 +1641,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub componentName -> componentName.flattenToShortString()); } - private <T> void readColonDelimitedSettingToSet(String settingName, int userId, Set<T> outSet, - Function<String, T> toItem) { + private <T> void readColonDelimitedSettingToSet(String settingName, int userId, + Function<String, T> toItem, Set<T> outSet) { final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), settingName, userId); - readColonDelimitedStringToSet(settingValue, outSet, false, toItem); + readColonDelimitedStringToSet(settingValue, toItem, outSet, false); } - private <T> void readColonDelimitedStringToSet(String names, Set<T> outSet, boolean doMerge, - Function<String, T> toItem) { + private <T> void readColonDelimitedStringToSet(String names, Function<String, T> toItem, + Set<T> outSet, boolean doMerge) { if (!doMerge) { outSet.clear(); } @@ -2104,7 +2149,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId); final Set<String> targetsFromSetting = new ArraySet<>(); - readColonDelimitedStringToSet(settingValue, targetsFromSetting, false, str -> str); + readColonDelimitedStringToSet(settingValue, str -> str, targetsFromSetting, false); // Fall back to device's default a11y service, only when setting is never updated. if (settingValue == null) { final String defaultService = mContext.getString( @@ -2128,7 +2173,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private boolean readAccessibilityButtonTargetsLocked(AccessibilityUserState userState) { final Set<String> targetsFromSetting = new ArraySet<>(); readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, - userState.mUserId, targetsFromSetting, str -> str); + userState.mUserId, str -> str, targetsFromSetting); final Set<String> currentTargets = userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON); @@ -2392,9 +2437,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * (It happens when an enabled accessibility service package is upgraded.) * * @param packageName The package name to check, or {@code null} to check all services. + * @param restoreFromSdkInt The target sdk version of the restored source device, or {@code 0} + * if the caller is not related to the restore. */ private void migrateAccessibilityButtonSettingsIfNecessaryLocked( - AccessibilityUserState userState, @Nullable String packageName) { + AccessibilityUserState userState, @Nullable String packageName, int restoreFromSdkInt) { + // No need to migrate settings if they are restored from a version after Q. + if (restoreFromSdkInt > Build.VERSION_CODES.Q) { + return; + } final Set<String> buttonTargets = userState.getShortcutTargetsLocked(ACCESSIBILITY_BUTTON); int lastSize = buttonTargets.size(); 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 bd25f2bea881..3ee5b28ee338 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java @@ -25,10 +25,12 @@ import static java.util.Arrays.copyOfRange; import android.annotation.Nullable; import android.content.Context; +import android.graphics.Point; import android.provider.Settings; import android.util.Log; import android.util.MathUtils; import android.util.Slog; +import android.view.Display; import android.view.MotionEvent; import com.android.internal.annotations.VisibleForTesting; @@ -90,6 +92,8 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate; private final int mDisplayId; + private final Context mContext; + private final Point mTempPoint = new Point(); private final Queue<MotionEvent> mDebugOutputEventHistory; @@ -107,7 +111,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl Slog.i(LOG_TAG, "WindowMagnificationGestureHandler() , displayId = " + displayId + ")"); } - + mContext = context; mWindowMagnificationMgr = windowMagnificationMgr; mDetectShortcutTrigger = detectShortcutTrigger; mDisplayId = displayId; @@ -184,7 +188,14 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl if (!mDetectShortcutTrigger) { return; } - toggleMagnification(Float.NaN, Float.NaN); + final Point screenSize = mTempPoint; + getScreenSize(mTempPoint); + toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f); + } + + private void getScreenSize(Point outSize) { + final Display display = mContext.getDisplay(); + display.getRealSize(outSize); } @Override diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index 7cf5fd621f18..b7fed87d570d 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -50,5 +50,5 @@ public abstract class BatteryStatsInternal { * Informs battery stats of binder stats for the given work source UID. */ public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount, - Collection<BinderCallsStats.CallStat> callStats); + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids); } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 72f29b431880..2534a535079e 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -76,6 +76,8 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; +import libcore.util.NativeAllocationRegistry; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -115,7 +117,6 @@ public class VibratorService extends IVibratorService.Stub // If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration. private static final long ASYNC_TIMEOUT_MULTIPLIER = 2; - // A mapping from the intensity adjustment to the scaling to apply, where the intensity // adjustment is defined as the delta between the default intensity level and the user selected // intensity level. It's important that we apply the scaling on the delta between the two so @@ -128,8 +129,6 @@ public class VibratorService extends IVibratorService.Stub private final LinkedList<VibrationInfo> mPreviousVibrations; private final int mPreviousVibrationsLimit; private final boolean mAllowPriorityVibrationsInLowPowerMode; - private final boolean mSupportsAmplitudeControl; - private final boolean mSupportsExternalControl; private final List<Integer> mSupportedEffects; private final long mCapabilities; private final int mDefaultVibrationAmplitude; @@ -174,22 +173,23 @@ public class VibratorService extends IVibratorService.Stub private int mRingIntensity; private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>(); - static native boolean vibratorExists(); - static native void vibratorInit(); + static native long vibratorInit(); + + static native long vibratorGetFinalizer(); + static native boolean vibratorExists(long controllerPtr); static native void vibratorOn(long milliseconds); - static native void vibratorOff(); - static native boolean vibratorSupportsAmplitudeControl(); - static native void vibratorSetAmplitude(int amplitude); - static native int[] vibratorGetSupportedEffects(); + static native void vibratorOff(long controllerPtr); + static native void vibratorSetAmplitude(long controllerPtr, int amplitude); + static native int[] vibratorGetSupportedEffects(long controllerPtr); static native long vibratorPerformEffect(long effect, long strength, Vibration vibration, boolean withCallback); static native void vibratorPerformComposedEffect( VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration); - static native boolean vibratorSupportsExternalControl(); - static native void vibratorSetExternalControl(boolean enabled); - static native long vibratorGetCapabilities(); - static native void vibratorAlwaysOnEnable(long id, long effect, long strength); - static native void vibratorAlwaysOnDisable(long id); + static native void vibratorSetExternalControl(long controllerPtr, boolean enabled); + static native long vibratorGetCapabilities(long controllerPtr); + static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect, + long strength); + static native void vibratorAlwaysOnDisable(long controllerPtr, long id); private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq, @@ -370,13 +370,20 @@ public class VibratorService extends IVibratorService.Stub mNativeWrapper = injector.getNativeWrapper(); mH = injector.createHandler(Looper.myLooper()); - mNativeWrapper.vibratorInit(); + long controllerPtr = mNativeWrapper.vibratorInit(); + long finalizerPtr = mNativeWrapper.vibratorGetFinalizer(); + + if (finalizerPtr != 0) { + NativeAllocationRegistry registry = + NativeAllocationRegistry.createMalloced( + VibratorService.class.getClassLoader(), finalizerPtr); + registry.registerNativeAllocation(this, controllerPtr); + } + // Reset the hardware to a default state, in case this is a runtime // restart instead of a fresh boot. mNativeWrapper.vibratorOff(); - mSupportsAmplitudeControl = mNativeWrapper.vibratorSupportsAmplitudeControl(); - mSupportsExternalControl = mNativeWrapper.vibratorSupportsExternalControl(); mSupportedEffects = asList(mNativeWrapper.vibratorGetSupportedEffects()); mCapabilities = mNativeWrapper.vibratorGetCapabilities(); @@ -605,7 +612,8 @@ public class VibratorService extends IVibratorService.Stub synchronized (mInputDeviceVibrators) { // Input device vibrators don't support amplitude controls yet, but are still used over // the system vibrator when connected. - return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty(); + return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL) + && mInputDeviceVibrators.isEmpty(); } } @@ -1288,7 +1296,7 @@ public class VibratorService extends IVibratorService.Stub } private void doVibratorSetAmplitude(int amplitude) { - if (mSupportsAmplitudeControl) { + if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { mNativeWrapper.vibratorSetAmplitude(amplitude); } } @@ -1708,14 +1716,25 @@ public class VibratorService extends IVibratorService.Stub @VisibleForTesting public static class NativeWrapper { + private long mNativeControllerPtr = 0; + /** Checks if vibrator exists on device. */ public boolean vibratorExists() { - return VibratorService.vibratorExists(); + return VibratorService.vibratorExists(mNativeControllerPtr); } - /** Initializes connection to vibrator HAL service. */ - public void vibratorInit() { - VibratorService.vibratorInit(); + /** + * Returns native pointer to newly created controller and initializes connection to vibrator + * HAL service. + */ + public long vibratorInit() { + mNativeControllerPtr = VibratorService.vibratorInit(); + return mNativeControllerPtr; + } + + /** Returns pointer to native finalizer function to be called by GC. */ + public long vibratorGetFinalizer() { + return VibratorService.vibratorGetFinalizer(); } /** Turns vibrator on for given time. */ @@ -1725,22 +1744,17 @@ public class VibratorService extends IVibratorService.Stub /** Turns vibrator off. */ public void vibratorOff() { - VibratorService.vibratorOff(); - } - - /** Returns true if vibrator supports {@link #vibratorSetAmplitude(int)}. */ - public boolean vibratorSupportsAmplitudeControl() { - return VibratorService.vibratorSupportsAmplitudeControl(); + VibratorService.vibratorOff(mNativeControllerPtr); } /** Sets the amplitude for the vibrator to run. */ public void vibratorSetAmplitude(int amplitude) { - VibratorService.vibratorSetAmplitude(amplitude); + VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] vibratorGetSupportedEffects() { - return VibratorService.vibratorGetSupportedEffects(); + return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr); } /** Turns vibrator on to perform one of the supported effects. */ @@ -1755,29 +1769,24 @@ public class VibratorService extends IVibratorService.Stub VibratorService.vibratorPerformComposedEffect(effect, vibration); } - /** Returns true if vibrator supports {@link #vibratorSetExternalControl(boolean)}. */ - public boolean vibratorSupportsExternalControl() { - return VibratorService.vibratorSupportsExternalControl(); - } - /** Enabled the device vibrator to be controlled by another service. */ public void vibratorSetExternalControl(boolean enabled) { - VibratorService.vibratorSetExternalControl(enabled); + VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long vibratorGetCapabilities() { - return VibratorService.vibratorGetCapabilities(); + return VibratorService.vibratorGetCapabilities(mNativeControllerPtr); } /** Enable always-on vibration with given id and effect. */ public void vibratorAlwaysOnEnable(long id, long effect, long strength) { - VibratorService.vibratorAlwaysOnEnable(id, effect, strength); + VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength); } /** Disable always-on vibration for given id. */ public void vibratorAlwaysOnDisable(long id) { - VibratorService.vibratorAlwaysOnDisable(id); + VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id); } } @@ -1852,7 +1861,7 @@ public class VibratorService extends IVibratorService.Stub @Override public int onExternalVibrationStart(ExternalVibration vib) { - if (!mSupportsExternalControl) { + if (!hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { return SCALE_MUTE; } if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE, @@ -2142,10 +2151,10 @@ public class VibratorService extends IVibratorService.Stub if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { pw.println(" Compose effects"); } - if (mSupportsAmplitudeControl || hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { + if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { pw.println(" Amplitude control"); } - if (mSupportsExternalControl || hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { + if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { pw.println(" External control"); } if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 91a1487c5245..5555650d9be2 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1579,6 +1579,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70; static final int KILL_APP_ZYGOTE_MSG = 71; static final int BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG = 72; + static final int WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG = 73; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1915,6 +1916,11 @@ public class ActivityManagerService extends IActivityManager.Stub case BINDER_HEAVYHITTER_AUTOSAMPLER_TIMEOUT_MSG: { handleBinderHeavyHitterAutoSamplerTimeOut(); } break; + case WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG: { + synchronized (ActivityManagerService.this) { + ((ContentProviderRecord) msg.obj).onProviderPublishStatusLocked(false); + } + } break; } } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 5081b360b4fe..d72998ba95f1 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -227,8 +227,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteBinderCallStats(int workSourceUid, long incrementatCallCount, - Collection<BinderCallsStats.CallStat> callStats) { - mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats); + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) { + mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats, + binderThreadNativeTids); } } diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 5cc7aba736e1..bfba4afcd4e4 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -50,6 +50,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.IBinder; +import android.os.Message; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; @@ -218,7 +219,7 @@ public class ContentProviderHelper { // of being published... but it is also allowed to run // in the caller's process, so don't make a connection // and just let the caller instantiate its own instance. - ContentProviderHolder holder = cpr.newHolder(null); + ContentProviderHolder holder = cpr.newHolder(null, true); // don't give caller the provider object, it needs to make its own. holder.provider = null; return holder; @@ -415,7 +416,7 @@ public class ContentProviderHelper { // info and allow the caller to instantiate it. Only do // this if the provider is the same user as the caller's // process, or can run as root (so can be in any process). - return cpr.newHolder(null); + return cpr.newHolder(null, true); } if (ActivityManagerDebugConfig.DEBUG_PROVIDER) { @@ -513,6 +514,38 @@ public class ContentProviderHelper { UserHandle.getAppId(cpi.applicationInfo.uid)); } + if (caller != null) { + // The client will be waiting, and we'll notify it when the provider is ready. + synchronized (cpr) { + if (cpr.provider == null) { + if (cpr.launchingApp == null) { + Slog.w(TAG, "Unable to launch app " + + cpi.applicationInfo.packageName + "/" + + cpi.applicationInfo.uid + " for provider " + + name + ": launching app became null"); + EventLogTags.writeAmProviderLostProcess( + UserHandle.getUserId(cpi.applicationInfo.uid), + cpi.applicationInfo.packageName, + cpi.applicationInfo.uid, name); + return null; + } + + if (conn != null) { + conn.waiting = true; + } + Message msg = mService.mHandler.obtainMessage( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG); + msg.obj = cpr; + mService.mHandler.sendMessageDelayed(msg, + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS); + } + } + // Return a holder instance even if we are waiting for the publishing of the provider, + // client will check for the holder.provider to see if it needs to wait for it. + return cpr.newHolder(conn, false); + } + + // Because of the provider's external client (i.e., SHELL), we'll have to wait right here. // Wait for the provider to be published... final long timeout = SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS; @@ -569,7 +602,7 @@ public class ContentProviderHelper { + " caller=" + callerName + "/" + Binder.getCallingUid()); return null; } - return cpr.newHolder(conn); + return cpr.newHolder(conn, false); } void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) { @@ -622,6 +655,8 @@ public class ContentProviderHelper { } if (wasInLaunchingProviders) { mService.mHandler.removeMessages( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst); + mService.mHandler.removeMessages( ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r); } // Make sure the package is associated with the process. @@ -635,6 +670,7 @@ public class ContentProviderHelper { dst.provider = src.provider; dst.setProcess(r); dst.notifyAll(); + dst.onProviderPublishStatusLocked(true); } dst.mRestartCount = 0; mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); @@ -1504,6 +1540,9 @@ public class ContentProviderHelper { synchronized (cpr) { cpr.launchingApp = null; cpr.notifyAll(); + cpr.onProviderPublishStatusLocked(false); + mService.mHandler.removeMessages( + ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, cpr); } final int userId = UserHandle.getUserId(cpr.uid); // Don't remove from provider map if it doesn't match diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java index d8d8cccc05f3..fb8b5d480b27 100644 --- a/services/core/java/com/android/server/am/ContentProviderRecord.java +++ b/services/core/java/com/android/server/am/ContentProviderRecord.java @@ -85,11 +85,12 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { noReleaseNeeded = cpr.noReleaseNeeded; } - public ContentProviderHolder newHolder(ContentProviderConnection conn) { + public ContentProviderHolder newHolder(ContentProviderConnection conn, boolean local) { ContentProviderHolder holder = new ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = noReleaseNeeded; holder.connection = conn; + holder.mLocal = local; return holder; } @@ -179,6 +180,50 @@ final class ContentProviderRecord implements ComponentName.WithComponentName { return !connections.isEmpty() || hasExternalProcessHandles(); } + /** + * Notify all clients that the provider has been published and ready to use, + * or timed out. + * + * @param status true: successfully published; false: timed out + */ + void onProviderPublishStatusLocked(boolean status) { + final int numOfConns = connections.size(); + final int userId = UserHandle.getUserId(appInfo.uid); + for (int i = 0; i < numOfConns; i++) { + final ContentProviderConnection conn = connections.get(i); + if (conn.waiting && conn.client != null) { + final ProcessRecord client = conn.client; + if (!status) { + if (launchingApp == null) { + Slog.w(TAG_AM, "Unable to launch app " + + appInfo.packageName + "/" + + appInfo.uid + " for provider " + + info.authority + ": launching app became null"); + EventLogTags.writeAmProviderLostProcess( + userId, + appInfo.packageName, + appInfo.uid, info.authority); + } else { + Slog.wtf(TAG_AM, "Timeout waiting for provider " + + appInfo.packageName + "/" + + appInfo.uid + " for provider " + + info.authority + + " caller=" + client); + } + } + if (client.thread != null) { + try { + client.thread.notifyContentProviderPublishStatus( + newHolder(status ? conn : null, false), + info.authority, userId, status); + } catch (RemoteException e) { + } + } + } + conn.waiting = false; + } + } + void dump(PrintWriter pw, String prefix, boolean full) { if (full) { pw.print(prefix); pw.print("package="); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 8970ec4c7bb7..0f2dfcc699e2 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -116,6 +116,10 @@ final class CoreSettingsObserver extends ContentObserver { WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT)); sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>( + DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, + WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, int.class, + WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>( DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE, WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class, WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT)); diff --git a/services/core/java/com/android/server/am/PendingTempWhitelists.java b/services/core/java/com/android/server/am/PendingTempWhitelists.java index b36e3c7b9e35..50d58f02baa7 100644 --- a/services/core/java/com/android/server/am/PendingTempWhitelists.java +++ b/services/core/java/com/android/server/am/PendingTempWhitelists.java @@ -32,13 +32,13 @@ final class PendingTempWhitelists { void put(int uid, ActivityManagerService.PendingTempWhitelist value) { mPendingTempWhitelist.put(uid, value); - mService.mAtmInternal.onUidAddedToPendingTempWhitelist(uid, value.tag); + mService.mAtmInternal.onUidAddedToPendingTempAllowlist(uid, value.tag); } void removeAt(int index) { final int uid = mPendingTempWhitelist.keyAt(index); mPendingTempWhitelist.removeAt(index); - mService.mAtmInternal.onUidRemovedFromPendingTempWhitelist(uid); + mService.mAtmInternal.onUidRemovedFromPendingTempAllowlist(uid); } ActivityManagerService.PendingTempWhitelist get(int uid) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 45f95fd3f663..2bbbbf1664d1 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -224,12 +224,35 @@ import java.util.concurrent.atomic.AtomicBoolean; if (!addSpeakerphoneClient(cb, pid, on)) { return false; } + if (on) { + // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for startBluetoothScoForClient_Sync(). + mBtHelper.stopBluetoothScoForPid(pid); + } final boolean wasOn = isSpeakerphoneOn(); updateSpeakerphoneOn(eventSource); return (wasOn != isSpeakerphoneOn()); } } + /** + * Turns speakerphone off for a given pid and update speakerphone state. + * @param pid + */ + @GuardedBy("mDeviceStateLock") + private void setSpeakerphoneOffForPid(int pid) { + SpeakerphoneClient client = getSpeakerphoneClientForPid(pid); + if (client == null) { + return; + } + client.unregisterDeathRecipient(); + mSpeakerphoneClients.remove(client); + final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(") + .append(pid).append(")").toString(); + updateSpeakerphoneOn(eventSource); + } + @GuardedBy("mDeviceStateLock") private void updateSpeakerphoneOn(String eventSource) { if (isSpeakerphoneOnRequested()) { @@ -488,6 +511,10 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, @NonNull String eventSource) { synchronized (mDeviceStateLock) { + // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for setSpeakerphoneOn(true). + setSpeakerphoneOffForPid(Binder.getCallingPid()); mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); } } @@ -1379,6 +1406,16 @@ import java.util.concurrent.atomic.AtomicBoolean; return false; } + @GuardedBy("mDeviceStateLock") + private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) { + for (SpeakerphoneClient cl : mSpeakerphoneClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // List of clients requesting speakerPhone ON @GuardedBy("mDeviceStateLock") private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients = diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 5e8f1ef0db85..ded0f9a3dca7 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -432,19 +432,35 @@ public class BtHelper { // and this must be done on behalf of system server to make sure permissions are granted. final long ident = Binder.clearCallingIdentity(); if (client != null) { - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); - client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, - SCO_MODE_VIRTUAL_CALL); - // If a disconnection is pending, the client will be removed whne clearAllScoClients() - // is called form receiveBtEvent() - if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ - && mScoAudioState != SCO_STATE_DEACTIVATING) { - client.remove(false /*stop */, true /*unregister*/); - } + stopAndRemoveClient(client, eventSource); } Binder.restoreCallingIdentity(ident); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + /*package*/ synchronized void stopBluetoothScoForPid(int pid) { + ScoClient client = getScoClientForPid(pid); + if (client == null) { + return; + } + final String eventSource = new StringBuilder("stopBluetoothScoForPid(") + .append(pid).append(")").toString(); + stopAndRemoveClient(client, eventSource); + } + + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); + client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + SCO_MODE_VIRTUAL_CALL); + // If a disconnection is pending, the client will be removed when clearAllScoClients() + // is called form receiveBtEvent() + if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ + && mScoAudioState != SCO_STATE_DEACTIVATING) { + client.remove(false /*stop */, true /*unregister*/); + } + } /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { if (mHearingAid == null) { @@ -982,6 +998,16 @@ public class BtHelper { return null; } + @GuardedBy("BtHelper.this") + private ScoClient getScoClientForPid(int pid) { + for (ScoClient cl : mScoClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 74ed815f080a..5a4237938086 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -17,6 +17,7 @@ package com.android.server.input; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -66,6 +67,7 @@ import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -181,8 +183,7 @@ public class InputManagerService extends IInputManager.Stub // State for vibrator tokens. private Object mVibratorLock = new Object(); - private HashMap<IBinder, VibratorToken> mVibratorTokens = - new HashMap<IBinder, VibratorToken>(); + private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>(); private int mNextVibratorTokenValue; // State for the currently installed input filter. @@ -190,12 +191,16 @@ public class InputManagerService extends IInputManager.Stub IInputFilter mInputFilter; // guarded by mInputFilterLock InputFilterHost mInputFilterHost; // guarded by mInputFilterLock + private final Object mGestureMonitorPidsLock = new Object(); + @GuardedBy("mGestureMonitorPidsLock") + private final ArrayMap<IBinder, Integer> mGestureMonitorPidsByToken = new ArrayMap<>(); + // The associations of input devices to displays by port. Maps from input device port (String) // to display id (int). Currently only accessed by InputReader. private final Map<String, Integer> mStaticAssociations; private final Object mAssociationsLock = new Object(); @GuardedBy("mAssociationLock") - private final Map<String, Integer> mRuntimeAssociations = new HashMap<String, Integer>(); + private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>(); private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); @@ -540,13 +545,17 @@ public class InputManagerService extends IInputManager.Stub if (displayId < Display.DEFAULT_DISPLAY) { throw new IllegalArgumentException("displayId must >= 0."); } + final int pid = Binder.getCallingPid(); final long ident = Binder.clearCallingIdentity(); try { InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName); InputMonitorHost host = new InputMonitorHost(inputChannels[0]); - nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, - true /*isGestureMonitor*/); + nativeRegisterInputMonitor( + mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/); + synchronized (mGestureMonitorPidsLock) { + mGestureMonitorPidsByToken.put(inputChannels[1].getToken(), pid); + } return new InputMonitor(inputChannels[1], host); } finally { Binder.restoreCallingIdentity(ident); @@ -575,6 +584,9 @@ public class InputManagerService extends IInputManager.Stub if (inputChannel == null) { throw new IllegalArgumentException("inputChannel must not be null."); } + synchronized (mGestureMonitorPidsLock) { + mGestureMonitorPidsByToken.remove(inputChannel.getToken()); + } nativeUnregisterInputChannel(mPtr, inputChannel); } @@ -1838,6 +1850,7 @@ public class InputManagerService extends IInputManager.Stub if (dumpStr != null) { pw.println(dumpStr); dumpAssociations(pw); + dumpGestureMonitorPidsByToken(pw); } } @@ -1861,6 +1874,19 @@ public class InputManagerService extends IInputManager.Stub } } + private void dumpGestureMonitorPidsByToken(PrintWriter pw) { + synchronized (mGestureMonitorPidsLock) { + if (!mGestureMonitorPidsByToken.isEmpty()) { + pw.println("Gesture monitor pids by token:"); + for (int i = 0; i < mGestureMonitorPidsByToken.size(); i++) { + pw.print(" " + i + ": "); + pw.print(" token: " + mGestureMonitorPidsByToken.keyAt(i)); + pw.println(" pid: " + mGestureMonitorPidsByToken.valueAt(i)); + } + } + } + } + private boolean checkCallingPermission(String permission, String func) { // Quick check: if the calling permission is me, it's all okay. if (Binder.getCallingPid() == Process.myPid()) { @@ -1883,6 +1909,7 @@ public class InputManagerService extends IInputManager.Stub public void monitor() { synchronized (mInputFilterLock) { } synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */} + synchronized (mGestureMonitorPidsLock) { /* Test if blocked by gesture monitor pids lock */} nativeMonitor(mPtr); } @@ -1944,6 +1971,9 @@ public class InputManagerService extends IInputManager.Stub // Native callback. private void notifyInputChannelBroken(IBinder token) { + synchronized (mGestureMonitorPidsLock) { + mGestureMonitorPidsByToken.remove(token); + } mWindowManagerCallbacks.notifyInputChannelBroken(token); } @@ -1959,8 +1989,12 @@ public class InputManagerService extends IInputManager.Stub // Native callback. private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) { - return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, - token, reason); + Integer gestureMonitorPid; + synchronized (mGestureMonitorPidsLock) { + gestureMonitorPid = mGestureMonitorPidsByToken.get(token); + } + return mWindowManagerCallbacks.notifyANR(inputApplicationHandle, token, gestureMonitorPid, + reason); } // Native callback. @@ -2206,22 +2240,48 @@ public class InputManagerService extends IInputManager.Stub * Callback interface implemented by the Window Manager. */ public interface WindowManagerCallbacks { + /** + * This callback is invoked when the confuguration changes. + */ public void notifyConfigurationChanged(); + /** + * This callback is invoked when the lid switch changes state. + * @param whenNanos the time when the change occurred + * @param lidOpen true if the lid is open + */ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen); + /** + * This callback is invoked when the camera lens cover switch changes state. + * @param whenNanos the time when the change occurred + * @param lensCovered true is the lens is covered + */ public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered); + /** + * This callback is invoked when an input channel is closed unexpectedly. + * @param token the connection token of the broken channel + */ public void notifyInputChannelBroken(IBinder token); /** - * Notifies the window manager about an application that is not responding. - * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. + * Notify the window manager about an application that is not responding. + * Return a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch. */ long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, - String reason); + @Nullable Integer pid, String reason); - public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags); + /** + * This callback is invoked when an event first arrives to InputDispatcher and before it is + * placed onto InputDispatcher's queue. If this event is intercepted, it will never be + * processed by InputDispacher. + * @param event The key event that's arriving to InputDispatcher + * @param policyFlags The policy flags + * @return the flags that tell InputDispatcher how to handle the event (for example, whether + * to pass it to the user) + */ + int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags); /** * Provides an opportunity for the window manager policy to intercept early motion event @@ -2231,11 +2291,23 @@ public class InputManagerService extends IInputManager.Stub int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos, int policyFlags); - public long interceptKeyBeforeDispatching(IBinder token, - KeyEvent event, int policyFlags); + /** + * This callback is invoked just before the key is about to be sent to an application. + * This allows the policy to make some last minute decisions on whether to intercept this + * key. + * @param token the window token that's about to receive this event + * @param event the key event that's being dispatched + * @param policyFlags the policy flags + * @return negative value if the key should be skipped (not sent to the app). 0 if the key + * should proceed getting dispatched to the app. positive value to indicate the additional + * time delay, in nanoseconds, to wait before sending this key to the app. + */ + long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags); - public KeyEvent dispatchUnhandledKey(IBinder token, - KeyEvent event, int policyFlags); + /** + * Dispatch unhandled key + */ + KeyEvent dispatchUnhandledKey(IBinder token, KeyEvent event, int policyFlags); public int getPointerLayer(); diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 249b6801758b..07527c2a15d8 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -270,11 +270,12 @@ public abstract class ApexManager { abstract boolean revertActiveSessions(); /** - * Abandons the staged session with the given sessionId. + * Abandons the staged session with the given sessionId. Client should handle {@code false} + * return value carefully as failure here can leave device in inconsistent state. * - * @return {@code true} upon success, {@code false} if any remote exception occurs + * @return {@code true} upon success, {@code false} if any exception occurs */ - abstract boolean abortStagedSession(int sessionId) throws PackageManagerException; + abstract boolean abortStagedSession(int sessionId); /** * Uninstalls given {@code apexPackage}. @@ -753,17 +754,13 @@ public abstract class ApexManager { } @Override - boolean abortStagedSession(int sessionId) throws PackageManagerException { + boolean abortStagedSession(int sessionId) { try { waitForApexService().abortStagedSession(sessionId); return true; - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - return false; } catch (Exception e) { - throw new PackageManagerException( - PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, - "Failed to abort staged session : " + e.getMessage()); + Slog.e(TAG, e.getMessage(), e); + return false; } } @@ -1122,7 +1119,7 @@ public abstract class ApexManager { } @Override - boolean abortStagedSession(int sessionId) throws PackageManagerException { + boolean abortStagedSession(int sessionId) { throw new UnsupportedOperationException(); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 7765f18300fe..ba708229c7ed 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2091,7 +2091,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (ps == null) { return 0; } - final File apkDirOrPath = ps.codePath; + final File apkDirOrPath = ps.getCodePath(); if (apkDirOrPath == null) { return 0; } @@ -3321,7 +3321,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionReady() { synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = true; mStagedSessionApplied = false; mStagedSessionFailed = false; @@ -3332,10 +3333,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } /** {@hide} */ - void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, - String errorMessage) { + void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) { synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = false; mStagedSessionApplied = false; mStagedSessionFailed = true; @@ -3350,7 +3351,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionApplied() { synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = false; mStagedSessionApplied = true; mStagedSessionFailed = false; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a726c8d26b98..58a1648e51ad 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3010,7 +3010,7 @@ public class PackageManagerService extends IPackageManager.Stub final int packageSettingCount = mSettings.mPackages.size(); for (int i = packageSettingCount - 1; i >= 0; i--) { PackageSetting ps = mSettings.mPackages.valueAt(i); - if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists()) + if (!isExternal(ps) && (ps.getCodePath() == null || !ps.getCodePath().exists()) && mSettings.getDisabledSystemPkgLPr(ps.name) != null) { mSettings.mPackages.removeAt(i); mSettings.enableSystemPackageLPw(ps.name); @@ -3175,11 +3175,11 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.WARN, "Expecting better updated system app for " + ps.name + "; removing system app. Last known" - + " codePath=" + ps.codePathString + + " codePath=" + ps.getCodePathString() + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.getLongVersionCode()); removePackageLI(scannedPkg, true); - mExpectingBetter.put(ps.name, ps.codePath); + mExpectingBetter.put(ps.name, ps.getCodePath()); } continue; @@ -3202,14 +3202,14 @@ public class PackageManagerService extends IPackageManager.Stub // code path, but, changes the package name. final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); - if (disabledPs.codePath == null || !disabledPs.codePath.exists() + if (disabledPs.getCodePath() == null || !disabledPs.getCodePath().exists() || disabledPs.pkg == null) { possiblyDeletedUpdatedSystemApps.add(ps.name); } else { // We're expecting that the system app should remain disabled, but add // it to expecting better to recover in case the data version cannot // be scanned. - mExpectingBetter.put(disabledPs.name, disabledPs.codePath); + mExpectingBetter.put(disabledPs.name, disabledPs.getCodePath()); } } } @@ -8551,6 +8551,15 @@ public class PackageManagerService extends IPackageManager.Stub if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { + if (listFactory) { + if (!ps.isSystem()) { + continue; + } + PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps); + if (psDisabled != null) { + ps = psDisabled; + } + } if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } @@ -8565,7 +8574,16 @@ public class PackageManagerService extends IPackageManager.Stub } else { list = new ArrayList<>(mPackages.size()); for (AndroidPackage p : mPackages.values()) { - final PackageSetting ps = getPackageSetting(p.getPackageName()); + PackageSetting ps = getPackageSetting(p.getPackageName()); + if (listFactory) { + if (!p.isSystem()) { + continue; + } + PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps); + if (psDisabled != null) { + ps = psDisabled; + } + } if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } @@ -9150,7 +9168,7 @@ public class PackageManagerService extends IPackageManager.Stub : getLastModifiedTime(parsedPackage); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage); if (ps != null && !forceCollect - && ps.codePathString.equals(parsedPackage.getCodePath()) + && ps.getCodePathString().equals(parsedPackage.getCodePath()) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { @@ -9383,8 +9401,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - final boolean newPkgChangedPaths = - pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath()); + final boolean newPkgChangedPaths = pkgAlreadyExists + && !pkgSetting.getCodePathString().equals(parsedPackage.getCodePath()); final boolean newPkgVersionGreater = pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated @@ -9403,11 +9421,11 @@ public class PackageManagerService extends IPackageManager.Stub "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode() - + "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath()); + + "; " + pkgSetting.getCodePathString() + + " --> " + parsedPackage.getCodePath()); final InstallArgs args = createInstallArgsForExisting( - pkgSetting.codePathString, - pkgSetting.resourcePathString, getAppDexInstructionSets( + pkgSetting.getCodePathString(), getAppDexInstructionSets( pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString)); args.cleanUpResourcesLI(); synchronized (mLock) { @@ -9482,11 +9500,10 @@ public class PackageManagerService extends IPackageManager.Stub + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode() - + "; " + pkgSetting.codePathString + " --> " + + "; " + pkgSetting.getCodePathString() + " --> " + parsedPackage.getCodePath()); InstallArgs args = createInstallArgsForExisting( - pkgSetting.codePathString, - pkgSetting.resourcePathString, getAppDexInstructionSets( + pkgSetting.getCodePathString(), getAppDexInstructionSets( pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); @@ -9499,7 +9516,7 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name - + "; old: " + pkgSetting.codePathString + " @ " + + "; old: " + pkgSetting.getCodePathString() + " @ " + pkgSetting.versionCode + "; new: " + parsedPackage.getCodePath() + " @ " + parsedPackage.getCodePath()); @@ -11325,7 +11342,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changedAbiCodePath == null) { changedAbiCodePath = new ArrayList<>(); } - changedAbiCodePath.add(ps.codePathString); + changedAbiCodePath.add(ps.getCodePathString()); } } } @@ -11421,7 +11438,6 @@ public class PackageManagerService extends IPackageManager.Stub // Initialize package source and resource directories final File destCodeFile = new File(parsedPackage.getCodePath()); - final File destResourceFile = new File(parsedPackage.getCodePath()); // We keep references to the derived CPU Abis from settings in oder to reuse // them in the case where we're not upgrading or booting for the first time. @@ -11479,7 +11495,7 @@ public class PackageManagerService extends IPackageManager.Stub // REMOVE SharedUserSetting from method; update in a separate call pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(), originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting, - destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(), + destCodeFile, parsedPackage.getNativeLibraryRootDir(), AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage), AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage), parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user, @@ -11497,7 +11513,7 @@ public class PackageManagerService extends IPackageManager.Stub // secondaryCpuAbi are not known at this point so we always update them // to null here, only to reset them at a later point. Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting, - destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(), + destCodeFile, parsedPackage.getNativeLibraryDir(), AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting), AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting), PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting), @@ -12066,15 +12082,13 @@ public class PackageManagerService extends IPackageManager.Stub if (known != null) { if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Examining " + pkg.getCodePath() - + " and requiring known paths " + known.codePathString - + " & " + known.resourcePathString); + + " and requiring known path " + known.getCodePathString()); } - if (!pkg.getCodePath().equals(known.codePathString) - || !pkg.getCodePath().equals(known.resourcePathString)) { + if (!pkg.getCodePath().equals(known.getCodePathString())) { throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED, "Application package " + pkg.getPackageName() + " found at " + pkg.getCodePath() - + " but expected at " + known.codePathString + + " but expected at " + known.getCodePathString() + "; ignoring."); } } else { @@ -15586,9 +15600,8 @@ public class PackageManagerService extends IPackageManager.Stub * Create args that describe an existing installed package. Typically used * when cleaning up old installs, or used as a move source. */ - private InstallArgs createInstallArgsForExisting(String codePath, - String resourcePath, String[] instructionSets) { - return new FileInstallArgs(codePath, resourcePath, instructionSets); + private InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) { + return new FileInstallArgs(codePath, instructionSets); } static abstract class InstallArgs { @@ -15669,10 +15682,8 @@ public class PackageManagerService extends IPackageManager.Stub abstract boolean doRename(int status, ParsedPackage parsedPackage); abstract int doPostInstall(int status, int uid); - /** @see PackageSettingBase#codePathString */ + /** @see PackageSettingBase#getCodePath() */ abstract String getCodePath(); - /** @see PackageSettingBase#resourcePathString */ - abstract String getResourcePath(); // Need installer lock especially for dex file removal. abstract void cleanUpResourcesLI(); @@ -15743,14 +15754,13 @@ public class PackageManagerService extends IPackageManager.Stub } /** Existing install */ - FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) { + FileInstallArgs(String codePath, String[] instructionSets) { super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY, null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0, PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN, false, DataLoaderType.NONE); this.codeFile = (codePath != null) ? new File(codePath) : null; - this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; } int copyApk() { @@ -15766,7 +15776,6 @@ public class PackageManagerService extends IPackageManager.Stub if (origin.staged) { if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy"); codeFile = origin.file; - resourceFile = origin.file; return PackageManager.INSTALL_SUCCEEDED; } @@ -15775,7 +15784,6 @@ public class PackageManagerService extends IPackageManager.Stub final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral); codeFile = tempDir; - resourceFile = tempDir; } catch (IOException e) { Slog.w(TAG, "Failed to create copy file: " + e); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; @@ -15846,7 +15854,6 @@ public class PackageManagerService extends IPackageManager.Stub // Reflect the rename internally codeFile = afterCodeFile; - resourceFile = afterCodeFile; // Reflect the rename in scanned details try { @@ -15875,11 +15882,6 @@ public class PackageManagerService extends IPackageManager.Stub return (codeFile != null) ? codeFile.getAbsolutePath() : null; } - @Override - String getResourcePath() { - return (resourceFile != null) ? resourceFile.getAbsolutePath() : null; - } - private boolean cleanUp() { if (codeFile == null || !codeFile.exists()) { return false; @@ -15892,10 +15894,6 @@ public class PackageManagerService extends IPackageManager.Stub removeCodePathLI(codeFile); - if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) { - resourceFile.delete(); - } - return true; } @@ -15927,7 +15925,6 @@ public class PackageManagerService extends IPackageManager.Stub */ class MoveInstallArgs extends InstallArgs { private File codeFile; - private File resourceFile; /** New install */ MoveInstallArgs(InstallParams params) { @@ -15950,7 +15947,6 @@ public class PackageManagerService extends IPackageManager.Stub final String toPathName = new File(move.fromCodePath).getName(); codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName); - resourceFile = codeFile; if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile); return PackageManager.INSTALL_SUCCEEDED; @@ -15987,11 +15983,6 @@ public class PackageManagerService extends IPackageManager.Stub return (codeFile != null) ? codeFile.getAbsolutePath() : null; } - @Override - String getResourcePath() { - return (resourceFile != null) ? resourceFile.getAbsolutePath() : null; - } - private boolean cleanUp(String volumeUuid) { final String toPathName = new File(move.fromCodePath).getName(); final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid), @@ -16777,7 +16768,6 @@ public class PackageManagerService extends IPackageManager.Stub // installed. We need to make sure to delete the older one's .apk. res.removedInfo.args = createInstallArgsForExisting( oldPackage.getCodePath(), - oldPackage.getCodePath(), getAppDexInstructionSets( AndroidPackageUtils.getPrimaryCpuAbi(oldPackage, deletedPkgSetting), @@ -18558,7 +18548,6 @@ public class PackageManagerService extends IPackageManager.Stub // user handle installed state int[] allUsers; /** enabled state of the uninstalled application */ - final int origEnabledState; synchronized (mLock) { uninstalledPs = mSettings.mPackages.get(packageName); if (uninstalledPs == null) { @@ -18574,10 +18563,6 @@ public class PackageManagerService extends IPackageManager.Stub } disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName); - // Save the enabled state before we delete the package. When deleting a stub - // application we always set the enabled state to 'disabled'. - origEnabledState = uninstalledPs == null - ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId); // Static shared libs can be declared by any package, so let us not // allow removing a package if it provides a lib others depend on. pkg = mPackages.get(packageName); @@ -18656,20 +18641,32 @@ public class PackageManagerService extends IPackageManager.Stub if (stubPkg != null && stubPkg.isStub()) { final PackageSetting stubPs; synchronized (mLock) { - // restore the enabled state of the stub; the state is overwritten when - // the stub is uninstalled stubPs = mSettings.getPackageLPr(stubPkg.getPackageName()); - if (stubPs != null) { - stubPs.setEnabled(origEnabledState, userId, "android"); - } } - if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT - || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) { - if (DEBUG_COMPRESSION) { - Slog.i(TAG, "Enabling system stub after removal; pkg: " - + stubPkg.getPackageName()); + + if (stubPs != null) { + boolean enable = false; + for (int aUserId : allUsers) { + if (stubPs.getInstalled(aUserId)) { + int enabled = stubPs.getEnabled(aUserId); + if (enabled == COMPONENT_ENABLED_STATE_DEFAULT + || enabled == COMPONENT_ENABLED_STATE_ENABLED) { + enable = true; + break; + } + } + } + + if (enable) { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Enabling system stub after removal; pkg: " + + stubPkg.getPackageName()); + } + enableCompressedPackage(stubPkg, stubPs); + } else if (DEBUG_COMPRESSION) { + Slog.i(TAG, "System stub disabled for all users, leaving uncompressed " + + "after removal; pkg: " + stubPkg.getPackageName()); } - enableCompressedPackage(stubPkg, stubPs); } } } @@ -18998,7 +18995,7 @@ public class PackageManagerService extends IPackageManager.Stub // Install the system package if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); try { - installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles, + installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles, outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings); } catch (PackageManagerException e) { @@ -19013,8 +19010,15 @@ public class PackageManagerService extends IPackageManager.Stub // and re-enable it afterward. final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName()); if (stubPs != null) { - stubPs.setEnabled( - COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); + int userId = action.user == null + ? UserHandle.USER_ALL : action.user.getIdentifier(); + if (userId == UserHandle.USER_ALL) { + for (int aUserId : allUserHandles) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android"); + } + } else if (userId >= UserHandle.USER_SYSTEM) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android"); + } } } } @@ -19123,7 +19127,7 @@ public class PackageManagerService extends IPackageManager.Stub // Delete application code and resources only for parent packages if (deleteCodeAndResources && (outInfo != null)) { outInfo.args = createInstallArgsForExisting( - ps.codePathString, ps.resourcePathString, getAppDexInstructionSets( + ps.getCodePathString(), getAppDexInstructionSets( ps.primaryCpuAbiString, ps.secondaryCpuAbiString)); if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args); } @@ -19660,7 +19664,7 @@ public class PackageManagerService extends IPackageManager.Stub final String[] packageNames = { packageName }; final long[] ceDataInodes = { ps.getCeDataInode(userId) }; - final String[] codePaths = { ps.codePathString }; + final String[] codePaths = { ps.getCodePathString() }; try { mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0, @@ -22582,11 +22586,11 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstallLock) { final AndroidPackage pkg; try { - pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null); + pkg = scanPackageTracedLI(ps.getCodePath(), parseFlags, SCAN_INITIAL, 0, null); loaded.add(pkg); } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage()); + Slog.w(TAG, "Failed to scan " + ps.getCodePath() + ": " + e.getMessage()); } if (!Build.FINGERPRINT.equals(ver.fingerprint)) { @@ -22657,33 +22661,33 @@ public class PackageManagerService extends IPackageManager.Stub final ArrayList<AndroidPackage> unloaded = new ArrayList<>(); synchronized (mInstallLock) { - synchronized (mLock) { - final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid); - for (PackageSetting ps : packages) { - if (ps.pkg == null) continue; - - final AndroidPackage pkg = ps.pkg; - final int deleteFlags = PackageManager.DELETE_KEEP_DATA; - final PackageRemovedInfo outInfo = new PackageRemovedInfo(this); - - try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags, - "unloadPrivatePackagesInner")) { - if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo, - false, null)) { - unloaded.add(pkg); - } else { - Slog.w(TAG, "Failed to unload " + ps.codePath); + synchronized (mLock) { + final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid); + for (PackageSetting ps : packages) { + if (ps.pkg == null) continue; + + final AndroidPackage pkg = ps.pkg; + final int deleteFlags = PackageManager.DELETE_KEEP_DATA; + final PackageRemovedInfo outInfo = new PackageRemovedInfo(this); + + try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags, + "unloadPrivatePackagesInner")) { + if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo, + false, null)) { + unloaded.add(pkg); + } else { + Slog.w(TAG, "Failed to unload " + ps.getCodePath()); + } } + + // Try very hard to release any references to this package + // so we don't risk the system server being killed due to + // open FDs + AttributeCache.instance().removePackage(ps.name); } - // Try very hard to release any references to this package - // so we don't risk the system server being killed due to - // open FDs - AttributeCache.instance().removePackage(ps.name); + mSettings.writeLPr(); } - - mSettings.writeLPr(); - } } if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded); @@ -22726,7 +22730,7 @@ public class PackageManagerService extends IPackageManager.Stub final int packageCount = mSettings.mPackages.size(); for (int i = 0; i < packageCount; i++) { final PackageSetting ps = mSettings.mPackages.valueAt(i); - codePaths.add(ps.codePath.getAbsolutePath()); + codePaths.add(ps.getCodePath().getAbsolutePath()); } return codePaths; } @@ -23643,8 +23647,20 @@ public class PackageManagerService extends IPackageManager.Stub } } - void onNewUserCreated(final int userId) { - mPermissionManager.onNewUserCreated(userId); + void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) { + if (DEBUG_PERMISSIONS) { + Slog.d(TAG, "onNewUserCreated(id=" + userId + + ", convertedFromPreCreated=" + convertedFromPreCreated + ")"); + } + if (!convertedFromPreCreated) { + mPermissionManager.onNewUserCreated(userId); + return; + } + if (!readPermissionStateForUser(userId)) { + // Could not read the existing permissions, re-grant them. + Slog.i(TAG, "re-granting permissions for pre-created user " + userId); + mPermissionManager.onNewUserCreated(userId); + } } boolean readPermissionStateForUser(@UserIdInt int userId) { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 668f375e2e9b..7aeec6d68d26 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -423,13 +423,15 @@ class PackageManagerShellCommand extends ShellCommand { final List<ApplicationInfo> list; if (packageName == null) { final ParceledListSlice<ApplicationInfo> packages = - mInterface.getInstalledApplications( - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); + mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + UserHandle.USER_SYSTEM); list = packages.getList(); } else { list = new ArrayList<>(1); - list.add(mInterface.getApplicationInfo(packageName, - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM)); + list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + UserHandle.USER_SYSTEM)); } for (ApplicationInfo info : list) { if (info.isUpdatedSystemApp()) { diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 432d7f335ebc..a3a727367c56 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -71,13 +71,13 @@ public class PackageSetting extends PackageSettingBase { private PackageStateUnserialized pkgState = new PackageStateUnserialized(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public PackageSetting(String name, String realName, File codePath, File resourcePath, + public PackageSetting(String name, String realName, @NonNull File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, long pVersionCode, int pkgFlags, int privateFlags, int sharedUserId, String[] usesStaticLibraries, long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups) { - super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString, + super(name, realName, codePath, legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode, pkgFlags, privateFlags, usesStaticLibraries, usesStaticLibrariesVersions); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 834303cc14c6..6010344b8c65 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -64,10 +64,8 @@ public abstract class PackageSettingBase extends SettingBase { * this is path to single base APK file; for cluster packages this is * path to the cluster directory. */ - File codePath; - String codePathString; - File resourcePath; - String resourcePathString; + private File mCodePath; + private String mCodePathString; String[] usesStaticLibraries; long[] usesStaticLibrariesVersions; @@ -138,7 +136,7 @@ public abstract class PackageSettingBase extends SettingBase { boolean forceQueryableOverride; - PackageSettingBase(String name, String realName, File codePath, File resourcePath, + PackageSettingBase(String name, String realName, @NonNull File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, long pVersionCode, int pkgFlags, int pkgPrivateFlags, @@ -148,10 +146,7 @@ public abstract class PackageSettingBase extends SettingBase { this.realName = realName; this.usesStaticLibraries = usesStaticLibraries; this.usesStaticLibrariesVersions = usesStaticLibrariesVersions; - this.codePath = codePath; - this.codePathString = codePath.toString(); - this.resourcePath = resourcePath; - this.resourcePathString = resourcePath.toString(); + setCodePath(codePath); this.legacyNativeLibraryPathString = legacyNativeLibraryPathString; this.primaryCpuAbiString = primaryCpuAbiString; this.secondaryCpuAbiString = secondaryCpuAbiString; @@ -235,8 +230,7 @@ public abstract class PackageSettingBase extends SettingBase { } private void doCopy(PackageSettingBase orig) { - codePath = orig.codePath; - codePathString = orig.codePathString; + setCodePath(orig.getCodePath()); cpuAbiOverrideString = orig.cpuAbiOverrideString; firstInstallTime = orig.firstInstallTime; installPermissionsFixed = orig.installPermissionsFixed; @@ -246,8 +240,6 @@ public abstract class PackageSettingBase extends SettingBase { legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString; // Intentionally skip mOldCodePaths; it's not relevant for copies primaryCpuAbiString = orig.primaryCpuAbiString; - resourcePath = orig.resourcePath; - resourcePathString = orig.resourcePathString; secondaryCpuAbiString = orig.secondaryCpuAbiString; signatures = orig.signatures; timeStamp = orig.timeStamp; @@ -705,6 +697,20 @@ public abstract class PackageSettingBase extends SettingBase { return userState.harmfulAppWarning; } + PackageSettingBase setCodePath(@NonNull File codePath) { + this.mCodePath = codePath; + this.mCodePathString = codePath.toString(); + return this; + } + + File getCodePath() { + return mCodePath; + } + + String getCodePathString() { + return mCodePathString; + } + /** * @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer) * @@ -727,10 +733,7 @@ public abstract class PackageSettingBase extends SettingBase { protected PackageSettingBase updateFrom(PackageSettingBase other) { super.copyFrom(other); - this.codePath = other.codePath; - this.codePathString = other.codePathString; - this.resourcePath = other.resourcePath; - this.resourcePathString = other.resourcePathString; + setCodePath(other.getCodePath()); this.usesStaticLibraries = other.usesStaticLibraries; this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions; this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 1805713387ae..0061480c97f1 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -538,7 +538,7 @@ public final class Settings { return null; } p.getPkgState().setUpdatedSystemApp(false); - PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath, + PackageSetting ret = addPackageLPw(name, p.realName, p.getCodePath(), p.legacyNativeLibraryPathString, p.primaryCpuAbiString, p.secondaryCpuAbiString, p.cpuAbiOverrideString, p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags, @@ -558,7 +558,7 @@ public final class Settings { mDisabledSysPackages.remove(name); } - PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, + PackageSetting addPackageLPw(String name, String realName, File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries, @@ -572,10 +572,9 @@ public final class Settings { "Adding duplicate package, keeping first: " + name); return null; } - p = new PackageSetting(name, realName, codePath, resourcePath, - legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, - cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, - 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames, + p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString, + primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags, + pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames, mimeGroups); p.appId = uid; if (registerExistingAppIdLPw(uid, p, name)) { @@ -635,7 +634,7 @@ public final class Settings { */ static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg, PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser, - File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi, + File codePath, String legacyNativeLibraryPath, String primaryCpuAbi, String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags, UserHandle installUser, boolean allowInstall, boolean instantApp, boolean virtualPreload, UserManagerService userManager, @@ -646,12 +645,11 @@ public final class Settings { if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + pkgName + " is adopting original package " + originalPkg.name); pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/); - pkgSetting.codePath = codePath; + pkgSetting.setCodePath(codePath); pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; pkgSetting.pkgFlags = pkgFlags; pkgSetting.pkgPrivateFlags = pkgPrivateFlags; pkgSetting.primaryCpuAbiString = primaryCpuAbi; - pkgSetting.resourcePath = resourcePath; pkgSetting.secondaryCpuAbiString = secondaryCpuAbi; // NOTE: Create a deeper copy of the package signatures so we don't // overwrite the signatures in the original package setting. @@ -662,7 +660,7 @@ public final class Settings { // Update new package state. pkgSetting.setTimeStamp(codePath.lastModified()); } else { - pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath, + pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi, null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, usesStaticLibraries, @@ -756,10 +754,9 @@ public final class Settings { */ static void updatePackageSetting(@NonNull PackageSetting pkgSetting, @Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser, - @NonNull File codePath, File resourcePath, - @Nullable String legacyNativeLibraryPath, @Nullable String primaryCpuAbi, - @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags, - @NonNull UserManagerService userManager, + @NonNull File codePath, @Nullable String legacyNativeLibraryPath, + @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags, + int pkgPrivateFlags, @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions, @Nullable Set<String> mimeGroupNames) throws PackageManagerException { @@ -773,12 +770,12 @@ public final class Settings { "Updating application package " + pkgName + " failed"); } - if (!pkgSetting.codePath.equals(codePath)) { + if (!pkgSetting.getCodePath().equals(codePath)) { final boolean isSystem = pkgSetting.isSystem(); Slog.i(PackageManagerService.TAG, "Update" + (isSystem ? " system" : "") + " package " + pkgName - + " code path from " + pkgSetting.codePathString + + " code path from " + pkgSetting.getCodePathString() + " to " + codePath.toString() + "; Retain data and using new"); if (!isSystem) { @@ -800,19 +797,7 @@ public final class Settings { // internal to external storage or vice versa. pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; } - pkgSetting.codePath = codePath; - pkgSetting.codePathString = codePath.toString(); - } - if (!pkgSetting.resourcePath.equals(resourcePath)) { - final boolean isSystem = pkgSetting.isSystem(); - Slog.i(PackageManagerService.TAG, - "Update" + (isSystem ? " system" : "") - + " package " + pkgName - + " resource path from " + pkgSetting.resourcePathString - + " to " + resourcePath.toString() - + "; Retain data and using new"); - pkgSetting.resourcePath = resourcePath; - pkgSetting.resourcePathString = resourcePath.toString(); + pkgSetting.setCodePath(codePath); } // If what we are scanning is a system (and possibly privileged) package, // then make it so, regardless of whether it was previously installed only @@ -2812,14 +2797,11 @@ public final class Settings { if (pkg.realName != null) { serializer.attribute(null, "realName", pkg.realName); } - serializer.attribute(null, "codePath", pkg.codePathString); + serializer.attribute(null, "codePath", pkg.getCodePathString()); serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime)); serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } if (pkg.legacyNativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString); } @@ -2857,10 +2839,7 @@ public final class Settings { if (pkg.realName != null) { serializer.attribute(null, "realName", pkg.realName); } - serializer.attribute(null, "codePath", pkg.codePathString); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } + serializer.attribute(null, "codePath", pkg.getCodePathString()); if (pkg.legacyNativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString); @@ -3559,13 +3538,10 @@ public final class Settings { String name = parser.getAttributeValue(null, ATTR_NAME); String realName = parser.getAttributeValue(null, "realName"); String codePathStr = parser.getAttributeValue(null, "codePath"); - String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi"); String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - String parentPackageName = parser.getAttributeValue(null, "parentPackageName"); - String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi"); String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi"); String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride"); @@ -3574,9 +3550,6 @@ public final class Settings { primaryCpuAbiStr = legacyCpuAbiStr; } - if (resourcePathStr == null) { - resourcePathStr = codePathStr; - } String version = parser.getAttributeValue(null, "version"); long versionCode = 0; if (version != null) { @@ -3593,9 +3566,8 @@ public final class Settings { pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; } PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), - new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr, - secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags, - 0 /*sharedUserId*/, null, null, null); + legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr, + versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null); String timeStampStr = parser.getAttributeValue(null, "ft"); if (timeStampStr != null) { try { @@ -3666,7 +3638,6 @@ public final class Settings { String idStr = null; String sharedIdStr = null; String codePathStr = null; - String resourcePathStr = null; String legacyCpuAbiString = null; String legacyNativeLibraryPathStr = null; String primaryCpuAbiString = null; @@ -3700,7 +3671,6 @@ public final class Settings { uidError = parser.getAttributeValue(null, "uidError"); sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); codePathStr = parser.getAttributeValue(null, "codePath"); - resourcePathStr = parser.getAttributeValue(null, "resourcePath"); legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); @@ -3818,9 +3788,6 @@ public final class Settings { + " sharedUserId=" + sharedIdStr); final int userId = idStr != null ? Integer.parseInt(idStr) : 0; final int sharedUserId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; - if (resourcePathStr == null) { - resourcePathStr = codePathStr; - } if (realName != null) { realName = realName.intern(); } @@ -3834,10 +3801,10 @@ public final class Settings { + parser.getPositionDescription()); } else if (userId > 0) { packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), - new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, - secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags, - pkgPrivateFlags, null /*usesStaticLibraries*/, - null /*usesStaticLibraryVersions*/, null /*mimeGroups*/); + legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, + cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags, + null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/, + null /*mimeGroups*/); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" + userId + " pkg=" + packageSetting); @@ -3852,8 +3819,8 @@ public final class Settings { } } else if (sharedIdStr != null) { if (sharedUserId > 0) { - packageSetting = new PackageSetting(name.intern(), realName, new File( - codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr, + packageSetting = new PackageSetting(name.intern(), realName, + new File(codePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, versionCode, pkgFlags, pkgPrivateFlags, sharedUserId, null /*usesStaticLibraries*/, @@ -4657,9 +4624,9 @@ public final class Settings { pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.sharedUser); } pw.print(prefix); pw.print(" pkg="); pw.println(pkg); - pw.print(prefix); pw.print(" codePath="); pw.println(ps.codePathString); + pw.print(prefix); pw.print(" codePath="); pw.println(ps.getCodePathString()); if (permissionNames == null) { - pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.resourcePathString); + pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.getCodePathString()); pw.print(prefix); pw.print(" legacyNativeLibraryDir="); pw.println(ps.legacyNativeLibraryPathString); pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.primaryCpuAbiString); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 89bdb3ecbff9..f9bf54a11df0 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -322,9 +322,6 @@ public class StagingManager { } final long activeVersion = activePackage.applicationInfo.longVersionCode; if (activeVersion != session.params.requiredInstalledVersionCode) { - if (!mApexManager.abortStagedSession(session.sessionId)) { - Slog.e(TAG, "Failed to abort apex session " + session.sessionId); - } throw new PackageManagerException( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "Installed version of APEX package " + activePackage.packageName @@ -338,14 +335,11 @@ public class StagingManager { throws PackageManagerException { final long activeVersion = activePackage.applicationInfo.longVersionCode; final long newVersionCode = newPackage.applicationInfo.longVersionCode; - boolean isAppDebuggable = (activePackage.applicationInfo.flags + final boolean isAppDebuggable = (activePackage.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted( session.params.installFlags, isAppDebuggable); if (activeVersion > newVersionCode && !allowsDowngrade) { - if (!mApexManager.abortStagedSession(session.sessionId)) { - Slog.e(TAG, "Failed to abort apex session " + session.sessionId); - } throw new PackageManagerException( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "Downgrade of APEX package " + newPackage.packageName @@ -835,37 +829,6 @@ public class StagingManager { return null; } - private void verifyApksInSession(PackageInstallerSession session) - throws PackageManagerException { - - final PackageInstallerSession apksToVerify = extractApksInSession( - session, /* preReboot */ true); - if (apksToVerify == null) { - return; - } - - final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( - (Intent result) -> { - int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - final String errorMessage = result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE); - Slog.e(TAG, "Failure to verify APK staged session " - + session.sessionId + " [" + errorMessage + "]"); - session.setStagedSessionFailed( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage); - mPreRebootVerificationHandler.onPreRebootVerificationComplete( - session.sessionId); - return; - } - mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( - session.sessionId); - }); - - apksToVerify.commit(receiver.getIntentSender(), false); - } - private void installApksInSession(@NonNull PackageInstallerSession session) throws PackageManagerException { @@ -908,10 +871,21 @@ public class StagingManager { mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId); } - private int parentOrOwnSessionId(PackageInstallerSession session) { + private int getSessionIdForParentOrSelf(PackageInstallerSession session) { return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId; } + private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) { + return session.hasParentSessionId() + ? getStagedSession(session.getParentSessionId()) + : session; + } + + private boolean isRollback(PackageInstallerSession session) { + final PackageInstallerSession root = getParentSessionOrSelf(session); + return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK; + } + /** * <p> Check if the session provided is non-overlapping with the active staged sessions. * @@ -937,6 +911,8 @@ public class StagingManager { boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService( Context.STORAGE_SERVICE)).isCheckpointSupported(); + final boolean isRollback = isRollback(session); + synchronized (mStagedSessions) { for (int i = 0; i < mStagedSessions.size(); i++) { final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i); @@ -951,8 +927,8 @@ public class StagingManager { } // Check if stagedSession has an active parent session or not if (stagedSession.hasParentSessionId()) { - int parentId = stagedSession.getParentSessionId(); - PackageInstallerSession parentSession = mStagedSessions.get(parentId); + final int parentId = stagedSession.getParentSessionId(); + final PackageInstallerSession parentSession = mStagedSessions.get(parentId); if (parentSession == null || parentSession.isStagedAndInTerminalState() || parentSession.isDestroyed()) { // Parent session has been abandoned or terminated already @@ -968,21 +944,37 @@ public class StagingManager { continue; } - // If session is not among the active sessions, then it cannot have same package - // name as any of the active sessions. + // New session cannot have same package name as one of the active sessions if (session.getPackageName().equals(stagedSession.getPackageName())) { - throw new PackageManagerException( - PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, - "Package: " + session.getPackageName() + " in session: " - + session.sessionId + " has been staged already by session: " - + stagedSession.sessionId, null); + if (isRollback) { + // If the new session is a rollback, then it gets priority. The existing + // session is failed to unblock rollback. + final PackageInstallerSession root = getParentSessionOrSelf(stagedSession); + if (!ensureActiveApexSessionIsAborted(root)) { + Slog.e(TAG, "Failed to abort apex session " + root.sessionId); + // Safe to ignore active apex session abort failure since session + // will be marked failed on next step and staging directory for session + // will be deleted. + } + root.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_OTHER_ERROR, + "Session was blocking rollback session: " + session.sessionId); + Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to " + + "blocking rollback session: " + session.sessionId); + } else { + throw new PackageManagerException( + PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, + "Package: " + session.getPackageName() + " in session: " + + session.sessionId + " has been staged already by session:" + + " " + stagedSession.sessionId, null); + } } // Staging multiple root sessions is not allowed if device doesn't support // checkpoint. If session and stagedSession do not have common ancestor, they are // from two different root sessions. - if (!supportsCheckpoint - && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) { + if (!supportsCheckpoint && getSessionIdForParentOrSelf(session) + != getSessionIdForParentOrSelf(stagedSession)) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, "Cannot stage multiple sessions without checkpoint support", null); @@ -1042,23 +1034,11 @@ public class StagingManager { // A session could be marked ready once its pre-reboot verification ends if (session.isStagedSessionReady()) { - if (sessionContainsApex(session)) { - try { - ApexSessionInfo apexSession = - mApexManager.getStagedSessionInfo(session.sessionId); - if (apexSession == null || isApexSessionFinalized(apexSession)) { - Slog.w(TAG, - "Cannot abort session " + session.sessionId - + " because it is not active."); - } else { - mApexManager.abortStagedSession(session.sessionId); - } - } catch (Exception e) { - // Failed to contact apexd service. The apex might still be staged. We can still - // safely cleanup the staged session since pre-reboot verification is complete. - // Also, cleaning up the stageDir prevents the apex from being activated. - Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId); - } + if (!ensureActiveApexSessionIsAborted(session)) { + // Failed to ensure apex session is aborted, so it can still be staged. We can still + // safely cleanup the staged session since pre-reboot verification is complete. + // Also, cleaning up the stageDir prevents the apex from being activated. + Slog.e(TAG, "Failed to abort apex session " + session.sessionId); } } @@ -1068,6 +1048,22 @@ public class StagingManager { return true; } + /** + * Ensure that there is no active apex session staged in apexd for the given session. + * + * @return returns true if it is ensured that there is no active apex session, otherwise false + */ + private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) { + if (!sessionContainsApex(session)) { + return true; + } + final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId); + if (apexSession == null || isApexSessionFinalized(apexSession)) { + return true; + } + return mApexManager.abortStagedSession(session.sessionId); + } + private boolean isApexSessionFinalized(ApexSessionInfo session) { /* checking if the session is in a final state, i.e., not active anymore */ return session.isUnknown || session.isActivationFailed || session.isSuccess @@ -1294,8 +1290,8 @@ public class StagingManager { + sessionId); return; } - if (session.isDestroyed()) { - // No point in running verification on a destroyed session + if (session.isDestroyed() || session.isStagedSessionFailed()) { + // No point in running verification on a destroyed/failed session onPreRebootVerificationComplete(sessionId); return; } @@ -1348,6 +1344,17 @@ public class StagingManager { obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget(); } + private void onPreRebootVerificationFailure(PackageInstallerSession session, + @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) { + if (!ensureActiveApexSessionIsAborted(session)) { + Slog.e(TAG, "Failed to abort apex session " + session.sessionId); + // Safe to ignore active apex session abortion failure since session will be marked + // failed on next step and staging directory for session will be deleted. + } + session.setStagedSessionFailed(errorCode, errorMessage); + onPreRebootVerificationComplete(session.sessionId); + } + // 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 @@ -1432,8 +1439,7 @@ public class StagingManager { validateApexSignature(apexPackages.get(i)); } } catch (PackageManagerException e) { - session.setStagedSessionFailed(e.error, e.getMessage()); - onPreRebootVerificationComplete(session.sessionId); + onPreRebootVerificationFailure(session, e.error, e.getMessage()); return; } @@ -1460,16 +1466,42 @@ public class StagingManager { try { Slog.d(TAG, "Running a pre-reboot verification for APKs in session " + session.sessionId + " by performing a dry-run install"); - // verifyApksInSession will notify the handler when APK verification is complete verifyApksInSession(session); - // TODO(b/118865310): abort the session on apexd. } catch (PackageManagerException e) { - session.setStagedSessionFailed(e.error, e.getMessage()); - onPreRebootVerificationComplete(session.sessionId); + onPreRebootVerificationFailure(session, e.error, e.getMessage()); } } + private void verifyApksInSession(PackageInstallerSession session) + throws PackageManagerException { + + final PackageInstallerSession apksToVerify = extractApksInSession( + session, /* preReboot */ true); + if (apksToVerify == null) { + return; + } + + final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( + (Intent result) -> { + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + final String errorMessage = result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE); + Slog.e(TAG, "Failure to verify APK staged session " + + session.sessionId + " [" + errorMessage + "]"); + onPreRebootVerificationFailure(session, + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage); + return; + } + mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( + session.sessionId); + }); + + apksToVerify.commit(receiver.getIntentSender(), false); + } + /** * Pre-reboot verification state for wrapping up: * <p><ul> @@ -1487,9 +1519,8 @@ public class StagingManager { } catch (Exception e) { // Failed to get hold of StorageManager Slog.e(TAG, "Failed to get hold of StorageManager", e); - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, + onPreRebootVerificationFailure(session, SessionInfo.STAGED_SESSION_UNKNOWN, "Failed to get hold of StorageManager"); - onPreRebootVerificationComplete(session.sessionId); return; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 8f11fd529e60..e3bee7228c68 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3497,7 +3497,7 @@ public class UserManagerService extends IUserManager.Stub { } t.traceBegin("PM.onNewUserCreated-" + userId); - mPm.onNewUserCreated(userId); + mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false); t.traceEnd(); if (preCreate) { // Must start user (which will be stopped right away, through @@ -3570,10 +3570,7 @@ public class UserManagerService extends IUserManager.Stub { writeUserListLP(); } updateUserIds(); - if (!mPm.readPermissionStateForUser(preCreatedUser.id)) { - // Could not read the existing permissions, re-grant them. - mPm.onNewUserCreated(preCreatedUser.id); - } + mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true); dispatchUserAdded(preCreatedUser); return preCreatedUser; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9c0e2b290007..1b4fac6f407a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -70,7 +70,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.PERSIST_ACROSS_REBOOTS; import static android.content.pm.ActivityInfo.PERSIST_ROOT_ONLY; @@ -1683,7 +1683,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (options != null) { final boolean useLockTask = options.getLockTaskMode(); if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) { - lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; + lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; } } return lockTaskLaunchMode; diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index ed1ea353eb3e..2c475e0b9bcb 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -74,9 +74,9 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.Task.ActivityState.PAUSED; import static com.android.server.wm.Task.ActivityState.PAUSING; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; +import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; -import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.Task.TAG_CLEANUP; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; @@ -130,6 +130,7 @@ import android.util.MergedConfiguration; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -788,7 +789,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final LockTaskController lockTaskController = mService.getLockTaskController(); if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV - || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED + || (task.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED && lockTaskController.getLockTaskModeState() == LOCK_TASK_MODE_LOCKED)) { lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */); @@ -1090,9 +1091,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Check if caller is already present on display final boolean uidPresentOnDisplay = displayContent.isUidPresent(callingUid); - final int displayOwnerUid = displayContent.mDisplay.getOwnerUid(); - if (displayContent.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID) { - // Limit launching on virtual displays, because their contents can be read from Surface + final Display display = displayContent.mDisplay; + if (!display.isTrusted()) { + // Limit launching on untrusted displays because their contents can be read from Surface // by apps that created them. if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" @@ -1116,7 +1117,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } // Check if the caller is the owner of the display. - if (displayOwnerUid == callingUid) { + if (display.getOwnerUid() == callingUid) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" + " allow launch for owner of the display"); return true; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 777ddda89e9d..2dc22ecfc022 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -528,8 +528,8 @@ public abstract class ActivityTaskManagerInternal { public abstract void onActiveUidsCleared(); public abstract void onUidProcStateChanged(int uid, int procState); - public abstract void onUidAddedToPendingTempWhitelist(int uid, String tag); - public abstract void onUidRemovedFromPendingTempWhitelist(int uid); + public abstract void onUidAddedToPendingTempAllowlist(int uid, String tag); + public abstract void onUidRemovedFromPendingTempAllowlist(int uid); /** Handle app crash event in {@link android.app.IActivityController} if there is one. */ public abstract boolean handleAppCrashInActivityController(String processName, int pid, diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 3edb690fd906..627361d780a2 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -183,7 +183,6 @@ import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; -import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -238,12 +237,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ProcessMap; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.TransferPipe; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.KeyguardDismissCallback; import com.android.internal.util.ArrayUtils; @@ -384,7 +380,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private AppOpsManager mAppOpsManager; /** All active uids in the system. */ private final MirrorActiveUids mActiveUids = new MirrorActiveUids(); - private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>(); + private final SparseArray<String> mPendingTempAllowlist = new SparseArray<>(); /** All processes currently running that might have a window organized by name. */ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>(); /** All processes we currently have running mapped by pid and uid */ @@ -1110,7 +1106,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int startActivityIntentSender(IApplicationThread caller, IIntentSender target, - IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, + IBinder allowlistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) { enforceNotIsolatedCaller("startActivityIntentSender"); // Refuse possible leaked file descriptors @@ -1133,7 +1129,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mAppSwitchesAllowedTime = 0; } } - return pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null, + return pir.sendInner(0, fillInIntent, resolvedType, allowlistToken, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions); } @@ -3037,7 +3033,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // system or a specific app. // * System-initiated requests will only start the pinned mode (screen pinning) // * App-initiated requests - // - will put the device in fully locked mode (LockTask), if the app is whitelisted + // - will put the device in fully locked mode (LockTask), if the app is allowlisted // - will start the pinned mode, otherwise final int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); @@ -3077,7 +3073,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "updateLockTaskPackages()"); } synchronized (mGlobalLock) { - if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" + if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":" + Arrays.toString(packages)); getLockTaskController().updateLockTaskPackages(userId, packages); } @@ -4095,10 +4091,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final Task stack = r.getRootTask(); stack.setPictureInPictureAspectRatio(aspectRatio); stack.setPictureInPictureActions(actions); - MetricsLoggerWrapper.logPictureInPictureEnter(mContext, - r.info.applicationInfo.uid, r.shortComponentName, - r.supportsEnterPipOnTaskSwitch); - logPictureInPictureArgs(params); } }; @@ -4142,7 +4134,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.pictureInPictureArgs.getAspectRatio()); stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); } - logPictureInPictureArgs(params); } } finally { Binder.restoreCallingIdentity(origId); @@ -4156,18 +4147,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return 3; } - private void logPictureInPictureArgs(PictureInPictureParams params) { - if (params.hasSetActions()) { - MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count", - params.getActions().size()); - } - if (params.hasSetAspectRatio()) { - LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED); - lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio()); - MetricsLogger.action(lm); - } - } - /** * Checks the state of the system and the activity associated with the given {@param token} to * verify that picture-in-picture is supported for that activity. @@ -5979,11 +5958,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } /** - * @return whitelist tag for a uid from mPendingTempWhitelist, null if not currently on - * the whitelist + * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on + * the allowlist */ - String getPendingTempWhitelistTagForUidLocked(int uid) { - return mPendingTempWhitelist.get(uid); + String getPendingTempAllowlistTagForUidLocked(int uid) { + return mPendingTempAllowlist.get(uid); } void logAppTooSlow(WindowProcessController app, long startTime, String msg) { @@ -7325,16 +7304,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void onUidAddedToPendingTempWhitelist(int uid, String tag) { + public void onUidAddedToPendingTempAllowlist(int uid, String tag) { synchronized (mGlobalLockWithoutBoost) { - mPendingTempWhitelist.put(uid, tag); + mPendingTempAllowlist.put(uid, tag); } } @Override - public void onUidRemovedFromPendingTempWhitelist(int uid) { + public void onUidRemovedFromPendingTempAllowlist(int uid) { synchronized (mGlobalLockWithoutBoost) { - mPendingTempWhitelist.remove(uid); + mPendingTempAllowlist.remove(uid); } } diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 5e1cbc3158c1..e166bfc08ad4 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -10,6 +10,7 @@ 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.ON_POINTER_DOWN_OUTSIDE_FOCUS; +import android.annotation.Nullable; import android.os.Build; import android.os.Debug; import android.os.IBinder; @@ -173,23 +174,23 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal */ @Override public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, - String reason) { + @Nullable Integer pid, String reason) { final long startTime = SystemClock.uptimeMillis(); try { - return notifyANRInner(inputApplicationHandle, token, reason); + return notifyANRInner(inputApplicationHandle, token, pid, reason); } finally { // Log the time because the method is called from InputDispatcher thread. It shouldn't - // take too long that may affect input response time. + // take too long because it blocks input while executing. Slog.d(TAG_WM, "notifyANR took " + (SystemClock.uptimeMillis() - startTime) + "ms"); } } private long notifyANRInner(InputApplicationHandle inputApplicationHandle, IBinder token, - String reason) { + @Nullable Integer pid, String reason) { ActivityRecord activity = null; WindowState windowState = null; boolean aboveSystem = false; - int windowPid = INVALID_PID; + int windowPid = pid != null ? pid : INVALID_PID; preDumpIfLockTooSlow(); diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index c7a438d527ad..8ef57f726658 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -33,11 +33,11 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTAS import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.Task.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.wm.Task.LOCK_TASK_AUTH_PINNABLE; -import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -264,12 +264,12 @@ public class LockTaskController { } /** - * @return whether the requested task is allowed to be locked (either whitelisted, or declares + * @return whether the requested task is allowed to be locked (either allowlisted, or declares * lockTaskMode="always" in the manifest). */ - boolean isTaskWhitelisted(Task task) { + boolean isTaskAllowlisted(Task task) { switch(task.mLockTaskAuth) { - case LOCK_TASK_AUTH_WHITELISTED: + case LOCK_TASK_AUTH_ALLOWLISTED: case LOCK_TASK_AUTH_LAUNCHABLE: case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return true; @@ -311,7 +311,7 @@ public class LockTaskController { private boolean isLockTaskModeViolationInternal(Task task, boolean isNewClearTask) { // TODO: Double check what's going on here. If the task is already in lock task mode, it's - // likely whitelisted, so will return false below. + // likely allowlisted, so will return false below. if (isTaskLocked(task) && !isNewClearTask) { // If the task is already at the top and won't be cleared, then allow the operation return false; @@ -327,7 +327,7 @@ public class LockTaskController { return false; } - return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty()); + return !(isTaskAllowlisted(task) || mLockTaskModeTasks.isEmpty()); } private boolean isRecentsAllowed(int userId) { @@ -356,7 +356,7 @@ public class LockTaskController { return false; default: } - return isPackageWhitelisted(userId, packageName); + return isPackageAllowlisted(userId, packageName); } private boolean isEmergencyCallTask(Task task) { @@ -556,7 +556,7 @@ public class LockTaskController { if (!isSystemCaller) { task.mLockTaskUid = callingUid; if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) { - // startLockTask() called by app, but app is not part of lock task whitelist. Show + // startLockTask() called by app, but app is not part of lock task allowlist. Show // app pinning request. We will come back here with isSystemCaller true. if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user"); StatusBarManagerInternal statusBarManager = LocalServices.getService( @@ -649,8 +649,8 @@ public class LockTaskController { /** * Update packages that are allowed to be launched in lock task mode. - * @param userId Which user this whitelist is associated with - * @param packages The whitelist of packages allowed in lock task mode + * @param userId Which user this allowlist is associated with + * @param packages The allowlist of packages allowed in lock task mode * @see #mLockTaskPackages */ void updateLockTaskPackages(int userId, String[] packages) { @@ -659,19 +659,19 @@ public class LockTaskController { boolean taskChanged = false; for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) { final Task lockedTask = mLockTaskModeTasks.get(taskNdx); - final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE - || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + final boolean wasAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED; lockedTask.setLockTaskAuth(); - final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE - || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + final boolean isAllowlisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_ALLOWLISTED; if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED || lockedTask.mUserId != userId - || !wasWhitelisted || isWhitelisted) { + || !wasAllowlisted || isAllowlisted) { continue; } - // Terminate locked tasks that have recently lost whitelist authorization. + // Terminate locked tasks that have recently lost allowlist authorization. if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); removeLockedTask(lockedTask); @@ -697,17 +697,17 @@ public class LockTaskController { } } - boolean isPackageWhitelisted(int userId, String pkg) { + boolean isPackageAllowlisted(int userId, String pkg) { if (pkg == null) { return false; } - String[] whitelist; - whitelist = mLockTaskPackages.get(userId); - if (whitelist == null) { + String[] allowlist; + allowlist = mLockTaskPackages.get(userId); + if (allowlist == null) { return false; } - for (String whitelistedPkg : whitelist) { - if (pkg.equals(whitelistedPkg)) { + for (String allowlistedPkg : allowlist) { + if (pkg.equals(allowlistedPkg)) { return true; } } diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java index 0f92bc83a666..61b6e0b25961 100644 --- a/services/core/java/com/android/server/wm/PolicyControl.java +++ b/services/core/java/com/android/server/wm/PolicyControl.java @@ -196,40 +196,40 @@ class PolicyControl { private static final String ALL = "*"; private static final String APPS = "apps"; - private final ArraySet<String> mWhitelist; - private final ArraySet<String> mBlacklist; + private final ArraySet<String> mAllowlist; + private final ArraySet<String> mDenylist; - private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) { - mWhitelist = whitelist; - mBlacklist = blacklist; + private Filter(ArraySet<String> allowlist, ArraySet<String> denylist) { + mAllowlist = allowlist; + mDenylist = denylist; } boolean matches(LayoutParams attrs) { if (attrs == null) return false; boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; - if (isApp && mBlacklist.contains(APPS)) return false; - if (onBlacklist(attrs.packageName)) return false; - if (isApp && mWhitelist.contains(APPS)) return true; - return onWhitelist(attrs.packageName); + if (isApp && mDenylist.contains(APPS)) return false; + if (onDenylist(attrs.packageName)) return false; + if (isApp && mAllowlist.contains(APPS)) return true; + return onAllowlist(attrs.packageName); } boolean matches(String packageName) { - return !onBlacklist(packageName) && onWhitelist(packageName); + return !onDenylist(packageName) && onAllowlist(packageName); } - private boolean onBlacklist(String packageName) { - return mBlacklist.contains(packageName) || mBlacklist.contains(ALL); + private boolean onDenylist(String packageName) { + return mDenylist.contains(packageName) || mDenylist.contains(ALL); } - private boolean onWhitelist(String packageName) { - return mWhitelist.contains(ALL) || mWhitelist.contains(packageName); + private boolean onAllowlist(String packageName) { + return mAllowlist.contains(ALL) || mAllowlist.contains(packageName); } void dump(PrintWriter pw) { pw.print("Filter["); - dump("whitelist", mWhitelist, pw); pw.print(','); - dump("blacklist", mBlacklist, pw); pw.print(']'); + dump("allowlist", mAllowlist, pw); pw.print(','); + dump("denylist", mDenylist, pw); pw.print(']'); } private void dump(String name, ArraySet<String> set, PrintWriter pw) { @@ -253,18 +253,18 @@ class PolicyControl { // e.g. "com.package1", or "apps, com.android.keyguard" or "*" static Filter parse(String value) { if (value == null) return null; - ArraySet<String> whitelist = new ArraySet<String>(); - ArraySet<String> blacklist = new ArraySet<String>(); + ArraySet<String> allowlist = new ArraySet<String>(); + ArraySet<String> denylist = new ArraySet<String>(); for (String token : value.split(",")) { token = token.trim(); if (token.startsWith("-") && token.length() > 1) { token = token.substring(1); - blacklist.add(token); + denylist.add(token); } else { - whitelist.add(token); + allowlist.add(token); } } - return new Filter(whitelist, blacklist); + return new Filter(allowlist, denylist); } } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index ba2c0b6dc0ac..df5356303f8b 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -655,7 +655,8 @@ class RecentTasks { } for (int i = mTasks.size() - 1; i >= 0; --i) { final Task task = mTasks.get(i); - if (task.mUserId == userId && !mService.getLockTaskController().isTaskWhitelisted(task)) { + if (task.mUserId == userId + && !mService.getLockTaskController().isTaskAllowlisted(task)) { remove(task); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ac96d144c4d0..1cb483c1d1a0 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2571,7 +2571,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mDisplayAccessUIDs.clear(); for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent displayContent = getChildAt(displayNdx); - // Only bother calculating the whitelist for private displays + // Only bother calculating the allowlist for private displays if (displayContent.isPrivate()) { mDisplayAccessUIDs.append( displayContent.mDisplayId, displayContent.getPresentUIDs()); diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index b71ecbb8a72d..ede6708d5f8f 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -233,10 +233,10 @@ public class SafeActivityOptions { Slog.w(TAG, msg); throw new SecurityException(msg); } - // Check if someone tries to launch an unwhitelisted activity into LockTask mode. + // Check if someone tries to launch an unallowlisted activity into LockTask mode. final boolean lockTaskMode = options.getLockTaskMode(); if (aInfo != null && lockTaskMode - && !supervisor.mService.getLockTaskController().isPackageWhitelisted( + && !supervisor.mService.getLockTaskController().isPackageAllowlisted( UserHandle.getUserId(callingUid), aInfo.packageName)) { final String msg = "Permission Denial: starting " + getIntentString(intent) + " from " + callerApp + " (pid=" + callingPid diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f3620050bed2..be0815b06051 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -45,7 +45,7 @@ import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; -import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; @@ -210,7 +210,6 @@ import android.window.ITaskOrganizer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledFunction; @@ -411,7 +410,7 @@ class Task extends WindowContainer<WindowContainer> { /** Starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing lockTask task. */ final static int LOCK_TASK_AUTH_LAUNCHABLE = 2; /** Can enter lockTask without user approval. Can start over existing lockTask task. */ - final static int LOCK_TASK_AUTH_WHITELISTED = 3; + final static int LOCK_TASK_AUTH_ALLOWLISTED = 3; /** Priv-app that starts in LOCK_TASK_MODE_LOCKED automatically. Can start over existing * lockTask task. */ final static int LOCK_TASK_AUTH_LAUNCHABLE_PRIV = 4; @@ -1686,7 +1685,7 @@ class Task extends WindowContainer<WindowContainer> { getDisplayArea().addStackReferenceIfNeeded((Task) child); } - // Make sure the list of display UID whitelists is updated + // Make sure the list of display UID allowlists is updated // now that this record is in a new task. mRootWindowContainer.updateUIDsPresentOnDisplay(); @@ -1903,7 +1902,7 @@ class Task extends WindowContainer<WindowContainer> { case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK"; case LOCK_TASK_AUTH_PINNABLE: return "LOCK_TASK_AUTH_PINNABLE"; case LOCK_TASK_AUTH_LAUNCHABLE: return "LOCK_TASK_AUTH_LAUNCHABLE"; - case LOCK_TASK_AUTH_WHITELISTED: return "LOCK_TASK_AUTH_WHITELISTED"; + case LOCK_TASK_AUTH_ALLOWLISTED: return "LOCK_TASK_AUTH_ALLOWLISTED"; case LOCK_TASK_AUTH_LAUNCHABLE_PRIV: return "LOCK_TASK_AUTH_LAUNCHABLE_PRIV"; default: return "unknown=" + mLockTaskAuth; } @@ -1923,8 +1922,8 @@ class Task extends WindowContainer<WindowContainer> { final LockTaskController lockTaskController = mAtmService.getLockTaskController(); switch (r.lockTaskLaunchMode) { case LOCK_TASK_LAUNCH_MODE_DEFAULT: - mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg) - ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; + mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg) + ? LOCK_TASK_AUTH_ALLOWLISTED : LOCK_TASK_AUTH_PINNABLE; break; case LOCK_TASK_LAUNCH_MODE_NEVER: @@ -1935,8 +1934,8 @@ class Task extends WindowContainer<WindowContainer> { mLockTaskAuth = LOCK_TASK_AUTH_LAUNCHABLE_PRIV; break; - case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - mLockTaskAuth = lockTaskController.isPackageWhitelisted(mUserId, pkg) + case LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED: + mLockTaskAuth = lockTaskController.isPackageAllowlisted(mUserId, pkg) ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; break; } @@ -2366,7 +2365,6 @@ class Task extends WindowContainer<WindowContainer> { private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) { if (mWmService.mDisableTransitionAnimation || !isVisible() - || getDisplayContent().mAppTransition.isTransitionSet() || getSurfaceControl() == null || !isLeafTask()) { return false; @@ -7372,8 +7370,6 @@ class Task extends WindowContainer<WindowContainer> { getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */); mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this); - MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext, - task.effectiveUid, task.realActivity.flattenToString()); }); } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 0e1b2f25c7af..4b5f38c40e4f 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -99,6 +99,7 @@ cc_defaults { "libpowermanager", "libutils", "libui", + "libvibratorservice", "libinput", "libinputflinger", "libinputflinger_base", diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index 05aa3594eb68..74e2328105dc 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -33,6 +33,8 @@ #include <inttypes.h> #include <stdio.h> +#include <vibratorservice/VibratorHalController.h> + using android::hardware::Return; using android::hardware::Void; using android::hardware::vibrator::V1_0::EffectStrength; @@ -226,8 +228,24 @@ bool isValidEffect(jlong effect) { return val >= *iter.begin() && val <= *std::prev(iter.end()); } -static void vibratorInit(JNIEnv *env, jclass clazz) -{ +static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) { + aidl::CompositeEffect effect; + effect.primitive = static_cast<aidl::CompositePrimitive>( + env->GetIntField(primitive, gPrimitiveClassInfo.id)); + effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale)); + effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, gPrimitiveClassInfo.delay)); + return effect; +} + +static void destroyVibratorController(void* rawVibratorController) { + vibrator::HalController* vibratorController = + reinterpret_cast<vibrator::HalController*>(rawVibratorController); + if (vibratorController) { + delete vibratorController; + } +} + +static jlong vibratorInit(JNIEnv* /* env */, jclass /* clazz */) { if (auto hal = getHal<aidl::IVibrator>()) { // IBinder::pingBinder isn't accessible as a pointer function // but getCapabilities can serve the same purpose @@ -236,25 +254,26 @@ static void vibratorInit(JNIEnv *env, jclass clazz) } else { halCall(&V1_0::IVibrator::ping).isOk(); } + std::unique_ptr<vibrator::HalController> controller = + std::make_unique<vibrator::HalController>(); + controller->init(); + return reinterpret_cast<jlong>(controller.release()); } -static jboolean vibratorExists(JNIEnv* /* env */, jclass /* clazz */) -{ - bool ok; +static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyVibratorController)); +} - if (auto hal = getHal<aidl::IVibrator>()) { - // IBinder::pingBinder isn't accessible as a pointer function - // but getCapabilities can serve the same purpose - int32_t cap; - ok = hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk(); - } else { - ok = halCall(&V1_0::IVibrator::ping).isOk(); +static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorExists failed because controller was not initialized"); + return JNI_FALSE; } - return ok ? JNI_TRUE : JNI_FALSE; + return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE; } -static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) -{ +static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) { if (auto hal = getHal<aidl::IVibrator>()) { auto status = hal->call(&aidl::IVibrator::on, timeout_ms, nullptr); if (!status.isOk()) { @@ -268,93 +287,53 @@ static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) } } -static void vibratorOff(JNIEnv* /* env */, jclass /* clazz */) -{ - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::off); - if (!status.isOk()) { - ALOGE("vibratorOff command failed: %s", status.toString8().string()); - } - } else { - Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR); - if (retStatus != Status::OK) { - ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); - } - } -} - -static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jclass) { - if (auto hal = getHal<aidl::IVibrator>()) { - int32_t cap = 0; - if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { - return false; - } - return (cap & aidl::IVibrator::CAP_AMPLITUDE_CONTROL) > 0; - } else { - return halCall(&V1_0::IVibrator::supportsAmplitudeControl).withDefault(false); +static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorOff failed because controller was not initialized"); + return; } + controller->off(); } -static void vibratorSetAmplitude(JNIEnv*, jclass, jint amplitude) { - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::IVibrator::setAmplitude, static_cast<float>(amplitude) / UINT8_MAX); - if (!status.isOk()) { - ALOGE("Failed to set vibrator amplitude: %s", status.toString8().string()); - } - } else { - Status status = halCall(&V1_0::IVibrator::setAmplitude, static_cast<uint32_t>(amplitude)) - .withDefault(Status::UNKNOWN_ERROR); - if (status != Status::OK) { - ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").", - static_cast<uint32_t>(status)); - } +static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, + jint amplitude) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorSetAmplitude failed because controller was not initialized"); + return; } + controller->setAmplitude(static_cast<int32_t>(amplitude)); } -static jboolean vibratorSupportsExternalControl(JNIEnv*, jclass) { - if (auto hal = getHal<aidl::IVibrator>()) { - int32_t cap = 0; - if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { - return false; - } - return (cap & aidl::IVibrator::CAP_EXTERNAL_CONTROL) > 0; - } else { - return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false); +static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, + jboolean enabled) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorSetExternalControl failed because controller was not initialized"); + return; } + controller->setExternalControl(enabled); } -static void vibratorSetExternalControl(JNIEnv*, jclass, jboolean enabled) { - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::IVibrator::setExternalControl, enabled); - if (!status.isOk()) { - ALOGE("Failed to set vibrator external control: %s", status.toString8().string()); - } - } else { - Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled)) - .withDefault(Status::UNKNOWN_ERROR); - if (status != Status::OK) { - ALOGE("Failed to set vibrator external control (%" PRIu32 ").", - static_cast<uint32_t>(status)); - } +static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorGetSupportedEffects failed because controller was not initialized"); + return nullptr; } -} - -static jintArray vibratorGetSupportedEffects(JNIEnv *env, jclass) { - if (auto hal = getHal<aidl::IVibrator>()) { - std::vector<aidl::Effect> supportedEffects; - if (!hal->call(&aidl::IVibrator::getSupportedEffects, &supportedEffects).isOk()) { - return nullptr; - } - jintArray arr = env->NewIntArray(supportedEffects.size()); - env->SetIntArrayRegion(arr, 0, supportedEffects.size(), - reinterpret_cast<jint*>(supportedEffects.data())); - return arr; - } else { + auto result = controller->getSupportedEffects(); + if (!result.isOk()) { return nullptr; } + std::vector<aidl::Effect> supportedEffects = result.value(); + jintArray effects = env->NewIntArray(supportedEffects.size()); + env->SetIntArrayRegion(effects, 0, supportedEffects.size(), + reinterpret_cast<jint*>(supportedEffects.data())); + return effects; } -static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong strength, +static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong effect, jlong strength, jobject vibration, jboolean withCallback) { if (auto hal = getHal<aidl::IVibrator>()) { int32_t lengthMs; @@ -420,17 +399,8 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass, jlong effect, jlong stre return -1; } -static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) { - aidl::CompositeEffect effect; - effect.primitive = static_cast<aidl::CompositePrimitive>( - env->GetIntField(primitive, gPrimitiveClassInfo.id)); - effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale)); - effect.delayMs = static_cast<int>(env->GetIntField(primitive, gPrimitiveClassInfo.delay)); - return effect; -} - -static void vibratorPerformComposedEffect(JNIEnv* env, jclass, jobjectArray composition, - jobject vibration) { +static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jobjectArray composition, + jobject vibration) { auto hal = getHal<aidl::IVibrator>(); if (!hal) { return; @@ -451,65 +421,71 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass, jobjectArray comp } } -static jlong vibratorGetCapabilities(JNIEnv*, jclass) { - if (auto hal = getHal<aidl::IVibrator>()) { - int32_t cap = 0; - if (!hal->call(&aidl::IVibrator::getCapabilities, &cap).isOk()) { - return 0; - } - return cap; +static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorGetCapabilities failed because controller was not initialized"); + return 0; } - - return 0; + auto result = controller->getCapabilities(); + return result.isOk() ? static_cast<jlong>(result.value()) : 0; } -static void vibratorAlwaysOnEnable(JNIEnv* env, jclass, jlong id, jlong effect, jlong strength) { - auto status = halCall(&aidl::IVibrator::alwaysOnEnable, id, - static_cast<aidl::Effect>(effect), static_cast<aidl::EffectStrength>(strength)); - if (!status.isOk()) { - ALOGE("vibratortAlwaysOnEnable command failed (%s).", status.toString8().string()); +static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong id, + jlong effect, jlong strength) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorAlwaysOnEnable failed because controller was not initialized"); + return; } + controller->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect), + static_cast<aidl::EffectStrength>(strength)); } -static void vibratorAlwaysOnDisable(JNIEnv* env, jclass, jlong id) { - auto status = halCall(&aidl::IVibrator::alwaysOnDisable, id); - if (!status.isOk()) { - ALOGE("vibratorAlwaysOnDisable command failed (%s).", status.toString8().string()); +static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, + jlong id) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorAlwaysOnDisable failed because controller was not initialized"); + return; } + controller->alwaysOnDisable(static_cast<int32_t>(id)); } static const JNINativeMethod method_table[] = { - {"vibratorExists", "()Z", (void*)vibratorExists}, - {"vibratorInit", "()V", (void*)vibratorInit}, + {"vibratorInit", "()J", (void*)vibratorInit}, + {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer}, + {"vibratorExists", "(J)Z", (void*)vibratorExists}, {"vibratorOn", "(J)V", (void*)vibratorOn}, - {"vibratorOff", "()V", (void*)vibratorOff}, - {"vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl}, - {"vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude}, + {"vibratorOff", "(J)V", (void*)vibratorOff}, + {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, {"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J", (void*)vibratorPerformEffect}, {"vibratorPerformComposedEffect", "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/" "VibratorService$Vibration;)V", (void*)vibratorPerformComposedEffect}, - {"vibratorGetSupportedEffects", "()[I", (void*)vibratorGetSupportedEffects}, - {"vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl}, - {"vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl}, - {"vibratorGetCapabilities", "()J", (void*)vibratorGetCapabilities}, - {"vibratorAlwaysOnEnable", "(JJJ)V", (void*)vibratorAlwaysOnEnable}, - {"vibratorAlwaysOnDisable", "(J)V", (void*)vibratorAlwaysOnDisable}, + {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, + {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl}, + {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities}, + {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable}, + {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, }; int register_android_server_VibratorService(JNIEnv *env) { - sMethodIdOnComplete = GetMethodIDOrDie(env, - FindClassOrDie(env, "com/android/server/VibratorService$Vibration"), - "onComplete", "()V"); - jclass primitiveClass = FindClassOrDie(env, - "android/os/VibrationEffect$Composition$PrimitiveEffect"); + sMethodIdOnComplete = + GetMethodIDOrDie(env, + FindClassOrDie(env, "com/android/server/VibratorService$Vibration"), + "onComplete", "()V"); + + jclass primitiveClass = + FindClassOrDie(env, "android/os/VibrationEffect$Composition$PrimitiveEffect"); gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I"); gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F"); gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I"); - return jniRegisterNativeMethods(env, "com/android/server/VibratorService", - method_table, NELEM(method_table)); + + return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table, + NELEM(method_table)); } -}; +}; // namespace android diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 784366318319..0202c88009fd 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -710,9 +710,8 @@ std::chrono::nanoseconds NativeInputManager::notifyAnr( jobject tokenObj = javaObjectForIBinder(env, token); jstring reasonObj = env->NewStringUTF(reason.c_str()); - jlong newTimeout = env->CallLongMethod(mServiceObj, - gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj, - reasonObj); + jlong newTimeout = env->CallLongMethod(mServiceObj, gServiceClassInfo.notifyANR, + inputApplicationHandleObj, tokenObj, reasonObj); if (checkAndClearExceptionFromCallback(env, "notifyANR")) { newTimeout = 0; // abort dispatch } else { diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index ecdb30f5e84b..09552082e4af 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -33,7 +33,6 @@ import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.parsing.pkg.ParsedPackage import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType -import com.android.server.pm.test.override.R import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.mockThrowOnUnmocked @@ -266,7 +265,7 @@ class PackageManagerComponentLabelIconOverrideTest { .hideAsFinal() private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"), - File("/test"), null, null, null, null, 0, 0, 0, 0, null, null, null)) { + null, null, null, null, 0, 0, 0, 0, null, null, null)) { this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp } diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index 41dfade1a09a..cffcdd8f94bd 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -25,17 +25,28 @@ java_test_host { ], test_suites: ["general-tests"], java_resources: [ - ":PackageManagerDummyAppVersion1", - ":PackageManagerDummyAppVersion2", - ":PackageManagerDummyAppVersion3", - ":PackageManagerDummyAppVersion4", - ":PackageManagerDummyAppOriginalOverride", - ":PackageManagerServiceHostTestsResources", - ] + ":PackageManagerTestAppStub", + ":PackageManagerTestAppVersion1", + ":PackageManagerTestAppVersion2", + ":PackageManagerTestAppVersion3", + ":PackageManagerTestAppVersion3Invalid", + ":PackageManagerTestAppVersion4", + ":PackageManagerTestAppOriginalOverride", + ], } -filegroup { - name: "PackageManagerServiceHostTestsResources", - srcs: [ "resources/*" ], - path: "resources/" +genrule { + name: "PackageManagerTestAppVersion3Invalid", + tools: [ + "soong_zip", + "zipalign", + ], + srcs: [ + ":PackageManagerTestAppVersion3", + ], + out: ["PackageManagerTestAppVersion3Invalid.apk"], + cmd: "mkdir -p $(genDir)/apk && unzip $(in) -d $(genDir)/apk" + + " && truncate -s 800 $(genDir)/apk/META-INF/CERT.RSA" + + " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" + + " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)", } diff --git a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk b/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk Binary files differdeleted file mode 100644 index 127886cf8e9e..000000000000 --- a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk +++ /dev/null diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt index 234fcf19062d..8dfefaf9750f 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt @@ -21,8 +21,9 @@ import com.android.tradefed.device.ITestDevice import java.io.File import java.io.FileOutputStream -internal fun SystemPreparer.pushApk(file: String, partition: Partition) = - pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString()) +internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) = + pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition) + .toString()) internal fun SystemPreparer.deleteApkFolders( partition: Partition, @@ -58,4 +59,55 @@ internal object HostUtils { } return file } + + /** + * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot, + * so the following methods parse the package dump directly to see if the path matches. + */ + fun getCodePaths(device: ITestDevice, pkgName: String) = + device.executeShellCommand("pm dump $pkgName") + .lineSequence() + .map(String::trim) + .filter { it.startsWith("codePath=") } + .map { it.removePrefix("codePath=") } + .toList() + + private fun userIdLineSequence(device: ITestDevice, pkgName: String) = + device.executeShellCommand("pm dump $pkgName") + .lineSequence() + .dropWhile { !it.startsWith("Packages:") } + .takeWhile { + !it.startsWith("Hidden system packages:") && + !it.startsWith("Queries:") + } + .map(String::trim) + .filter { it.startsWith("User ") } + + fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) = + userIdLineSequence(device, pkgName).associate { + val userId = it.removePrefix("User ") + .takeWhile(Char::isDigit) + .toInt() + val enabled = it.substringAfter("enabled=") + .takeWhile(Char::isDigit) + .toInt() + .let { + when (it) { + 0, 1 -> true + else -> false + } + } + userId to enabled + } + + fun getUserIdToPkgInstalledState(device: ITestDevice, pkgName: String) = + userIdLineSequence(device, pkgName).associate { + val userId = it.removePrefix("User ") + .takeWhile(Char::isDigit) + .toInt() + val installed = it.substringAfter("installed=") + .takeWhile { !it.isWhitespace() } + .toBoolean() + userId to installed + } } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt index 39b40d8d3195..b7d135991ccd 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt @@ -33,11 +33,11 @@ import org.junit.runner.RunWith class InvalidNewSystemAppTest : BaseHostJUnit4Test() { companion object { - private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app" - private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk" - private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk" - private const val VERSION_THREE_INVALID = "PackageManagerDummyAppVersion3Invalid.apk" - private const val VERSION_FOUR = "PackageManagerDummyAppVersion4.apk" + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" + private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk" + private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk" @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) @@ -49,14 +49,14 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() { @get:Rule val rules = RuleChain.outerRule(tempFolder).around(preparer)!! - private val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT) + private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT) @Before @After fun removeApk() { device.uninstallPackage(TEST_PKG_NAME) - device.deleteFile(filePath.parent.toString()) - device.reboot() + preparer.deleteFile(filePath.parent.toString()) + .reboot() } @Test diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt index fb0348c3146b..4ae3ca5f7263 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt @@ -33,11 +33,11 @@ import org.junit.runner.RunWith class OriginalPackageMigrationTest : BaseHostJUnit4Test() { companion object { - private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app" - private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk" - private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk" - private const val VERSION_THREE = "PackageManagerDummyAppVersion3.apk" - private const val NEW_PKG = "PackageManagerDummyAppOriginalOverride.apk" + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" + private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk" + private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk" @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) @@ -55,6 +55,7 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { fun deleteApkFolders() { preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE, NEW_PKG) + .reboot() } @Test @@ -99,9 +100,7 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { } private fun assertCodePath(apk: String) { - // dumpsys package and therefore device.getAppPackageInfo doesn't work here for some reason, - // so parse the package dump directly to see if the path matches. - assertThat(device.executeShellCommand("pm dump $TEST_PKG_NAME")) - .contains(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString()) + assertThat(HostUtils.getCodePaths(device, TEST_PKG_NAME)) + .containsExactly(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString()) } } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt new file mode 100644 index 000000000000..bc478b0e2c90 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt @@ -0,0 +1,650 @@ +/* + * 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.test + +import com.android.internal.util.test.SystemPreparer +import com.android.tradefed.device.ITestDevice +import com.android.tradefed.device.UserInfo +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth.assertThat +import org.junit.AfterClass +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import java.io.File +import java.util.zip.GZIPOutputStream + +@RunWith(DeviceJUnit4ClassRunner::class) +class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() { + + companion object { + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_STUB = "PackageManagerTestAppStub.apk" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + + /** + * How many total users on device to test, including primary. This will clean up any + * users created specifically for this test. + */ + private const val USER_COUNT = 3 + + /** + * Whether to manually reset state at each test method without rebooting + * for faster iterative development. + */ + private const val DEBUG_NO_REBOOT = false + + @get:ClassRule + val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) + + private val parentClassName = SystemStubMultiUserDisableUninstallTest::class.java.simpleName + + private val deviceCompressedFile = + HostUtils.makePathForApk("$parentClassName.apk", Partition.PRODUCT).parent + .resolve("$parentClassName.apk.gz") + + private val stubFile = + HostUtils.makePathForApk("$parentClassName-Stub.apk", Partition.PRODUCT) + + private val secondaryUsers = mutableListOf<Int>() + private val usersToRemove = mutableListOf<Int>() + private var savedDevice: ITestDevice? = null + private var savedPreparer: SystemPreparer? = null + + private fun setUpUsers(device: ITestDevice) { + if (this.savedDevice != null) return + this.savedDevice = device + secondaryUsers.clear() + secondaryUsers += device.userInfos.values.map(UserInfo::userId).filterNot { it == 0 } + while (secondaryUsers.size < USER_COUNT) { + secondaryUsers += device.createUser(parentClassName + secondaryUsers.size) + .also { usersToRemove += it } + } + } + + @JvmStatic + @AfterClass + fun cleanUp() { + savedDevice ?: return + + usersToRemove.forEach { + savedDevice?.removeUser(it) + } + + savedDevice?.uninstallPackage(TEST_PKG_NAME) + savedDevice?.deleteFile(stubFile.parent.toString()) + savedDevice?.deleteFile(deviceCompressedFile.parent.toString()) + savedDevice?.reboot() + savedDevice = null + + if (DEBUG_NO_REBOOT) { + savedPreparer?.after() + savedPreparer = null + } + } + } + + private val tempFolder = TemporaryFolder() + private val preparer: SystemPreparer = SystemPreparer(tempFolder, + SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device } + + @get:Rule + val rules = RuleChain.outerRule(tempFolder).let { + if (DEBUG_NO_REBOOT) { + it!! + } else { + it.around(preparer)!! + } + } + + private var hostCompressedFile: File? = null + + private val previousCodePaths = mutableListOf<String>() + + @Before + fun ensureUserAndCompressStubAndInstall() { + setUpUsers(device) + + val initialized = hostCompressedFile != null + if (!initialized) { + hostCompressedFile = tempFolder.newFile() + hostCompressedFile!!.outputStream().use { + javaClass.classLoader + .getResource(VERSION_ONE)!! + .openStream() + .use { input -> + GZIPOutputStream(it).use { output -> + input.copyTo(output) + } + } + } + } + + device.uninstallPackage(TEST_PKG_NAME) + + if (!initialized || !DEBUG_NO_REBOOT) { + savedPreparer = preparer + preparer.pushResourceFile(VERSION_STUB, stubFile.toString()) + .pushFile(hostCompressedFile, deviceCompressedFile.toString()) + .reboot() + } + + // This test forces the state to installed/enabled for all users, + // since it only tests the uninstall/disable side. + installExisting(User.PRIMARY) + installExisting(User.SECONDARY) + + ensureEnabled() + + // Ensure data app isn't re-installed multiple times by comparing against the original path + val codePath = HostUtils.getCodePaths(device, TEST_PKG_NAME).first() + assertThat(codePath).contains("/data/app") + assertThat(codePath).contains(TEST_PKG_NAME) + + previousCodePaths.clear() + previousCodePaths += codePath + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disablePrimaryFirstAndUninstall() { + toggleEnabled(false, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(false, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + device.uninstallPackage(TEST_PKG_NAME) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + } + + @Test + fun disableSecondaryFirstAndUninstall() { + toggleEnabled(false, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(false, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + device.uninstallPackage(TEST_PKG_NAME) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstalledEnablePrimaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + device.uninstallPackage(TEST_PKG_NAME) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstalledEnableSecondaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + device.uninstallPackage(TEST_PKG_NAME) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallPrimaryFirstByUserAndInstallExistingPrimaryFirst() { + uninstall(User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + uninstall(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + installExisting(User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + installExisting(User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallSecondaryFirstByUserAndInstallExistingSecondaryFirst() { + uninstall(User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + uninstall(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + installExisting(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + installExisting(User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallUpdatesAndEnablePrimaryFirst() { + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + // TODO: Is this intentional? There is no user argument for this command. + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + // If any user is enabled when uninstalling updates, /data is re-uncompressed + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + // Test enabling secondary to ensure path does not change, even though it's already enabled + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallUpdatesAndEnableSecondaryFirst() { + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + // If any user is enabled when uninstalling updates, /data is re-uncompressed + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstallUpdatesAndEnablePrimaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstallUpdatesAndEnableSecondaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstalledUninstallUpdatesAndEnablePrimaryFirst() { + uninstall(User.PRIMARY) + uninstall(User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstalledUninstallUpdatesAndEnableSecondaryFirst() { + uninstall(User.PRIMARY) + uninstall(User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + private fun ensureEnabled() { + toggleEnabled(true, User.PRIMARY) + toggleEnabled(true, User.SECONDARY) + + assertThat(HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME).all { it.value }) + .isTrue() + } + + private fun toggleEnabled(enabled: Boolean, user: User, pkgName: String = TEST_PKG_NAME) { + val command = if (enabled) "enable" else "disable" + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm $command --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm $command --user $it $pkgName") + } + } + } + } + + private fun uninstall(user: User, pkgName: String = TEST_PKG_NAME) { + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm uninstall --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm uninstall --user $it $pkgName") + } + } + } + } + + private fun installExisting(user: User, pkgName: String = TEST_PKG_NAME) { + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm install-existing --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm install-existing --user $it $pkgName") + } + } + } + } + + private fun assertState( + primaryInstalled: Boolean, + primaryEnabled: Boolean, + secondaryInstalled: Boolean, + secondaryEnabled: Boolean, + codePaths: List<CodePath> + ) { + HostUtils.getUserIdToPkgInstalledState(device, TEST_PKG_NAME) + .forEach { (userId, installed) -> + if (userId == 0) { + assertThat(installed).isEqualTo(primaryInstalled) + } else { + assertThat(installed).isEqualTo(secondaryInstalled) + } + } + + HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME) + .forEach { (userId, enabled) -> + if (userId == 0) { + assertThat(enabled).isEqualTo(primaryEnabled) + } else { + assertThat(enabled).isEqualTo(secondaryEnabled) + } + } + + assertCodePaths(codePaths.first(), codePaths.getOrNull(1)) + } + + private fun assertCodePaths(firstCodePath: CodePath, secondCodePath: CodePath? = null) { + val codePaths = HostUtils.getCodePaths(device, TEST_PKG_NAME) + assertThat(codePaths).hasSize(listOfNotNull(firstCodePath, secondCodePath).size) + + when (firstCodePath) { + CodePath.SAME -> { + assertThat(codePaths[0]).contains("/data/app") + assertThat(codePaths[0]).contains(TEST_PKG_NAME) + assertThat(codePaths[0]).isEqualTo(previousCodePaths.last()) + } + CodePath.DIFFERENT -> { + assertThat(codePaths[0]).contains("/data/app") + assertThat(codePaths[0]).contains(TEST_PKG_NAME) + assertThat(previousCodePaths).doesNotContain(codePaths[0]) + previousCodePaths.add(codePaths[0]) + } + CodePath.SYSTEM -> assertThat(codePaths[0]).isEqualTo(stubFile.parent.toString()) + } + + when (secondCodePath) { + CodePath.SAME, CodePath.DIFFERENT -> + throw AssertionError("secondDataPath cannot be a data path") + CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString()) + } + } + + enum class User { + /** The primary system user 0 */ + PRIMARY, + + /** + * All other users on the device that are not 0. This is split into an enum so that all + * methods that handle secondary act on all non-system users. Some behaviors only occur + * if a package state is marked for all non-primary users on the device, which can be + * more than just 1. + */ + SECONDARY + } + + enum class CodePath { + /** The data code path hasn't changed */ + SAME, + + /** New data code path */ + DIFFERENT, + + /** The static system code path */ + SYSTEM + } +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp index c9b29275a731..4a3076e4736a 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp @@ -13,26 +13,32 @@ // limitations under the License. android_test_helper_app { - name: "PackageManagerDummyAppVersion1", + name: "PackageManagerTestAppStub", + manifest: "AndroidManifestVersion1.xml", + srcs: [] +} + +android_test_helper_app { + name: "PackageManagerTestAppVersion1", manifest: "AndroidManifestVersion1.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion2", + name: "PackageManagerTestAppVersion2", manifest: "AndroidManifestVersion2.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion3", + name: "PackageManagerTestAppVersion3", manifest: "AndroidManifestVersion3.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion4", + name: "PackageManagerTestAppVersion4", manifest: "AndroidManifestVersion4.xml" } android_test_helper_app { - name: "PackageManagerDummyAppOriginalOverride", + name: "PackageManagerTestAppOriginalOverride", manifest: "AndroidManifestOriginalOverride.xml" } diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml index f16e1bc8a927..cba580e44065 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml @@ -16,10 +16,10 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app.override" + package="com.android.server.pm.test.test_app.override" android:versionCode="2" > - <original-package android:name="com.android.server.pm.test.dummy_app"/> + <original-package android:name="com.android.server.pm.test.test_app"/> </manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml index b492a31349fc..efc7372a177c 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="1" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml index 25e9f8eb2a67..620054c5dd57 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="2" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml index 935f5e62f508..1997771783dd 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="3" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml index d0643cbb2aeb..d6ade0304189 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="4" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index c6922536f61a..1b6ac3c84210 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -183,13 +183,12 @@ public class VibratorServiceTest { @Test public void hasAmplitudeControl_withAmplitudeControlSupport_returnsTrue() { - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); assertTrue(createService().hasAmplitudeControl()); } @Test public void hasAmplitudeControl_withNoAmplitudeControlSupport_returnsFalse() { - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(false); assertFalse(createService().hasAmplitudeControl()); } @@ -270,7 +269,7 @@ public class VibratorServiceTest { @Test public void vibrate_withOneShotAndAmplitudeControl_turnsVibratorOnAndSetsAmplitude() { - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); Mockito.clearInvocations(mNativeWrapperMock); @@ -340,7 +339,7 @@ public class VibratorServiceTest { @Test public void vibrate_withWaveform_controlsVibratorAmplitudeDuringTotalVibrationTime() throws Exception { - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); Mockito.clearInvocations(mNativeWrapperMock); @@ -511,7 +510,7 @@ public class VibratorServiceTest { setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); - when(mNativeWrapperMock.vibratorSupportsAmplitudeControl()).thenReturn(true); + mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); VibratorService service = createService(); service.systemReady(); 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 f991dff2797f..db56657e208c 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -550,7 +550,6 @@ public class AppsFilterTest { .setAppId(DUMMY_TARGET_APPID) .setName("com.some.package") .setCodePath("/") - .setResourcePath("/") .setPVersionCode(1L) .build(); PackageSetting calling = simulateAddPackage(appsFilter, @@ -874,7 +873,6 @@ public class AppsFilterTest { .setAppId(appId) .setName(newPkg.getPackageName()) .setCodePath("/") - .setResourcePath("/") .setPVersionCode(1L); final PackageSetting setting = (action == null ? settingBuilder : action.withBuilder(settingBuilder)).build(); diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java index 164bd725dd66..90edaef4294f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java @@ -38,8 +38,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase { public PackageSetting generateFakePackageSetting(String name) { return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"), - new File(mContext.getCacheDir(), "fakeResPath"), "", "", "", - "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/, + "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index c4e25b53d5d5..80f145b16147 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -86,8 +86,7 @@ public class PackageManagerServiceTest { // Create a real (non-null) PackageSetting and confirm that the removed // users are copied properly setting = new PackageSetting("name", "realName", new File("codePath"), - new File("resourcePath"), "legacyNativeLibraryPathString", - "primaryCpuAbiString", "secondaryCpuAbiString", + "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString", "cpuAbiOverrideString", 0, 0, 0, 0, null, null, null); pri.populateUsers(new int[] { diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index aa92ba49d190..0bf06bb4dda7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -439,7 +439,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME, REAL_PACKAGE_NAME, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -461,7 +460,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME /*pkgName*/, REAL_PACKAGE_NAME /*realPkgName*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -477,7 +475,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME /*pkgName*/, REAL_PACKAGE_NAME /*realPkgName*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, null /*primaryCpuAbiString*/, null /*secondaryCpuAbiString*/, @@ -507,7 +504,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -541,7 +537,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -581,7 +576,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, testUserSetting01 /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - null /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -609,7 +603,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -624,12 +617,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); - assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM)); assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); - assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); // signatures object must be different assertNotSame(testPkgSetting01.signatures, originalSignatures); @@ -649,7 +641,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -665,12 +656,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(0)); - assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64")); - assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86")); assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE)); // by default, the package is considered stopped @@ -695,7 +685,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, testUserSetting01 /*sharedUser*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -711,12 +700,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(10064)); - assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64")); - assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86")); assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE)); final PackageUserState userState = testPkgSetting01.readUserState(0); @@ -738,7 +726,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -754,12 +741,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(10064)); - assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); - assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); assertNotSame(testPkgSetting01.signatures, disabledSignatures); assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE)); @@ -806,10 +792,10 @@ public class PackageManagerSettingsTests { private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) { assertThat(origPkgSetting, is(not(testPkgSetting))); assertThat(origPkgSetting.appId, is(testPkgSetting.appId)); - assertSame(origPkgSetting.codePath, testPkgSetting.codePath); - assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath)); - assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString); - assertThat(origPkgSetting.codePathString, is(testPkgSetting.codePathString)); + assertSame(origPkgSetting.getCodePath(), testPkgSetting.getCodePath()); + assertThat(origPkgSetting.getCodePath(), is(testPkgSetting.getCodePath())); + assertSame(origPkgSetting.getCodePathString(), testPkgSetting.getCodePathString()); + assertThat(origPkgSetting.getCodePathString(), is(testPkgSetting.getCodePathString())); assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString); assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString)); assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime)); @@ -823,7 +809,9 @@ public class PackageManagerSettingsTests { testPkgSetting.legacyNativeLibraryPathString); assertThat(origPkgSetting.legacyNativeLibraryPathString, is(testPkgSetting.legacyNativeLibraryPathString)); - assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups); + if (origPkgSetting.mimeGroups != null) { + assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups); + } assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups)); assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState); assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState)); @@ -839,10 +827,6 @@ public class PackageManagerSettingsTests { assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString); assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString)); assertThat(origPkgSetting.realName, is(testPkgSetting.realName)); - assertSame(origPkgSetting.resourcePath, testPkgSetting.resourcePath); - assertThat(origPkgSetting.resourcePath, is(testPkgSetting.resourcePath)); - assertSame(origPkgSetting.resourcePathString, testPkgSetting.resourcePathString); - assertThat(origPkgSetting.resourcePathString, is(testPkgSetting.resourcePathString)); assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString); assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString)); assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser); @@ -874,7 +858,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME, REAL_PACKAGE_NAME, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -893,7 +876,6 @@ public class PackageManagerSettingsTests { packageName, packageName, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index b0b5386a49bd..2651cfa5449b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -312,8 +312,7 @@ public class PackageParserTest { private static PackageSetting mockPkgSetting(AndroidPackage pkg) { return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(), - new File(pkg.getCodePath()), new File(pkg.getCodePath()), null, - pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(), + new File(pkg.getCodePath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(), null, pkg.getVersionCode(), PackageInfoUtils.appInfoFlags(pkg, null), PackageInfoUtils.appInfoPrivateFlags(pkg, null), diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java index d12ea89469b7..f8e92ad71d8e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -30,7 +30,6 @@ public class PackageSettingBuilder { private String mName; private String mRealName; private String mCodePath; - private String mResourcePath; private String mLegacyNativeLibraryPathString; private String mPrimaryCpuAbiString; private String mSecondaryCpuAbiString; @@ -74,11 +73,6 @@ public class PackageSettingBuilder { return this; } - public PackageSettingBuilder setResourcePath(String resourcePath) { - this.mResourcePath = resourcePath; - return this; - } - public PackageSettingBuilder setLegacyNativeLibraryPathString( String legacyNativeLibraryPathString) { this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString; @@ -162,10 +156,10 @@ public class PackageSettingBuilder { public PackageSetting build() { final PackageSetting packageSetting = new PackageSetting(mName, mRealName, - new File(mCodePath), new File(mResourcePath), - mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString, - mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId, - mUsesStaticLibraries, mUsesStaticLibrariesVersions, mMimeGroups); + new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, + mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags, + mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions, + mMimeGroups); packageSetting.signatures = mSigningDetails != null ? new PackageSignatures(mSigningDetails) : new PackageSignatures(); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java index 55bc6812328a..7108490f80f8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java @@ -465,9 +465,9 @@ public class PackageSignaturesTest { // Generic PackageSetting object with values from a test app installed on a device to be // used to test the methods under the PackageSignatures signatures data member. File appPath = new File("/data/app/app"); - PackageSetting result = new PackageSetting("test.app", null, appPath, appPath, - "/data/app/app", null, null, null, - 1, 940097092, 0, 0 /*userId*/, null, null, null /*mimeGroups*/); + PackageSetting result = new PackageSetting("test.app", null, appPath, + "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null, + null /*mimeGroups*/); return result; } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index e7eff00c472e..56dddb0a8112 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -484,8 +484,7 @@ public class ScanTests { private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) { return new PackageSettingBuilder() .setName(packageName) - .setCodePath(createCodePath(packageName)) - .setResourcePath(createCodePath(packageName)); + .setCodePath(createCodePath(packageName)); } private static ScanRequestBuilder createBasicScanRequestBuilder(ParsingPackage pkg) { @@ -534,8 +533,7 @@ public class ScanTests { arrayContaining("some.static.library", "some.other.static.library")); assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L})); assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage)); - assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName)))); - assertThat(pkgSetting.resourcePath, is(new File(createCodePath(packageName)))); + assertThat(pkgSetting.getCodePath(), is(new File(createCodePath(packageName)))); assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345))); } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 4040fa6a675e..e3c795d03381 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -38,6 +38,7 @@ <uses-permission android:name="android.permission.REORDER_TASKS" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.STATUS_BAR" /> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) --> <application android:debuggable="true" 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 addf1ffe40c2..96b970056c98 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -38,7 +38,13 @@ import static org.mockito.ArgumentMatchers.eq; import android.app.WaitResult; import android.content.pm.ActivityInfo; +import android.graphics.PixelFormat; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.media.ImageReader; import android.platform.test.annotations.Presubmit; +import android.view.Display; +import android.view.DisplayInfo; import androidx.test.filters.MediumTest; @@ -173,4 +179,50 @@ public class ActivityStackSupervisorTests extends WindowTestsBase { verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */, eq(true) /* focused */); } + + @Test + /** Ensures that a trusted virtual display can launch arbitrary activities. */ + public void testTrustedVirtualDisplayCanLaunchActivities() { + final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); + final Task stack = new StackBuilder(mRootWindowContainer) + .setDisplay(newDisplay).build(); + final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); + VirtualDisplay virtualDisplay = createVirtualDisplay(true); + final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234, + virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info); + + assertThat(allowed).isTrue(); + } + + @Test + /** Ensures that an untrusted virtual display cannot launch arbitrary activities. */ + public void testUntrustedVirtualDisplayCannotLaunchActivities() { + final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); + final Task stack = new StackBuilder(mRootWindowContainer) + .setDisplay(newDisplay).build(); + final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); + VirtualDisplay virtualDisplay = createVirtualDisplay(false); + final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234, + virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info); + + assertThat(allowed).isFalse(); + } + + private VirtualDisplay createVirtualDisplay(boolean trusted) { + final DisplayManager dm = mContext.getSystemService(DisplayManager.class); + final DisplayInfo displayInfo = new DisplayInfo(); + final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY); + defaultDisplay.getDisplayInfo(displayInfo); + int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; + if (trusted) { + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; + } + + final ImageReader imageReader = ImageReader.newInstance( + displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2); + + return dm.createVirtualDisplay("virtualDisplay", displayInfo.logicalWidth, + displayInfo.logicalHeight, + displayInfo.logicalDensityDpi, imageReader.getSurface(), flags); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index a137cde2d351..044f81986517 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -168,8 +168,8 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_once() throws Exception { - // GIVEN a task record with whitelisted auth - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN a task record with allowlisted auth + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); // WHEN calling setLockTaskMode for LOCKED mode without resuming mLockTaskController.startLockTaskMode(tr, false, TEST_UID); @@ -185,9 +185,9 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_twice() throws Exception { - // GIVEN two task records with whitelisted auth - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); // WHEN calling setLockTaskMode for LOCKED mode on both tasks mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); @@ -205,7 +205,7 @@ public class LockTaskControllerTest { @Test public void testStartLockTaskMode_pinningRequest() { - // GIVEN a task record that is not whitelisted, i.e. with pinned auth + // GIVEN a task record that is not allowlisted, i.e. with pinned auth Task tr = getTask(Task.LOCK_TASK_AUTH_PINNABLE); // WHEN calling startLockTaskMode @@ -236,23 +236,23 @@ public class LockTaskControllerTest { @Test public void testLockTaskViolation() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN it's not a lock task violation to try and launch this task without clearing assertFalse(mLockTaskController.isLockTaskModeViolation(tr, false)); - // THEN it's a lock task violation to launch another task that is not whitelisted + // THEN it's a lock task violation to launch another task that is not allowlisted assertTrue(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_PINNABLE))); // THEN it's a lock task violation to launch another task that is disallowed from lock task assertTrue(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_DONT_LOCK))); - // THEN it's no a lock task violation to launch another task that is whitelisted + // THEN it's no a lock task violation to launch another task that is allowlisted assertFalse(mLockTaskController.isLockTaskModeViolation(getTask( - Task.LOCK_TASK_AUTH_WHITELISTED))); + Task.LOCK_TASK_AUTH_ALLOWLISTED))); assertFalse(mLockTaskController.isLockTaskModeViolation(getTask( Task.LOCK_TASK_AUTH_LAUNCHABLE))); // THEN it's not a lock task violation to launch another task that is priv launchable @@ -262,8 +262,8 @@ public class LockTaskControllerTest { @Test public void testLockTaskViolation_emergencyCall() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // GIVEN tasks necessary for emergency calling @@ -294,8 +294,8 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode() throws Exception { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN the same caller calls stopLockTaskMode @@ -311,8 +311,8 @@ public class LockTaskControllerTest { @Test(expected = SecurityException.class) public void testStopLockTaskMode_differentCaller() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN a different caller calls stopLockTaskMode @@ -323,8 +323,8 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_systemCaller() { - // GIVEN one task record with whitelisted auth that is in lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN one task record with allowlisted auth that is in lock task mode + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN system calls stopLockTaskMode @@ -336,9 +336,9 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_twoTasks() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -357,9 +357,9 @@ public class LockTaskControllerTest { @Test public void testStopLockTaskMode_rootTask() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -405,9 +405,9 @@ public class LockTaskControllerTest { @Test public void testClearLockedTasks() throws Exception { - // GIVEN two task records with whitelisted auth that is in lock task mode - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); - Task tr2 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + // GIVEN two task records with allowlisted auth that is in lock task mode + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); + Task tr2 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); mLockTaskController.startLockTaskMode(tr2, false, TEST_UID); @@ -434,7 +434,7 @@ public class LockTaskControllerTest { .thenReturn(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -454,7 +454,7 @@ public class LockTaskControllerTest { .thenReturn(true); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -471,7 +471,7 @@ public class LockTaskControllerTest { Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1, mContext.getUserId()); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -488,7 +488,7 @@ public class LockTaskControllerTest { Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 0, mContext.getUserId()); // AND there is a task record - Task tr1 = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr1 = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr1, true, TEST_UID); // WHEN calling clearLockedTasks on the root task @@ -500,45 +500,45 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskPackages() { - String[] whitelist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; - String[] whitelist2 = {TEST_PACKAGE_NAME}; - - // No package is whitelisted initially - for (String pkg : whitelist1) { - assertFalse("Package shouldn't be whitelisted: " + pkg, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); - assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, - mLockTaskController.isPackageWhitelisted(0, pkg)); + String[] allowlist1 = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + String[] allowlist2 = {TEST_PACKAGE_NAME}; + + // No package is allowlisted initially + for (String pkg : allowlist1) { + assertFalse("Package shouldn't be allowlisted: " + pkg, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be allowlisted for user 0: " + pkg, + mLockTaskController.isPackageAllowlisted(0, pkg)); } - // Apply whitelist - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist1); + // Apply allowlist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist1); - // Assert the whitelist is applied to the correct user - for (String pkg : whitelist1) { - assertTrue("Package should be whitelisted: " + pkg, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg)); - assertFalse("Package shouldn't be whitelisted for user 0: " + pkg, - mLockTaskController.isPackageWhitelisted(0, pkg)); + // Assert the allowlist is applied to the correct user + for (String pkg : allowlist1) { + assertTrue("Package should be allowlisted: " + pkg, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg)); + assertFalse("Package shouldn't be allowlisted for user 0: " + pkg, + mLockTaskController.isPackageAllowlisted(0, pkg)); } - // Update whitelist - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist2); + // Update allowlist + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist2); - // Assert the new whitelist is applied - assertTrue("Package should remain whitelisted: " + TEST_PACKAGE_NAME, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME)); - assertFalse("Package should no longer be whitelisted: " + TEST_PACKAGE_NAME_2, - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); + // Assert the new allowlist is applied + assertTrue("Package should remain allowlisted: " + TEST_PACKAGE_NAME, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME)); + assertFalse("Package should no longer be allowlisted: " + TEST_PACKAGE_NAME_2, + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, TEST_PACKAGE_NAME_2)); } @Test public void testUpdateLockTaskPackages_taskRemoved() throws Exception { - // GIVEN two tasks which are whitelisted initially + // GIVEN two tasks which are allowlisted initially Task tr1 = getTaskForUpdate(TEST_PACKAGE_NAME, true); Task tr2 = getTaskForUpdate(TEST_PACKAGE_NAME_2, false); - String[] whitelist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + String[] allowlist = {TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // GIVEN the tasks are launched into LockTask mode mLockTaskController.startLockTaskMode(tr1, false, TEST_UID); @@ -548,9 +548,9 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isTaskLocked(tr2)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); - // WHEN removing one package from whitelist - whitelist = new String[] {TEST_PACKAGE_NAME}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // WHEN removing one package from allowlist + allowlist = new String[] {TEST_PACKAGE_NAME}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the task running that package should be stopped verify(tr2).performClearTaskLocked(); @@ -560,9 +560,9 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isTaskLocked(tr1)); verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); - // WHEN removing the last package from whitelist - whitelist = new String[] {}; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // WHEN removing the last package from allowlist + allowlist = new String[] {}; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the last task should be cleared, and the system should quit LockTask mode verify(tr1).performClearTaskLocked(); @@ -574,7 +574,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures() throws Exception { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN lock task mode should be started with default status bar masks @@ -616,7 +616,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures_differentUser() throws Exception { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN lock task mode should be started with default status bar masks @@ -638,7 +638,7 @@ public class LockTaskControllerTest { @Test public void testUpdateLockTaskFeatures_keyguard() { // GIVEN a locked task - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // THEN keyguard should be disabled @@ -704,7 +704,7 @@ public class LockTaskControllerTest { TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); // Start lock task mode - Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED); + Task tr = getTask(Task.LOCK_TASK_AUTH_ALLOWLISTED); mLockTaskController.startLockTaskMode(tr, false, TEST_UID); // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled @@ -719,15 +719,15 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS)); - // unwhitelisted package should not be allowed + // unallowlisted package should not be allowed assertFalse(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); - // update the whitelist - String[] whitelist = new String[] { TEST_PACKAGE_NAME }; - mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist); + // update the allowlist + String[] allowlist = new String[] { TEST_PACKAGE_NAME }; + mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); - // whitelisted package should be allowed + // allowlisted package should be allowed assertTrue(mLockTaskController.isActivityAllowed( TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT)); @@ -755,17 +755,17 @@ public class LockTaskControllerTest { } /** - * @param isAppAware {@code true} if the app has marked if_whitelisted in its manifest + * @param isAppAware {@code true} if the app has marked if allowlisted in its manifest */ private Task getTaskForUpdate(String pkg, boolean isAppAware) { - final int authIfWhitelisted = isAppAware + final int authIfAllowlisted = isAppAware ? Task.LOCK_TASK_AUTH_LAUNCHABLE - : Task.LOCK_TASK_AUTH_WHITELISTED; - Task tr = getTask(pkg, authIfWhitelisted); + : Task.LOCK_TASK_AUTH_ALLOWLISTED; + Task tr = getTask(pkg, authIfAllowlisted); doAnswer((invocation) -> { - boolean isWhitelisted = - mLockTaskController.isPackageWhitelisted(TEST_USER_ID, pkg); - tr.mLockTaskAuth = isWhitelisted ? authIfWhitelisted : Task.LOCK_TASK_AUTH_PINNABLE; + boolean isAllowlisted = + mLockTaskController.isPackageAllowlisted(TEST_USER_ID, pkg); + tr.mLockTaskAuth = isAllowlisted ? authIfAllowlisted : Task.LOCK_TASK_AUTH_PINNABLE; return null; }).when(tr).setLockTaskAuth(); return tr; diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp new file mode 100644 index 000000000000..9d35cbc3de7f --- /dev/null +++ b/tests/Input/Android.bp @@ -0,0 +1,12 @@ +android_test { + name: "InputTests", + srcs: ["src/**/*.kt"], + platform_apis: true, + certificate: "platform", + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.rules", + "android-support-test", + "ub-uiautomator", + ], +} diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml new file mode 100644 index 000000000000..4195df72864c --- /dev/null +++ b/tests/Input/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.input"> + <uses-permission android:name="android.permission.MONITOR_INPUT"/> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> + <uses-permission android:name="android.permission.INJECT_EVENTS"/> + + <application android:label="InputTest"> + + <activity android:name=".UnresponsiveGestureMonitorActivity" + android:label="Unresponsive gesture monitor" + android:process=":externalProcess"> + </activity> + + + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.test.input" + android:label="Input Tests"/> +</manifest> diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml new file mode 100644 index 000000000000..c62db1ea5ca9 --- /dev/null +++ b/tests/Input/AndroidTest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2020 Google Inc. All Rights Reserved. + --> +<configuration description="Runs Input Tests"> + <option name="test-tag" value="InputTests" /> + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on" /> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="InputTests.apk"/> + + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.test.input"/> + <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" /> + <option name="shell-timeout" value="660s" /> + <option name="test-timeout" value="600s" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + </test> +</configuration> diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt new file mode 100644 index 000000000000..4da3eca25ea0 --- /dev/null +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -0,0 +1,115 @@ +/* + * 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.test.input + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.filters.MediumTest + +import android.graphics.Rect +import android.os.SystemClock +import android.provider.Settings +import android.provider.Settings.Global.HIDE_ERROR_DIALOGS +import android.support.test.uiautomator.By +import android.support.test.uiautomator.UiDevice +import android.support.test.uiautomator.UiObject2 +import android.support.test.uiautomator.Until +import android.view.InputDevice +import android.view.MotionEvent + +import org.junit.After +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** + * This test makes sure that an unresponsive gesture monitor gets an ANR. + * + * The gesture monitor must be registered from a different process than the instrumented process. + * Otherwise, when the test runs, you will get: + * Test failed to run to completion. + * Reason: 'Instrumentation run failed due to 'keyDispatchingTimedOut''. + * Check device logcat for details + * RUNNER ERROR: Instrumentation run failed due to 'keyDispatchingTimedOut' + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +class AnrTest { + companion object { + private const val TAG = "AnrTest" + } + + val mInstrumentation = InstrumentationRegistry.getInstrumentation() + var mHideErrorDialogs = 0 + + @Before + fun setUp() { + val contentResolver = mInstrumentation.targetContext.contentResolver + mHideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0) + Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0) + } + + @After + fun tearDown() { + val contentResolver = mInstrumentation.targetContext.contentResolver + Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, mHideErrorDialogs) + } + + @Test + fun testGestureMonitorAnr() { + startUnresponsiveActivity() + val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation) + val obj: UiObject2? = uiDevice.wait(Until.findObject( + By.text("Unresponsive gesture monitor")), 10000) + + if (obj == null) { + fail("Could not find unresponsive activity") + return + } + + val rect: Rect = obj.visibleBounds + val downTime = SystemClock.uptimeMillis() + val downEvent = MotionEvent.obtain(downTime, downTime, + MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */) + downEvent.source = InputDevice.SOURCE_TOUCHSCREEN + + mInstrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/) + + // Todo: replace using timeout from android.hardware.input.IInputManager + SystemClock.sleep(5000) // default ANR timeout for gesture monitors + + clickCloseAppOnAnrDialog() + } + + private fun clickCloseAppOnAnrDialog() { + // Find anr dialog and kill app + val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation) + val closeAppButton: UiObject2? = + uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000) + if (closeAppButton == null) { + fail("Could not find anr dialog") + return + } + closeAppButton.click() + } + + private fun startUnresponsiveActivity() { + val flags = " -W -n " + val startCmd = "am start $flags com.android.test.input/.UnresponsiveGestureMonitorActivity" + mInstrumentation.uiAutomation.executeShellCommand(startCmd) + } +}
\ No newline at end of file diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt new file mode 100644 index 000000000000..d83a4570fedc --- /dev/null +++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt @@ -0,0 +1,52 @@ +/** + * 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.test.input + +import android.app.Activity +import android.hardware.input.InputManager +import android.os.Bundle +import android.os.Looper +import android.util.Log +import android.view.InputChannel +import android.view.InputEvent +import android.view.InputEventReceiver +import android.view.InputMonitor + +class UnresponsiveReceiver(channel: InputChannel, looper: Looper) : + InputEventReceiver(channel, looper) { + companion object { + const val TAG = "UnresponsiveReceiver" + } + override fun onInputEvent(event: InputEvent) { + Log.i(TAG, "Received $event") + // Not calling 'finishInputEvent' in order to trigger the ANR + } +} + +class UnresponsiveGestureMonitorActivity : Activity() { + companion object { + const val MONITOR_NAME = "unresponsive gesture monitor" + } + private lateinit var mInputEventReceiver: InputEventReceiver + private lateinit var mInputMonitor: InputMonitor + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mInputMonitor = InputManager.getInstance().monitorGestureInput(MONITOR_NAME, displayId) + mInputEventReceiver = UnresponsiveReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()) + } +} diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index da6018e2e2c9..530d0e492f2e 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -29,6 +29,7 @@ java_test_host { "compatibility-tradefed", "frameworks-base-hostutils", "module_test_util", + "cts-install-lib-host", ], data: [ ":com.android.apex.cts.shim.v2_prebuilt", diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index 7cfbdc2b5062..5285b04f67d7 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; +import com.android.cts.install.lib.host.InstallUtilsHost; import com.android.ddmlib.Log; import com.android.tests.rollback.host.AbandonSessionsRule; import com.android.tests.util.ModuleTestUtils; @@ -49,6 +50,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private static final String APK_A = "TestAppAv1.apk"; private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this); + private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); /** * Runs the given phase of a test by calling into the device. @@ -93,7 +95,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbStagedInstallWaitForReadyFlagWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); String output = getDevice().executeAdbCommand("install", "--staged", @@ -107,7 +109,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbStagedInstallNoWaitFlagWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); String output = getDevice().executeAdbCommand("install", "--staged", @@ -122,7 +124,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbInstallMultiPackageCommandWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); File apkFile = mTestUtils.getTestFile(APK_A); diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java index f30c35aca8da..c2a5459ae125 100644 --- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java +++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java @@ -34,6 +34,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import javax.annotation.Nullable; @@ -49,7 +51,7 @@ import javax.annotation.Nullable; public class SystemPreparer extends ExternalResource { private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000; - // The paths of the files pushed onto the device through this rule. + // The paths of the files pushed onto the device through this rule to be removed after. private ArrayList<String> mPushedFiles = new ArrayList<>(); // The package names of packages installed through this rule. @@ -81,7 +83,7 @@ public class SystemPreparer extends ExternalResource { final ITestDevice device = mDeviceProvider.getDevice(); remount(); assertTrue(device.pushFile(copyResourceToTemp(filePath), outputPath)); - mPushedFiles.add(outputPath); + addPushedFile(device, outputPath); return this; } @@ -91,10 +93,23 @@ public class SystemPreparer extends ExternalResource { final ITestDevice device = mDeviceProvider.getDevice(); remount(); assertTrue(device.pushFile(file, outputPath)); - mPushedFiles.add(outputPath); + addPushedFile(device, outputPath); return this; } + private void addPushedFile(ITestDevice device, String outputPath) + throws DeviceNotAvailableException { + Path pathCreated = Paths.get(outputPath); + + // Find the top most parent that is new to the device + while (pathCreated.getParent() != null + && !device.doesFileExist(pathCreated.getParent().toString())) { + pathCreated = pathCreated.getParent(); + } + + mPushedFiles.add(pathCreated.toString()); + } + /** Deletes the given path from the device */ public SystemPreparer deleteFile(String file) throws DeviceNotAvailableException { final ITestDevice device = mDeviceProvider.getDevice(); @@ -203,7 +218,7 @@ public class SystemPreparer extends ExternalResource { /** Removes installed packages and files that were pushed to the device. */ @Override - protected void after() { + public void after() { final ITestDevice device = mDeviceProvider.getDevice(); try { remount(); |