diff options
19 files changed, 790 insertions, 480 deletions
diff --git a/ApiDocs.bp b/ApiDocs.bp index 1e5ae7bb6270..82759f767c3e 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -112,7 +112,6 @@ stubs_defaults { } // Defaults module for doc-stubs targets that use module source code as input. -// This is the default/normal. stubs_defaults { name: "framework-doc-stubs-sources-default", defaults: ["framework-doc-stubs-default"], @@ -148,12 +147,6 @@ droidstubs { } droidstubs { - name: "framework-doc-stubs", - defaults: ["framework-doc-stubs-sources-default"], - args: metalava_framework_docs_args, -} - -droidstubs { name: "framework-doc-system-stubs", defaults: ["framework-doc-stubs-sources-default"], args: metalava_framework_docs_args + @@ -161,11 +154,8 @@ droidstubs { api_levels_sdk_type: "system", } -// Experimental target building doc stubs with module stub source code as input. -// This is intended to eventually replace framework-doc-stubs, once all diffs -// have been eliminated. droidstubs { - name: "framework-doc-stubs-module-stubs", + name: "framework-doc-stubs", defaults: ["framework-doc-stubs-default"], args: metalava_framework_docs_args, srcs: [ diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 1d07a0330bc5..eb8f43e3d073 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -220,8 +220,10 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { String networkId = null; boolean roaming = !snapshot.getNetworkCapabilities().hasCapability( NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); - boolean metered = !snapshot.getNetworkCapabilities().hasCapability( - NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + boolean metered = !(snapshot.getNetworkCapabilities().hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + || snapshot.getNetworkCapabilities().hasCapability( + NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)); final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities()); diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 3db71361610d..69b4187711f6 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -371,6 +371,10 @@ public class UiTranslationController { Log.v(TAG, "onVirtualViewTranslationCompleted: received response for " + "AutofillId " + autofillId); } + view.onVirtualViewTranslationResponses(virtualChildResponse); + if (mCurrentState == STATE_UI_TRANSLATION_PAUSED) { + return; + } mActivity.runOnUiThread(() -> { if (view.getViewTranslationCallback() == null) { if (DEBUG) { @@ -379,7 +383,6 @@ public class UiTranslationController { } return; } - view.onVirtualViewTranslationResponses(virtualChildResponse); if (view.getViewTranslationCallback() != null) { view.getViewTranslationCallback().onShowTranslation(view); } @@ -427,6 +430,8 @@ public class UiTranslationController { + " may be gone."); continue; } + int currentState; + currentState = mCurrentState; mActivity.runOnUiThread(() -> { ViewTranslationCallback callback = view.getViewTranslationCallback(); if (view.getViewTranslationResponse() != null @@ -460,6 +465,9 @@ public class UiTranslationController { callback.enableContentPadding(); } view.onViewTranslationResponse(response); + if (currentState == STATE_UI_TRANSLATION_PAUSED) { + return; + } callback.onShowTranslation(view); }); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 8205d3579b3b..e6f91a25f168 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -128,6 +128,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.accessibility.magnification.MagnificationController; import com.android.server.accessibility.magnification.MagnificationProcessor; +import com.android.server.accessibility.magnification.MagnificationScaleProvider; import com.android.server.accessibility.magnification.WindowMagnificationManager; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -338,7 +339,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler, mWindowManagerService, this, mSecurityPolicy, this, mTraceManager); mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler); - mMagnificationController = new MagnificationController(this, mLock, mContext); + mMagnificationController = new MagnificationController(this, mLock, mContext, + new MagnificationScaleProvider(mContext)); mMagnificationProcessor = new MagnificationProcessor(mMagnificationController); init(); } @@ -1364,6 +1366,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void switchUser(int userId) { + mMagnificationController.updateUserIdIfNeeded(userId); synchronized (mLock) { if (mCurrentUserId == userId && mInitialized) { return; @@ -1386,8 +1389,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // The user changed. mCurrentUserId = userId; - - mMagnificationController.updateUserIdIfNeeded(mCurrentUserId); AccessibilityUserState userState = getCurrentUserStateLocked(); readConfigurationForUserStateLocked(userState); @@ -1444,6 +1445,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { mUserStates.remove(userId); } + getMagnificationController().onUserRemoved(userId); } // Called only during settings restore; currently supports only the owner user diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index 8f30aa9acc79..c62473db948c 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -28,10 +28,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.Rect; import android.graphics.Region; -import android.os.AsyncTask; import android.os.Handler; import android.os.Message; -import android.provider.Settings; import android.text.TextUtils; import android.util.MathUtils; import android.util.Slog; @@ -59,7 +57,8 @@ import java.util.Locale; * holding the current state of magnification and animation, and it handles * communication between the accessibility manager and window manager. * - * Magnification is limited to the range [MIN_SCALE, MAX_SCALE], and can only occur inside the + * Magnification is limited to the range controlled by + * {@link MagnificationScaleProvider#constrainScale(float)}, and can only occur inside the * magnification region. If a value is out of bounds, it will be adjusted to guarantee these * constraints. */ @@ -69,13 +68,9 @@ public class FullScreenMagnificationController { private static final MagnificationAnimationCallback STUB_ANIMATION_CALLBACK = success -> { }; - public static final float MIN_SCALE = 1.0f; - public static final float MAX_SCALE = 8.0f; private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false; - private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f; - private final Object mLock; private final ControllerContext mControllerCtx; @@ -84,7 +79,7 @@ public class FullScreenMagnificationController { private final MagnificationInfoChangedCallback mMagnificationInfoChangedCallback; - private int mUserId; + private final MagnificationScaleProvider mScaleProvider; private final long mMainThreadId; @@ -489,7 +484,7 @@ public class FullScreenMagnificationController { return false; } // Constrain scale immediately for use in the pivot calculations. - scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); + scale = MagnificationScaleProvider.constrainScale(scale); final Rect viewport = mTempRect; mMagnificationRegion.getBounds(viewport); @@ -557,7 +552,7 @@ public class FullScreenMagnificationController { // Compute changes. boolean changed = false; - final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); + final float normScale = MagnificationScaleProvider.constrainScale(scale); if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) { mCurrentMagnificationSpec.scale = normScale; changed = true; @@ -658,12 +653,13 @@ public class FullScreenMagnificationController { */ public FullScreenMagnificationController(@NonNull Context context, @NonNull AccessibilityManagerService ams, @NonNull Object lock, - @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) { + @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback, + @NonNull MagnificationScaleProvider scaleProvider) { this(new ControllerContext(context, ams, LocalServices.getService(WindowManagerInternal.class), new Handler(context.getMainLooper()), context.getResources().getInteger(R.integer.config_longAnimTime)), lock, - magnificationInfoChangedCallback); + magnificationInfoChangedCallback, scaleProvider); } /** @@ -672,12 +668,14 @@ public class FullScreenMagnificationController { @VisibleForTesting public FullScreenMagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock, - @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback) { + @NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback, + @NonNull MagnificationScaleProvider scaleProvider) { mControllerCtx = ctx; mLock = lock; mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId(); mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this); mMagnificationInfoChangedCallback = magnificationInfoChangedCallback; + mScaleProvider = scaleProvider; } /** @@ -1096,18 +1094,9 @@ public class FullScreenMagnificationController { /** * Persists the default display magnification scale to the current user's settings. */ - public void persistScale() { - // TODO: b/123047354, Need support multi-display? + public void persistScale(int displayId) { final float scale = getScale(Display.DEFAULT_DISPLAY); - final int userId = mUserId; - - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - mControllerCtx.putMagnificationScale(scale, userId); - return null; - } - }.execute(); + mScaleProvider.putScale(scale, displayId); } /** @@ -1117,21 +1106,8 @@ public class FullScreenMagnificationController { * @return the previously persisted magnification scale, or the default * scale if none is available */ - public float getPersistedScale() { - return mControllerCtx.getMagnificationScale(mUserId); - } - - /** - * Sets the currently active user ID. - * - * @param userId the currently active user ID - */ - public void setUserId(int userId) { - if (mUserId == userId) { - return; - } - mUserId = userId; - resetAllIfNeeded(false); + public float getPersistedScale(int displayId) { + return mScaleProvider.getScale(displayId); } /** @@ -1225,7 +1201,11 @@ public class FullScreenMagnificationController { mControllerCtx.getHandler().sendMessage(m); } - private void resetAllIfNeeded(boolean animate) { + /** + * Resets magnification on all displays. + * @param animate reset the magnification with animation + */ + void resetAllIfNeeded(boolean animate) { synchronized (mLock) { for (int i = 0; i < mDisplays.size(); i++) { resetIfNeeded(mDisplays.keyAt(i), animate); @@ -1288,8 +1268,8 @@ public class FullScreenMagnificationController { public String toString() { StringBuilder builder = new StringBuilder(); builder.append("MagnificationController["); - builder.append("mUserId=").append(mUserId); builder.append(", mDisplays=").append(mDisplays); + builder.append(", mScaleProvider=").append(mScaleProvider); builder.append("]"); return builder.toString(); } @@ -1570,23 +1550,6 @@ public class FullScreenMagnificationController { } /** - * Write Settings of magnification scale. - */ - public void putMagnificationScale(float value, int userId) { - Settings.Secure.putFloatForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId); - } - - /** - * Get Settings of magnification scale. - */ - public float getMagnificationScale(int userId) { - return Settings.Secure.getFloatForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, - DEFAULT_MAGNIFICATION_SCALE, userId); - } - - /** * @return Configuration of animation duration. */ public long getAnimationDuration() { diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index 8f4a5cb4dad0..935df9934dcb 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -119,11 +119,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH private static final boolean DEBUG_DETECTING = false | DEBUG_ALL; private static final boolean DEBUG_PANNING_SCALING = false | DEBUG_ALL; - // The MIN_SCALE is different from MagnificationController.MIN_SCALE due + // The MIN_SCALE is different from MagnificationScaleProvider.MIN_SCALE due // to AccessibilityService.MagnificationController#setScale() has // different scale range private static final float MIN_SCALE = 2.0f; - private static final float MAX_SCALE = FullScreenMagnificationController.MAX_SCALE; + private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE; @VisibleForTesting final FullScreenMagnificationController mFullScreenMagnificationController; @@ -341,7 +341,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } public void persistScaleAndTransitionTo(State state) { - mFullScreenMagnificationController.persistScale(); + mFullScreenMagnificationController.persistScale(mDisplayId); clear(); transitionTo(state); } @@ -945,7 +945,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (DEBUG_DETECTING) Slog.i(mLogTag, "zoomOn(" + centerX + ", " + centerY + ")"); final float scale = MathUtils.constrain( - mFullScreenMagnificationController.getPersistedScale(), + mFullScreenMagnificationController.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE); mFullScreenMagnificationController.setScaleAndCenter(mDisplayId, scale, centerX, centerY, diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 5a6836c2d771..3708c7a422a0 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -23,11 +23,13 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; import android.os.SystemClock; +import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; @@ -75,12 +77,15 @@ public class MagnificationController implements WindowMagnificationManager.Callb private final SparseArray<DisableMagnificationCallback> mMagnificationEndRunnableSparseArray = new SparseArray(); + private final MagnificationScaleProvider mScaleProvider; private FullScreenMagnificationController mFullScreenMagnificationController; private WindowMagnificationManager mWindowMagnificationMgr; private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; @GuardedBy("mLock") private int mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE; + // Track the active user to reset the magnification and get the associated user settings. + private @UserIdInt int mUserId = UserHandle.USER_SYSTEM; @GuardedBy("mLock") private boolean mImeWindowVisible = false; private long mWindowModeEnabledTime = 0; @@ -98,17 +103,19 @@ public class MagnificationController implements WindowMagnificationManager.Callb } public MagnificationController(AccessibilityManagerService ams, Object lock, - Context context) { + Context context, MagnificationScaleProvider scaleProvider) { mAms = ams; mLock = lock; mContext = context; + mScaleProvider = scaleProvider; } @VisibleForTesting public MagnificationController(AccessibilityManagerService ams, Object lock, Context context, FullScreenMagnificationController fullScreenMagnificationController, - WindowMagnificationManager windowMagnificationManager) { - this(ams, lock, context); + WindowMagnificationManager windowMagnificationManager, + MagnificationScaleProvider scaleProvider) { + this(ams, lock, context, scaleProvider); mFullScreenMagnificationController = fullScreenMagnificationController; mWindowMagnificationMgr = windowMagnificationManager; } @@ -194,7 +201,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb final FullScreenMagnificationController screenMagnificationController = getFullScreenMagnificationController(); final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr(); - final float scale = windowMagnificationMgr.getPersistedScale(); + final float scale = mScaleProvider.getScale(displayId); final DisableMagnificationCallback animationEndCallback = new DisableMagnificationCallback(transitionCallBack, displayId, targetMode, scale, magnificationCenter); @@ -313,13 +320,23 @@ public class MagnificationController implements WindowMagnificationManager.Callb * @param userId the currently active user ID */ public void updateUserIdIfNeeded(int userId) { + if (mUserId == userId) { + return; + } + mUserId = userId; + final FullScreenMagnificationController fullMagnificationController; + final WindowMagnificationManager windowMagnificationManager; synchronized (mLock) { - if (mFullScreenMagnificationController != null) { - mFullScreenMagnificationController.setUserId(userId); - } - if (mWindowMagnificationMgr != null) { - mWindowMagnificationMgr.setUserId(userId); - } + fullMagnificationController = mFullScreenMagnificationController; + windowMagnificationManager = mWindowMagnificationMgr; + } + + mScaleProvider.onUserChanged(userId); + if (fullMagnificationController != null) { + fullMagnificationController.resetAllIfNeeded(false); + } + if (windowMagnificationManager != null) { + windowMagnificationManager.disableAllWindowMagnifiers(); } } @@ -337,6 +354,14 @@ public class MagnificationController implements WindowMagnificationManager.Callb mWindowMagnificationMgr.onDisplayRemoved(displayId); } } + mScaleProvider.onDisplayRemoved(displayId); + } + + /** + * Called when the given user is removed. + */ + public void onUserRemoved(int userId) { + mScaleProvider.onUserRemoved(userId); } public void setMagnificationCapabilities(int capabilities) { @@ -378,8 +403,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb synchronized (mLock) { if (mFullScreenMagnificationController == null) { mFullScreenMagnificationController = new FullScreenMagnificationController(mContext, - mAms, mLock, this); - mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked()); + mAms, mLock, this, mScaleProvider); } } return mFullScreenMagnificationController; @@ -404,7 +428,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb synchronized (mLock) { if (mWindowMagnificationMgr == null) { mWindowMagnificationMgr = new WindowMagnificationManager(mContext, - mAms.getCurrentUserIdLocked(), this, mAms.getTraceManager()); + mUserId, this, mAms.getTraceManager(), + mScaleProvider); } return mWindowMagnificationMgr; } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java new file mode 100644 index 000000000000..8e1aa38be9e8 --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + +import android.content.Context; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.MathUtils; +import android.util.SparseArray; +import android.view.Display; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; + +/** + * Supplies setter/getter of the magnification scale for the given display. Only the value of the + * default play is persisted. It also constraints the range of applied magnification scale between + * [MIN_SCALE, MAX_SCALE] which is consistent with the range provided by + * {@code AccessibilityService.MagnificationController#setScale()}. + */ +public class MagnificationScaleProvider { + + @VisibleForTesting + protected static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f; + public static final float MIN_SCALE = 1.0f; + public static final float MAX_SCALE = 8.0f; + + private final Context mContext; + // Stores the scale for non-default displays. + @GuardedBy("mLock") + private final SparseArray<SparseArray<Float>> mUsersScales = new SparseArray(); + private int mCurrentUserId = UserHandle.USER_SYSTEM; + private final Object mLock = new Object(); + + public MagnificationScaleProvider(Context context) { + mContext = context; + } + + /** + * Stores the user settings scale associated to the given display. Only the scale of the + * default display is persistent. + * + * @param scale the magnification scale + * @param displayId the id of the display + */ + void putScale(float scale, int displayId) { + if (displayId == Display.DEFAULT_DISPLAY) { + BackgroundThread.getHandler().post( + () -> Settings.Secure.putFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale, + mCurrentUserId)); + } else { + synchronized (mLock) { + getScalesWithCurrentUser().put(displayId, scale); + } + } + } + + /** + * Gets the user settings scale with the given display. + * + * @param displayId the id of the display + * @return the magnification scale. + */ + float getScale(int displayId) { + if (displayId == Display.DEFAULT_DISPLAY) { + return Settings.Secure.getFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, + DEFAULT_MAGNIFICATION_SCALE, mCurrentUserId); + } else { + synchronized (mLock) { + return getScalesWithCurrentUser().get(displayId, DEFAULT_MAGNIFICATION_SCALE); + } + } + } + + + @GuardedBy("mLock") + private SparseArray<Float> getScalesWithCurrentUser() { + SparseArray<Float> scales = mUsersScales.get(mCurrentUserId); + if (scales == null) { + scales = new SparseArray<>(); + mUsersScales.put(mCurrentUserId, scales); + } + + return scales; + } + + void onUserChanged(int userId) { + synchronized (mLock) { + mCurrentUserId = userId; + } + } + + void onUserRemoved(int userId) { + synchronized (mLock) { + mUsersScales.remove(userId); + } + } + + void onDisplayRemoved(int displayId) { + synchronized (mLock) { + final int userCounts = mUsersScales.size(); + for (int i = userCounts - 1; i >= 0; i--) { + mUsersScales.get(i).remove(displayId); + } + } + } + + @Override + public String toString() { + synchronized (mLock) { + return "MagnificationScaleProvider{" + + "mCurrentUserId=" + mCurrentUserId + + "Scale on the default display=" + getScale(Display.DEFAULT_DISPLAY) + + "Scales on non-default displays=" + getScalesWithCurrentUser() + + '}'; + } + } + + static float constrainScale(float scale) { + return MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); + } +} 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 bc61284f6084..7d8f545b65c3 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java @@ -69,7 +69,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl //Ensure the range has consistency with FullScreenMagnificationGestureHandler. private static final float MIN_SCALE = 2.0f; - private static final float MAX_SCALE = WindowMagnificationManager.MAX_SCALE; + private static final float MAX_SCALE = MagnificationScaleProvider.MAX_SCALE; private final WindowMagnificationManager mWindowMagnificationMgr; @VisibleForTesting @@ -177,8 +177,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl } final float scale = MathUtils.constrain( - mWindowMagnificationMgr.getPersistedScale(), - MIN_SCALE, MAX_SCALE); + mWindowMagnificationMgr.getPersistedScale(mDisplayId), MIN_SCALE, MAX_SCALE); mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY); } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java index 7a111d80b42e..ce7ba7568b6e 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java @@ -29,8 +29,6 @@ import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; -import android.provider.Settings; -import android.util.MathUtils; import android.util.Slog; import android.util.SparseArray; import android.view.MotionEvent; @@ -40,7 +38,6 @@ import android.view.accessibility.MagnificationAnimationCallback; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.statusbar.StatusBarManagerInternal; @@ -49,6 +46,8 @@ import com.android.server.statusbar.StatusBarManagerInternal; * A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper} * create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with * SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}. + * The applied magnification scale is constrained by + * {@link MagnificationScaleProvider#constrainScale(float)} */ public class WindowMagnificationManager implements PanningScalingHandler.MagnificationDelegate { @@ -57,10 +56,6 @@ public class WindowMagnificationManager implements private static final String TAG = "WindowMagnificationMgr"; - //Ensure the range has consistency with full screen. - static final float MAX_SCALE = FullScreenMagnificationController.MAX_SCALE; - static final float MIN_SCALE = FullScreenMagnificationController.MIN_SCALE; - private final Object mLock = new Object(); private final Context mContext; @VisibleForTesting @@ -71,7 +66,6 @@ public class WindowMagnificationManager implements private ConnectionCallback mConnectionCallback; @GuardedBy("mLock") private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>(); - private int mUserId; private boolean mReceiverRegistered = false; @VisibleForTesting @@ -116,13 +110,14 @@ public class WindowMagnificationManager implements private final Callback mCallback; private final AccessibilityTraceManager mTrace; + private final MagnificationScaleProvider mScaleProvider; public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback, - AccessibilityTraceManager trace) { + AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) { mContext = context; - mUserId = userId; mCallback = callback; mTrace = trace; + mScaleProvider = scaleProvider; } /** @@ -159,15 +154,6 @@ public class WindowMagnificationManager implements } /** - * Sets the currently active user ID. - * - * @param userId the currently active user ID - */ - public void setUserId(int userId) { - mUserId = userId; - } - - /** * @return {@code true} if {@link IWindowMagnificationConnection} is available */ public boolean isConnected() { @@ -219,13 +205,18 @@ public class WindowMagnificationManager implements return true; } - @GuardedBy("mLock") - private void disableAllWindowMagnifiers() { - for (int i = 0; i < mWindowMagnifiers.size(); i++) { - final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); - magnifier.disableWindowMagnificationInternal(null); + /** + * Disables window magnifier on all displays without animation. + */ + void disableAllWindowMagnifiers() { + synchronized (mLock) { + for (int i = 0; i < mWindowMagnifiers.size(); i++) { + final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); + magnifier.disableWindowMagnificationInternal(null); + } + mWindowMagnifiers.clear(); } - mWindowMagnifiers.clear(); + } private void resetWindowMagnifiers() { @@ -378,29 +369,24 @@ public class WindowMagnificationManager implements } /** - * Retrieves a previously persisted magnification scale from the current - * user's settings. + * Retrieves a previously magnification scale from the current + * user's settings. Only the value of the default display is persisted. * - * @return the previously persisted magnification scale, or the default + * @return the previously magnification scale, or the default * scale if none is available */ - float getPersistedScale() { - return Settings.Secure.getFloatForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, - MIN_SCALE, mUserId); + float getPersistedScale(int displayId) { + return mScaleProvider.getScale(displayId); } /** - * Persists the default display magnification scale to the current user's settings. + * Persists the default display magnification scale to the current user's settings. Only the + * value of the default display is persisted in user's settings. */ void persistScale(int displayId) { - float scale = getScale(displayId); if (scale != 1.0f) { - BackgroundThread.getHandler().post(() -> { - Settings.Secure.putFloatForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale, mUserId); - }); + mScaleProvider.putScale(scale, displayId); } } @@ -511,7 +497,7 @@ public class WindowMagnificationManager implements * * @param displayId The logical display id. */ - void onDisplayRemoved(int displayId) { + public void onDisplayRemoved(int displayId) { disableWindowMagnification(displayId, true); } @@ -613,7 +599,7 @@ public class WindowMagnificationManager implements private static class WindowMagnifier { private final int mDisplayId; - private float mScale = MIN_SCALE; + private float mScale = MagnificationScaleProvider.MIN_SCALE; private boolean mEnabled; private final WindowMagnificationManager mWindowMagnificationManager; @@ -633,7 +619,7 @@ public class WindowMagnificationManager implements if (mEnabled) { return false; } - final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); + final float normScale = MagnificationScaleProvider.constrainScale(scale); if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale, centerX, centerY, animationCallback)) { mScale = normScale; @@ -664,7 +650,7 @@ public class WindowMagnificationManager implements if (!mEnabled) { return; } - final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); + final float normScale = MagnificationScaleProvider.constrainScale(scale); if (Float.compare(mScale, normScale) != 0 && mWindowMagnificationManager.setScaleInternal(mDisplayId, scale)) { mScale = normScale; diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java new file mode 100644 index 000000000000..2a0adeb71559 --- /dev/null +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.companion; + +import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING; +import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import static com.android.internal.util.CollectionUtils.filter; +import static com.android.internal.util.FunctionalUtils.uncheckExceptions; +import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.server.companion.CompanionDeviceManagerService.DEBUG; +import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG; +import static com.android.server.companion.CompanionDeviceManagerService.getCallingUserId; + +import static java.util.Collections.unmodifiableMap; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.companion.Association; +import android.companion.AssociationRequest; +import android.companion.CompanionDeviceManager; +import android.companion.ICompanionDeviceDiscoveryService; +import android.companion.IFindDeviceCallback; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.Signature; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.PackageUtils; +import android.util.Slog; + +import com.android.internal.infra.AndroidFuture; +import com.android.internal.infra.PerUser; +import com.android.internal.infra.ServiceConnector; +import com.android.internal.util.ArrayUtils; +import com.android.server.FgThread; + +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +class AssociationRequestsProcessor { + private static final String TAG = LOG_TAG + ".AssociationRequestsProcessor"; + + private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION; + static { + final Map<String, String> map = new ArrayMap<>(); + map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH); + map.put(DEVICE_PROFILE_APP_STREAMING, + Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING); + + DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map); + } + + private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative( + CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, + ".CompanionDeviceDiscoveryService"); + + private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5; + private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min; + + private final Context mContext; + private final CompanionDeviceManagerService mService; + + private AssociationRequest mRequest; + private IFindDeviceCallback mFindDeviceCallback; + private String mCallingPackage; + private AndroidFuture<?> mOngoingDeviceDiscovery; + + private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors; + + AssociationRequestsProcessor(CompanionDeviceManagerService service) { + mContext = service.getContext(); + mService = service; + + final Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO); + mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() { + @Override + protected ServiceConnector<ICompanionDeviceDiscoveryService> create(int userId) { + return new ServiceConnector.Impl<>( + mContext, + serviceIntent, 0/* bindingFlags */, userId, + ICompanionDeviceDiscoveryService.Stub::asInterface); + } + }; + } + + void process(AssociationRequest request, IFindDeviceCallback callback, String callingPackage) + throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "process(request=" + request + ", from=" + callingPackage + ")"); + } + + checkNotNull(request, "Request cannot be null"); + checkNotNull(callback, "Callback cannot be null"); + mService.checkCallerIsSystemOr(callingPackage); + int userId = getCallingUserId(); + mService.checkUsesFeature(callingPackage, userId); + final String deviceProfile = request.getDeviceProfile(); + validateDeviceProfileAndCheckPermission(deviceProfile); + + mFindDeviceCallback = callback; + mRequest = request; + mCallingPackage = callingPackage; + request.setCallingPackage(callingPackage); + + if (mayAssociateWithoutPrompt(callingPackage, userId)) { + Slog.i(TAG, "setSkipPrompt(true)"); + request.setSkipPrompt(true); + } + callback.asBinder().linkToDeath(mBinderDeathRecipient /* recipient */, 0); + + mOngoingDeviceDiscovery = getDeviceProfilePermissionDescription(deviceProfile) + .thenComposeAsync(description -> { + if (DEBUG) { + Slog.d(TAG, "fetchProfileDescription done: " + description); + } + + request.setDeviceProfilePrivilegesDescription(description); + + return mServiceConnectors.forUser(userId).postAsync(service -> { + if (DEBUG) { + Slog.d(TAG, "Connected to CDM service -> " + + "Starting discovery for " + request); + } + + AndroidFuture<String> future = new AndroidFuture<>(); + service.startDiscovery(request, callingPackage, callback, future); + return future; + }).cancelTimeout(); + + }, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> { + if (err == null) { + mService.createAssociationInternal( + userId, deviceAddress, callingPackage, deviceProfile); + } else { + Slog.e(TAG, "Failed to discover device(s)", err); + callback.onFailure("No devices found: " + err.getMessage()); + } + cleanup(); + })); + } + + void stopScan(AssociationRequest request, IFindDeviceCallback callback, String callingPackage) { + if (DEBUG) { + Slog.d(TAG, "stopScan(request = " + request + ")"); + } + if (Objects.equals(request, mRequest) + && Objects.equals(callback, mFindDeviceCallback) + && Objects.equals(callingPackage, mCallingPackage)) { + cleanup(); + } + } + + private void validateDeviceProfileAndCheckPermission(@Nullable String deviceProfile) { + // Device profile can be null. + if (deviceProfile == null) return; + + if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) { + // TODO: remove, when properly supporting this profile. + throw new UnsupportedOperationException( + "DEVICE_PROFILE_APP_STREAMING is not fully supported yet."); + } + + if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) { + throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile); + } + + final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile); + if (mContext.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) { + throw new SecurityException("Application must hold " + permission + " to associate " + + "with a device with " + deviceProfile + " profile."); + } + } + + private void cleanup() { + if (DEBUG) { + Slog.d(TAG, "cleanup(); discovery = " + + mOngoingDeviceDiscovery + ", request = " + mRequest); + } + synchronized (mService.mLock) { + AndroidFuture<?> ongoingDeviceDiscovery = mOngoingDeviceDiscovery; + if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) { + ongoingDeviceDiscovery.cancel(true); + } + if (mFindDeviceCallback != null) { + mFindDeviceCallback.asBinder().unlinkToDeath(mBinderDeathRecipient, 0); + mFindDeviceCallback = null; + } + mRequest = null; + mCallingPackage = null; + } + } + + private boolean mayAssociateWithoutPrompt(String packageName, int userId) { + String[] sameOemPackages = mContext.getResources() + .getStringArray(com.android.internal.R.array.config_companionDevicePackages); + if (!ArrayUtils.contains(sameOemPackages, packageName)) { + Slog.w(TAG, packageName + + " can not silently create associations due to no package found." + + " Packages from OEM: " + Arrays.toString(sameOemPackages) + ); + return false; + } + + // Throttle frequent associations + long now = System.currentTimeMillis(); + Set<Association> recentAssociations = filter( + mService.getAllAssociations(userId, packageName), + a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS); + + if (recentAssociations.size() >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) { + Slog.w(TAG, "Too many associations. " + packageName + + " already associated " + recentAssociations.size() + + " devices within the last " + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS + + "ms: " + recentAssociations); + return false; + } + String[] sameOemCerts = mContext.getResources() + .getStringArray(com.android.internal.R.array.config_companionDeviceCerts); + + Signature[] signatures = mService.mPackageManagerInternal + .getPackage(packageName).getSigningDetails().getSignatures(); + String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures); + + Set<String> sameOemPackageCerts = + getSameOemPackageCerts(packageName, sameOemPackages, sameOemCerts); + + for (String cert : apkCerts) { + if (sameOemPackageCerts.contains(cert)) { + return true; + } + } + + Slog.w(TAG, packageName + + " can not silently create associations. " + packageName + + " has SHA256 certs from APK: " + Arrays.toString(apkCerts) + + " and from OEM: " + Arrays.toString(sameOemCerts) + ); + + return false; + } + + @NonNull + private AndroidFuture<String> getDeviceProfilePermissionDescription( + @Nullable String deviceProfile) { + if (deviceProfile == null) { + return AndroidFuture.completedFuture(null); + } + + final AndroidFuture<String> result = new AndroidFuture<>(); + mService.mPermissionControllerManager.getPrivilegesDescriptionStringForProfile( + deviceProfile, FgThread.getExecutor(), desc -> { + try { + result.complete(String.valueOf(desc)); + } catch (Exception e) { + result.completeExceptionally(e); + } + }); + return result; + } + + + void dump(@NonNull PrintWriter pw) { + pw.append("Discovery Service State:").append('\n'); + for (int i = 0, size = mServiceConnectors.size(); i < size; i++) { + int userId = mServiceConnectors.keyAt(i); + pw.append(" ") + .append("u").append(Integer.toString(userId)).append(": ") + .append(Objects.toString(mServiceConnectors.valueAt(i))) + .append('\n'); + } + } + + private final IBinder.DeathRecipient mBinderDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + if (DEBUG) { + Slog.d(TAG, "binderDied()"); + } + mService.mMainHandler.post(AssociationRequestsProcessor.this::cleanup); + } + }; + + private static Set<String> getSameOemPackageCerts( + String packageName, String[] oemPackages, String[] sameOemCerts) { + Set<String> sameOemPackageCerts = new HashSet<>(); + + // Assume OEM may enter same package name in the parallel string array with + // multiple APK certs corresponding to it + for (int i = 0; i < oemPackages.length; i++) { + if (oemPackages[i].equals(packageName)) { + sameOemPackageCerts.add(sameOemCerts[i].replaceAll(":", "")); + } + } + + return sameOemPackageCerts; + } +} diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index a48172bff4df..ad4c35c2e755 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -19,8 +19,6 @@ package com.android.server.companion; import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES; import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED; -import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING; -import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH; import static android.companion.DeviceId.TYPE_MAC_ADDRESS; import static android.content.pm.PackageManager.CERT_INPUT_SHA256; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -31,7 +29,6 @@ import static com.android.internal.util.CollectionUtils.filter; import static com.android.internal.util.CollectionUtils.find; import static com.android.internal.util.CollectionUtils.forEach; import static com.android.internal.util.CollectionUtils.map; -import static com.android.internal.util.FunctionalUtils.uncheckExceptions; import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkState; @@ -39,13 +36,10 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; import static java.util.Collections.emptySet; -import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MINUTES; -import android.Manifest; -import android.annotation.CheckResult; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -64,10 +58,8 @@ import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.companion.Association; import android.companion.AssociationRequest; -import android.companion.CompanionDeviceManager; import android.companion.DeviceId; import android.companion.DeviceNotAssociatedException; -import android.companion.ICompanionDeviceDiscoveryService; import android.companion.ICompanionDeviceManager; import android.companion.IFindDeviceCallback; import android.content.BroadcastReceiver; @@ -81,14 +73,11 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.content.pm.Signature; import android.content.pm.UserInfo; import android.net.NetworkPolicyManager; import android.os.Binder; import android.os.Environment; import android.os.Handler; -import android.os.IBinder; -import android.os.IInterface; import android.os.Parcel; import android.os.PowerWhitelistManager; import android.os.Process; @@ -103,7 +92,6 @@ import android.text.BidiFormatter; import android.util.ArrayMap; import android.util.ArraySet; import android.util.ExceptionUtils; -import android.util.PackageUtils; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -111,9 +99,6 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsService; import com.android.internal.content.PackageMonitor; -import com.android.internal.infra.AndroidFuture; -import com.android.internal.infra.PerUser; -import com.android.internal.infra.ServiceConnector; import com.android.internal.notification.NotificationAccessConfirmationActivityContract; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; @@ -146,27 +131,13 @@ import java.util.function.Predicate; /** @hide */ @SuppressLint("LongLogTag") -public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient { +public class CompanionDeviceManagerService extends SystemService { static final String LOG_TAG = "CompanionDeviceManagerService"; static final boolean DEBUG = false; - private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION; - static { - final Map<String, String> map = new ArrayMap<>(); - map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH); - map.put(DEVICE_PROFILE_APP_STREAMING, - Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING); - - DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map); - } - /** Range of Association IDs allocated for a user.*/ static final int ASSOCIATIONS_IDS_PER_USER_RANGE = 100000; - private static final ComponentName SERVICE_TO_BIND_TO = ComponentName.createRelative( - CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, - ".CompanionDeviceDiscoveryService"); - private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000; private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000; @@ -177,9 +148,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private static final String PREF_FILE_NAME = "companion_device_preferences.xml"; private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done"; - private static final int ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW = 5; - private static final long ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS = 60 * 60 * 1000; // 60 min; - private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); static { sDateFormat.setTimeZone(TimeZone.getDefault()); @@ -188,19 +156,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private final CompanionDeviceManagerImpl mImpl; // Persistent data store for all Associations. private final PersistentDataStore mPersistentDataStore; + private final AssociationRequestsProcessor mAssociationRequestsProcessor; private PowerWhitelistManager mPowerWhitelistManager; - private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors; private IAppOpsService mAppOpsManager; private RoleManager mRoleManager; private BluetoothAdapter mBluetoothAdapter; private UserManager mUserManager; - private IFindDeviceCallback mFindDeviceCallback; private ScanCallback mBleScanCallback = new BleScanCallback(); - private AssociationRequest mRequest; - private String mCallingPackage; - private AndroidFuture<?> mOngoingDeviceDiscovery; - private PermissionControllerManager mPermissionControllerManager; + PermissionControllerManager mPermissionControllerManager; private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener = new BluetoothDeviceConnectedListener(); @@ -212,8 +176,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables = new ArrayMap<>(); - private final Object mLock = new Object(); - private final Handler mMainHandler = Handler.getMain(); + final Object mLock = new Object(); + final Handler mMainHandler = Handler.getMain(); private CompanionDevicePresenceController mCompanionDevicePresenceController; /** Maps a {@link UserIdInt} to a set of associations for the user. */ @@ -236,6 +200,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind super(context); mImpl = new CompanionDeviceManagerImpl(); mPersistentDataStore = new PersistentDataStore(); + mAssociationRequestsProcessor = new AssociationRequestsProcessor(this); mPowerWhitelistManager = context.getSystemService(PowerWhitelistManager.class); mRoleManager = context.getSystemService(RoleManager.class); @@ -249,17 +214,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind mUserManager = context.getSystemService(UserManager.class); mCompanionDevicePresenceController = new CompanionDevicePresenceController(); - Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO); - mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() { - @Override - protected ServiceConnector<ICompanionDeviceDiscoveryService> create(int userId) { - return new ServiceConnector.Impl<>( - getContext(), - serviceIntent, 0/* bindingFlags */, userId, - ICompanionDeviceDiscoveryService.Stub::asInterface); - } - }; - registerPackageMonitor(); } @@ -357,39 +311,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } - @Override - public void binderDied() { - Slog.w(LOG_TAG, "binderDied()"); - mMainHandler.post(this::cleanup); - } - - private void cleanup() { - Slog.d(LOG_TAG, "cleanup(); discovery = " - + mOngoingDeviceDiscovery + ", request = " + mRequest); - synchronized (mLock) { - AndroidFuture<?> ongoingDeviceDiscovery = mOngoingDeviceDiscovery; - if (ongoingDeviceDiscovery != null && !ongoingDeviceDiscovery.isDone()) { - ongoingDeviceDiscovery.cancel(true); - } - mFindDeviceCallback = unlinkToDeath(mFindDeviceCallback, this, 0); - mRequest = null; - mCallingPackage = null; - } - } - - /** - * Usage: {@code a = unlinkToDeath(a, deathRecipient, flags); } - */ - @Nullable - @CheckResult - private static <T extends IInterface> T unlinkToDeath(T iinterface, - IBinder.DeathRecipient deathRecipient, int flags) { - if (iinterface != null) { - iinterface.asBinder().unlinkToDeath(deathRecipient, flags); - } - return null; - } - class CompanionDeviceManagerImpl extends ICompanionDeviceManager.Stub { @Override @@ -410,61 +331,15 @@ public class CompanionDeviceManagerService extends SystemService implements Bind String callingPackage) throws RemoteException { Slog.i(LOG_TAG, "associate(request = " + request + ", callback = " + callback + ", callingPackage = " + callingPackage + ")"); - checkNotNull(request, "Request cannot be null"); - checkNotNull(callback, "Callback cannot be null"); - checkCallerIsSystemOr(callingPackage); - int userId = getCallingUserId(); - checkUsesFeature(callingPackage, userId); - final String deviceProfile = request.getDeviceProfile(); - validateDeviceProfileAndCheckPermission(deviceProfile); - - mFindDeviceCallback = callback; - mRequest = request; - mCallingPackage = callingPackage; - request.setCallingPackage(callingPackage); - - if (mayAssociateWithoutPrompt(callingPackage, userId)) { - Slog.i(LOG_TAG, "setSkipPrompt(true)"); - request.setSkipPrompt(true); - } - callback.asBinder().linkToDeath(CompanionDeviceManagerService.this /* recipient */, 0); - - mOngoingDeviceDiscovery = getDeviceProfilePermissionDescription(deviceProfile) - .thenComposeAsync(description -> { - Slog.d(LOG_TAG, "fetchProfileDescription done: " + description); - - request.setDeviceProfilePrivilegesDescription(description); - - return mServiceConnectors.forUser(userId).postAsync(service -> { - Slog.d(LOG_TAG, "Connected to CDM service; starting discovery for " + request); - - AndroidFuture<String> future = new AndroidFuture<>(); - service.startDiscovery(request, callingPackage, callback, future); - return future; - }).cancelTimeout(); - - }, FgThread.getExecutor()).whenComplete(uncheckExceptions((deviceAddress, err) -> { - if (err == null) { - createAssociationInternal( - userId, deviceAddress, callingPackage, deviceProfile); - } else { - Slog.e(LOG_TAG, "Failed to discover device(s)", err); - callback.onFailure("No devices found: " + err.getMessage()); - } - cleanup(); - })); + mAssociationRequestsProcessor.process(request, callback, callingPackage); } @Override public void stopScan(AssociationRequest request, IFindDeviceCallback callback, String callingPackage) { - Slog.d(LOG_TAG, "stopScan(request = " + request + ")"); - if (Objects.equals(request, mRequest) - && Objects.equals(callback, mFindDeviceCallback) - && Objects.equals(callingPackage, mCallingPackage)) { - cleanup(); - } + Slog.i(LOG_TAG, "stopScan(request = " + request + ")"); + mAssociationRequestsProcessor.stopScan(request, callback, callingPackage); } @Override @@ -505,44 +380,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind == PERMISSION_GRANTED; } - private void checkCallerIsSystemOr(String pkg) throws RemoteException { - checkCallerIsSystemOr(pkg, getCallingUserId()); - } - - private void checkCallerIsSystemOr(String pkg, int userId) throws RemoteException { - if (isCallerSystem()) { - return; - } - - checkArgument(getCallingUserId() == userId, - "Must be called by either same user or system"); - int callingUid = Binder.getCallingUid(); - if (mAppOpsManager.checkPackage(callingUid, pkg) != AppOpsManager.MODE_ALLOWED) { - throw new SecurityException(pkg + " doesn't belong to uid " + callingUid); - } - } - - private void validateDeviceProfileAndCheckPermission(@Nullable String deviceProfile) { - // Device profile can be null. - if (deviceProfile == null) return; - - if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) { - // TODO: remove, when properly supporting this profile. - throw new UnsupportedOperationException( - "DEVICE_PROFILE_APP_STREAMING is not fully supported yet."); - } - - if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) { - throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile); - } - - final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile); - if (getContext().checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) { - throw new SecurityException("Application must hold " + permission + " to associate " - + "with a device with " + deviceProfile + " profile."); - } - } - @Override public PendingIntent requestNotificationAccess(ComponentName component) throws RemoteException { @@ -674,23 +511,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind checkUsesFeature(callingPackage, userId); } - private void checkUsesFeature(String pkg, int userId) { - if (isCallerSystem()) { - // Drop the requirement for calls from system process - return; - } - - FeatureInfo[] reqFeatures = getPackageInfo(pkg, userId).reqFeatures; - String requiredFeature = PackageManager.FEATURE_COMPANION_DEVICE_SETUP; - int numFeatures = ArrayUtils.size(reqFeatures); - for (int i = 0; i < numFeatures; i++) { - if (requiredFeature.equals(reqFeatures[i].name)) return; - } - throw new IllegalStateException("Must declare uses-feature " - + requiredFeature - + " in manifest to use this API"); - } - @Override public boolean canPairWithoutPrompt( String packageName, String deviceMacAddress, int userId) { @@ -740,14 +560,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind .append(sDateFormat.format(time)).append('\n'); } - fout.append("Discovery Service State:").append('\n'); - for (int i = 0, size = mServiceConnectors.size(); i < size; i++) { - int userId = mServiceConnectors.keyAt(i); - fout.append(" ") - .append("u").append(Integer.toString(userId)).append(": ") - .append(Objects.toString(mServiceConnectors.valueAt(i))) - .append('\n'); - } + mAssociationRequestsProcessor.dump(fout); fout.append("Device Listener Services State:").append('\n'); for (int i = 0, size = mCompanionDevicePresenceController.mBoundServices.size(); @@ -762,7 +575,24 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } - private static int getCallingUserId() { + void checkCallerIsSystemOr(String pkg) throws RemoteException { + checkCallerIsSystemOr(pkg, getCallingUserId()); + } + + private void checkCallerIsSystemOr(String pkg, int userId) throws RemoteException { + if (isCallerSystem()) { + return; + } + + checkArgument(getCallingUserId() == userId, + "Must be called by either same user or system"); + int callingUid = Binder.getCallingUid(); + if (mAppOpsManager.checkPackage(callingUid, pkg) != AppOpsManager.MODE_ALLOWED) { + throw new SecurityException(pkg + " doesn't belong to uid " + callingUid); + } + } + + static int getCallingUserId() { return UserHandle.getUserId(Binder.getCallingUid()); } @@ -770,6 +600,23 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return Binder.getCallingUid() == Process.SYSTEM_UID; } + void checkUsesFeature(String pkg, int userId) { + if (isCallerSystem()) { + // Drop the requirement for calls from system process + return; + } + + FeatureInfo[] reqFeatures = getPackageInfo(pkg, userId).reqFeatures; + String requiredFeature = PackageManager.FEATURE_COMPANION_DEVICE_SETUP; + int numFeatures = ArrayUtils.size(reqFeatures); + for (int i = 0; i < numFeatures; i++) { + if (requiredFeature.equals(reqFeatures[i].name)) return; + } + throw new IllegalStateException("Must declare uses-feature " + + requiredFeature + + " in manifest to use this API"); + } + void createAssociationInternal( int userId, String deviceMacAddress, String packageName, String deviceProfile) { final Association association = new Association( @@ -950,72 +797,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } - private Set<String> getSameOemPackageCerts( - String packageName, String[] oemPackages, String[] sameOemCerts) { - Set<String> sameOemPackageCerts = new HashSet<>(); - - // Assume OEM may enter same package name in the parallel string array with - // multiple APK certs corresponding to it - for (int i = 0; i < oemPackages.length; i++) { - if (oemPackages[i].equals(packageName)) { - sameOemPackageCerts.add(sameOemCerts[i].replaceAll(":", "")); - } - } - - return sameOemPackageCerts; - } - - boolean mayAssociateWithoutPrompt(String packageName, int userId) { - String[] sameOemPackages = getContext() - .getResources() - .getStringArray(com.android.internal.R.array.config_companionDevicePackages); - if (!ArrayUtils.contains(sameOemPackages, packageName)) { - Slog.w(LOG_TAG, packageName - + " can not silently create associations due to no package found." - + " Packages from OEM: " + Arrays.toString(sameOemPackages) - ); - return false; - } - - // Throttle frequent associations - long now = System.currentTimeMillis(); - Set<Association> recentAssociations = filter( - getAllAssociations(userId, packageName), - a -> now - a.getTimeApprovedMs() < ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS); - - if (recentAssociations.size() >= ASSOCIATE_WITHOUT_PROMPT_MAX_PER_TIME_WINDOW) { - Slog.w(LOG_TAG, "Too many associations. " + packageName - + " already associated " + recentAssociations.size() - + " devices within the last " + ASSOCIATE_WITHOUT_PROMPT_WINDOW_MS - + "ms: " + recentAssociations); - return false; - } - String[] sameOemCerts = getContext() - .getResources() - .getStringArray(com.android.internal.R.array.config_companionDeviceCerts); - - Signature[] signatures = mPackageManagerInternal - .getPackage(packageName).getSigningDetails().getSignatures(); - String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures); - - Set<String> sameOemPackageCerts = - getSameOemPackageCerts(packageName, sameOemPackages, sameOemCerts); - - for (String cert : apkCerts) { - if (sameOemPackageCerts.contains(cert)) { - return true; - } - } - - Slog.w(LOG_TAG, packageName - + " can not silently create associations. " + packageName - + " has SHA256 certs from APK: " + Arrays.toString(apkCerts) - + " and from OEM: " + Arrays.toString(sameOemCerts) - ); - - return false; - } - private static <T> boolean containsEither(T[] array, T a, T b) { return ArrayUtils.contains(array, a) || ArrayUtils.contains(array, b); } @@ -1120,7 +901,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } - private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) { + Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) { return filter( getAllAssociations(userId), // Null filter == get all associations @@ -1444,25 +1225,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind return result; } - @NonNull - private AndroidFuture<String> getDeviceProfilePermissionDescription( - @Nullable String deviceProfile) { - if (deviceProfile == null) { - return AndroidFuture.completedFuture(null); - } - - final AndroidFuture<String> result = new AndroidFuture<>(); - mPermissionControllerManager.getPrivilegesDescriptionStringForProfile( - deviceProfile, FgThread.getExecutor(), desc -> { - try { - result.complete(String.valueOf(desc)); - } catch (Exception e) { - result.completeExceptionally(e); - } - }); - return result; - } - static int getFirstAssociationIdForUser(@UserIdInt int userId) { // We want the IDs to start from 1, not 0. return userId * ASSOCIATIONS_IDS_PER_USER_RANGE + 1; diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 92e0845012de..f57a852fe8c5 100755 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -1075,17 +1075,22 @@ class TvInputHardwareManager implements TvInputHal.Callback { } if (shouldRecreateAudioPatch) { mCommittedVolume = volume; - if (mAudioPatch != null) { - mAudioManager.releaseAudioPatch(mAudioPatch); - } - mAudioManager.createAudioPatch( + // only recreate if something was updated or audioPath is null + if (mAudioPatch == null || sinkUpdated ||sourceUpdated ) { + if (mAudioPatch != null) { + mAudioManager.releaseAudioPatch(mAudioPatch); + audioPatchArray[0] = null; + } + mAudioManager.createAudioPatch( audioPatchArray, new AudioPortConfig[] { sourceConfig }, sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()])); - mAudioPatch = audioPatchArray[0]; - if (sourceGainConfig != null) { - mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig); + mAudioPatch = audioPatchArray[0]; } + } + + if (sourceGainConfig != null) { + mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java index fb5c557c8d4b..96af61737bff 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java @@ -100,7 +100,11 @@ public class FullScreenMagnificationControllerTest { MagnificationAnimationCallback.class); private final MagnificationInfoChangedCallback mRequestObserver = mock( MagnificationInfoChangedCallback.class); - final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null); + private final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler( + null); + private final MagnificationScaleProvider mScaleProvider = mock( + MagnificationScaleProvider.class); + ValueAnimator mMockValueAnimator; ValueAnimator.AnimatorUpdateListener mTargetAnimationListener; @@ -123,7 +127,7 @@ public class FullScreenMagnificationControllerTest { initMockWindowManager(); mFullScreenMagnificationController = new FullScreenMagnificationController( - mMockControllerCtx, new Object(), mRequestObserver); + mMockControllerCtx, new Object(), mRequestObserver, mScaleProvider); } @After @@ -412,12 +416,12 @@ public class FullScreenMagnificationControllerTest { MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId); PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER; PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, - FullScreenMagnificationController.MAX_SCALE); + MagnificationScaleProvider.MAX_SCALE); MagnificationSpec endSpec = getMagnificationSpec( - FullScreenMagnificationController.MAX_SCALE, offsets); + MagnificationScaleProvider.MAX_SCALE, offsets); assertTrue(mFullScreenMagnificationController.setScaleAndCenter(displayId, - FullScreenMagnificationController.MAX_SCALE + 1.0f, + MagnificationScaleProvider.MAX_SCALE + 1.0f, newCenter.x, newCenter.y, false, SERVICE_ID_1)); mMessageCapturingHandler.sendAllMessages(); @@ -632,31 +636,6 @@ public class FullScreenMagnificationControllerTest { } @Test - public void testSetUserId_resetsOnlyIfIdChanges() { - for (int i = 0; i < DISPLAY_COUNT; i++) { - testSetUserId_resetsOnlyIfIdChanges(i); - resetMockWindowManager(); - } - } - - private void testSetUserId_resetsOnlyIfIdChanges(int displayId) { - final int userId1 = 1; - final int userId2 = 2; - - register(displayId); - mFullScreenMagnificationController.setUserId(userId1); - PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER; - float scale = 2.0f; - mFullScreenMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, - false, SERVICE_ID_1); - - mFullScreenMagnificationController.setUserId(userId1); - assertTrue(mFullScreenMagnificationController.isMagnifying(displayId)); - mFullScreenMagnificationController.setUserId(userId2); - assertFalse(mFullScreenMagnificationController.isMagnifying(displayId)); - } - - @Test public void testResetIfNeeded_doesWhatItSays() { for (int i = 0; i < DISPLAY_COUNT; i++) { testResetIfNeeded_doesWhatItSays(i); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index 6c32f7e8bacb..2060223f6f98 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -22,6 +22,8 @@ import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static com.android.server.testutils.TestUtils.strictMock; import static org.junit.Assert.assertFalse; @@ -38,16 +40,15 @@ import static org.mockito.Mockito.when; import android.animation.ValueAnimator; import android.annotation.NonNull; -import android.content.Context; import android.graphics.PointF; import android.os.Handler; import android.os.Message; +import android.testing.TestableContext; import android.util.DebugUtils; import android.view.InputDevice; import android.view.MotionEvent; import android.view.ViewConfiguration; -import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.AccessibilityManagerService; @@ -60,6 +61,7 @@ import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -121,7 +123,6 @@ public class FullScreenMagnificationGestureHandlerTest { private static final int DISPLAY_0 = 0; - private Context mContext; FullScreenMagnificationController mFullScreenMagnificationController; @Mock MagnificationGestureHandler.Callback mMockCallback; @@ -134,6 +135,9 @@ public class FullScreenMagnificationGestureHandlerTest { @Mock AccessibilityTraceManager mMockTraceManager; + @Rule + public final TestableContext mContext = new TestableContext(getInstrumentation().getContext()); + private OffsettableClock mClock; private FullScreenMagnificationGestureHandler mMgh; private TestHandler mHandler; @@ -143,7 +147,6 @@ public class FullScreenMagnificationGestureHandlerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mContext = InstrumentationRegistry.getContext(); final FullScreenMagnificationController.ControllerContext mockController = mock(FullScreenMagnificationController.ControllerContext.class); final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class); @@ -157,14 +160,16 @@ public class FullScreenMagnificationGestureHandlerTest { when(mockController.getAnimationDuration()).thenReturn(1000L); when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true); mFullScreenMagnificationController = new FullScreenMagnificationController(mockController, - new Object(), mMagnificationInfoChangedCallback) { + new Object(), mMagnificationInfoChangedCallback, + new MagnificationScaleProvider(mContext)) { @Override public boolean magnificationRegionContains(int displayId, float x, float y) { return true; } @Override - void setForceShowMagnifiableBounds(int displayId, boolean show) {} + void setForceShowMagnifiableBounds(int displayId, boolean show) { + } }; mFullScreenMagnificationController.register(DISPLAY_0); mClock = new OffsettableClock.Stopped(); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index 2cb3d27229bc..69061c14c70e 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -46,6 +46,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.test.mock.MockContentResolver; +import android.testing.DexmakerShareClassLoaderRule; import android.view.Display; import android.view.accessibility.IRemoteMagnificationAnimationCallback; import android.view.accessibility.MagnificationAnimationCallback; @@ -58,6 +59,7 @@ import com.android.server.accessibility.AccessibilityTraceManager; 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; @@ -79,7 +81,8 @@ public class MagnificationControllerTest { private static final float MAGNIFIED_CENTER_X = 100; private static final float MAGNIFIED_CENTER_Y = 200; private static final float DEFAULT_SCALE = 3f; - private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT; + private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM; + private static final int SECOND_USER_ID = CURRENT_USER_ID + 1; private static final int MODE_WINDOW = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; private static final int MODE_FULLSCREEN = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; @@ -94,6 +97,7 @@ public class MagnificationControllerTest { private Context mContext; @Mock private FullScreenMagnificationController mScreenMagnificationController; + private MagnificationScaleProvider mScaleProvider; @Captor private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor; @@ -103,6 +107,11 @@ public class MagnificationControllerTest { private MagnificationController mMagnificationController; private FullScreenMagnificationControllerStubber mScreenMagnificationControllerStubber; + // To mock package-private class + @Rule + public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = + new DexmakerShareClassLoaderRule(); + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -113,15 +122,17 @@ public class MagnificationControllerTest { Settings.Secure.putFloatForUser(mMockResolver, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE, CURRENT_USER_ID); + mScaleProvider = spy(new MagnificationScaleProvider(mContext)); mWindowMagnificationManager = Mockito.spy( new WindowMagnificationManager(mContext, CURRENT_USER_ID, - mock(WindowMagnificationManager.Callback.class), mTraceManager)); + mock(WindowMagnificationManager.Callback.class), mTraceManager, + mScaleProvider)); mMockConnection = new MockWindowMagnificationConnection(true); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber( mScreenMagnificationController); mMagnificationController = spy(new MagnificationController(mService, new Object(), mContext, - mScreenMagnificationController, mWindowMagnificationManager)); + mScreenMagnificationController, mWindowMagnificationManager, mScaleProvider)); mMagnificationController.setMagnificationCapabilities( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); @@ -283,14 +294,16 @@ public class MagnificationControllerTest { verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY); verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY); + verify(mScaleProvider).onDisplayRemoved(TEST_DISPLAY); } @Test - public void updateUserIdIfNeeded_AllModulesAvailable_setUserId() { - mMagnificationController.updateUserIdIfNeeded(CURRENT_USER_ID); + public void updateUserIdIfNeeded_AllModulesAvailable_disableMagnificationAndChangeUserId() { + mMagnificationController.updateUserIdIfNeeded(SECOND_USER_ID); - verify(mScreenMagnificationController).setUserId(CURRENT_USER_ID); - verify(mWindowMagnificationManager).setUserId(CURRENT_USER_ID); + verify(mScreenMagnificationController).resetAllIfNeeded(false); + verify(mWindowMagnificationManager).disableAllWindowMagnifiers(); + verify(mScaleProvider).onUserChanged(SECOND_USER_ID); } @Test @@ -575,6 +588,13 @@ public class MagnificationControllerTest { verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt()); } + @Test + public void onUserRemoved_notifyScaleProvider() { + mMagnificationController.onUserRemoved(SECOND_USER_ID); + + verify(mScaleProvider).onUserRemoved(SECOND_USER_ID); + } + private void setMagnificationEnabled(int mode) throws RemoteException { setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y); } @@ -627,7 +647,8 @@ public class MagnificationControllerTest { TEST_DISPLAY); doAnswer(invocation -> mIsMagnifying).when( mScreenMagnificationController).isForceShowMagnifiableBounds(TEST_DISPLAY); - doAnswer(invocation -> mScale).when(mScreenMagnificationController).getPersistedScale(); + doAnswer(invocation -> mScale).when(mScreenMagnificationController).getPersistedScale( + TEST_DISPLAY); doAnswer(invocation -> mScale).when(mScreenMagnificationController).getScale( TEST_DISPLAY); doAnswer(invocation -> mCenterX).when(mScreenMagnificationController).getCenterX( diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationScaleProviderTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationScaleProviderTest.java new file mode 100644 index 000000000000..9b392b200821 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationScaleProviderTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility.magnification; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertEquals; + +import android.os.UserHandle; +import android.testing.TestableContext; +import android.view.Display; + +import com.android.compatibility.common.util.TestUtils; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +/** + * Tests for {@link MagnificationScaleProvider}. + */ +public class MagnificationScaleProviderTest { + + private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY + 1; + private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM; + private static final int SECOND_USER_ID = CURRENT_USER_ID + 1; + + private static final float TEST_SCALE = 3; + private static final float DEFAULT_SCALE = + MagnificationScaleProvider.DEFAULT_MAGNIFICATION_SCALE; + + @Rule + public final TestableContext mContext = new TestableContext(getInstrumentation().getContext()); + + private MagnificationScaleProvider mScaleProvider; + + @Before + public void setUp() { + mScaleProvider = new MagnificationScaleProvider(mContext); + } + + @Test + public void putScaleOnDefaultDisplay_getExpectedValue() throws Exception { + mScaleProvider.putScale(TEST_SCALE, Display.DEFAULT_DISPLAY); + + TestUtils.waitUntil("settings value is not changed", + () -> Float.compare(mScaleProvider.getScale(Display.DEFAULT_DISPLAY), + TEST_SCALE) == 0); + } + + @Test + public void putScaleOnTestDisplay_getExpectedValue() { + mScaleProvider.putScale(TEST_SCALE, TEST_DISPLAY); + + assertEquals(TEST_SCALE, mScaleProvider.getScale(TEST_DISPLAY), 0); + } + + @Test + public void onUserChanged_putScale_fallbackToDefaultScale() { + mScaleProvider.putScale(TEST_SCALE, TEST_DISPLAY); + + mScaleProvider.onUserChanged(SECOND_USER_ID); + assertEquals(DEFAULT_SCALE, mScaleProvider.getScale(TEST_DISPLAY), 0); + } + + @Test + public void onUserRemoved_setScaleOnSecondUser_fallbackToDefaultScale() { + mScaleProvider.onUserChanged(SECOND_USER_ID); + mScaleProvider.putScale(TEST_SCALE, TEST_DISPLAY); + mScaleProvider.onUserChanged(CURRENT_USER_ID); + + mScaleProvider.onUserRemoved(SECOND_USER_ID); + // Assume the second user is created with the same id + mScaleProvider.onUserChanged(SECOND_USER_ID); + + assertEquals(DEFAULT_SCALE, mScaleProvider.getScale(TEST_DISPLAY), 0); + } + + @Test + public void onTestDisplayRemoved_setScaleOnTestDisplay_fallbackToDefaultScale() { + mScaleProvider.putScale(TEST_SCALE, TEST_DISPLAY); + + mScaleProvider.onDisplayRemoved(TEST_DISPLAY); + + assertEquals(DEFAULT_SCALE, mScaleProvider.getScale(TEST_DISPLAY), 0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index 95f43275376e..1b8aff50d2e2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -21,10 +21,10 @@ import static com.android.server.testutils.TestUtils.strictMock; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; -import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.os.RemoteException; +import android.testing.TestableContext; import android.util.DebugUtils; import android.view.InputDevice; import android.view.MotionEvent; @@ -39,6 +39,7 @@ import com.android.server.accessibility.utils.TouchEventGenerator; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -67,7 +68,10 @@ public class WindowMagnificationGestureHandlerTest { public static final float DEFAULT_TAP_Y = 299; private static final int DISPLAY_0 = MockWindowMagnificationConnection.TEST_DISPLAY; - private Context mContext; + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getContext()); + private WindowMagnificationManager mWindowMagnificationManager; private MockWindowMagnificationConnection mMockConnection; private WindowMagnificationGestureHandler mWindowMagnificationGestureHandler; @@ -79,9 +83,9 @@ public class WindowMagnificationGestureHandlerTest { @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); - mContext = InstrumentationRegistry.getInstrumentation().getContext(); mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0, - mock(WindowMagnificationManager.Callback.class), mMockTrace); + mock(WindowMagnificationManager.Callback.class), mMockTrace, + new MagnificationScaleProvider(mContext)); mMockConnection = new MockWindowMagnificationConnection(); mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler( mContext, mWindowMagnificationManager, mMockTrace, mMockCallback, diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index af6d40f2fdf2..da881c4e6494 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -67,7 +67,7 @@ import org.mockito.invocation.InvocationOnMock; public class WindowMagnificationManagerTest { private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; - private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT; + private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM; private MockWindowMagnificationConnection mMockConnection; @Mock @@ -91,7 +91,7 @@ public class WindowMagnificationManagerTest { mResolver = new MockContentResolver(); mMockConnection = new MockWindowMagnificationConnection(); mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID, - mMockCallback, mMockTrace); + mMockCallback, mMockTrace, new MagnificationScaleProvider(mContext)); when(mContext.getContentResolver()).thenReturn(mResolver); doAnswer((InvocationOnMock invocation) -> { @@ -230,7 +230,7 @@ public class WindowMagnificationManagerTest { public void getPersistedScale() { mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - assertEquals(mWindowMagnificationManager.getPersistedScale(), 2.5f); + assertEquals(mWindowMagnificationManager.getPersistedScale(TEST_DISPLAY), 2.5f); } @Test @@ -264,7 +264,7 @@ public class WindowMagnificationManagerTest { mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f); assertEquals(mWindowMagnificationManager.getScale(TEST_DISPLAY), - WindowMagnificationManager.MAX_SCALE); + MagnificationScaleProvider.MAX_SCALE); } @Test |