1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.accessibility.magnification; 18 19 import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION; 20 import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION_CALLBACK; 21 import static android.os.Build.HW_TIMEOUT_MULTIPLIER; 22 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; 23 24 import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID; 25 import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; 26 27 import android.annotation.IntDef; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.RequiresNoPermission; 31 import android.content.BroadcastReceiver; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.graphics.PointF; 36 import android.graphics.Rect; 37 import android.graphics.Region; 38 import android.os.Binder; 39 import android.os.IBinder; 40 import android.os.RemoteException; 41 import android.os.SystemClock; 42 import android.util.MathUtils; 43 import android.util.Slog; 44 import android.util.SparseArray; 45 import android.util.SparseBooleanArray; 46 import android.view.MotionEvent; 47 import android.view.accessibility.IMagnificationConnection; 48 import android.view.accessibility.IMagnificationConnectionCallback; 49 import android.view.accessibility.MagnificationAnimationCallback; 50 51 import com.android.internal.accessibility.common.MagnificationConstants; 52 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; 53 import com.android.internal.annotations.GuardedBy; 54 import com.android.internal.annotations.VisibleForTesting; 55 import com.android.server.LocalServices; 56 import com.android.server.accessibility.AccessibilityTraceManager; 57 import com.android.server.statusbar.StatusBarManagerInternal; 58 import com.android.server.wm.WindowManagerInternal; 59 60 import java.lang.annotation.Retention; 61 import java.lang.annotation.RetentionPolicy; 62 import java.util.concurrent.atomic.AtomicLongFieldUpdater; 63 64 /** 65 * A class to manipulate magnification through {@link MagnificationConnectionWrapper} 66 * create by {@link #setConnection(IMagnificationConnection)}. To set the connection with 67 * SysUI, call {@code StatusBarManagerInternal#requestMagnificationConnection(boolean)}. 68 * The applied magnification scale is constrained by 69 * {@link MagnificationScaleProvider#constrainScale(float)} 70 */ 71 public class MagnificationConnectionManager implements 72 PanningScalingHandler.MagnificationDelegate, 73 WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks { 74 75 private static final boolean DBG = false; 76 77 private static final String TAG = "MagnificationConnectionManager"; 78 79 /** 80 * Indicate that the magnification window is at the magnification center. 81 */ 82 public static final int WINDOW_POSITION_AT_CENTER = 0; 83 84 /** 85 * Indicate that the magnification window is at the top-left side of the magnification 86 * center. The offset is equal to a half of MirrorSurfaceView. So, the bottom-right corner 87 * of the window is at the magnification center. 88 */ 89 public static final int WINDOW_POSITION_AT_TOP_LEFT = 1; 90 91 @Retention(RetentionPolicy.SOURCE) 92 @IntDef(prefix = { "WINDOW_POSITION_AT_" }, value = { 93 WINDOW_POSITION_AT_CENTER, 94 WINDOW_POSITION_AT_TOP_LEFT 95 }) 96 public @interface WindowPosition {} 97 98 /** Magnification connection is connecting. */ 99 private static final int CONNECTING = 0; 100 /** Magnification connection is connected. */ 101 private static final int CONNECTED = 1; 102 /** Magnification connection is disconnecting. */ 103 private static final int DISCONNECTING = 2; 104 /** Magnification connection is disconnected. */ 105 private static final int DISCONNECTED = 3; 106 107 @Retention(RetentionPolicy.SOURCE) 108 @IntDef(prefix = {"CONNECTION_STATE"}, value = { 109 CONNECTING, 110 CONNECTED, 111 DISCONNECTING, 112 DISCONNECTED 113 }) 114 private @interface ConnectionState { 115 } 116 connectionStateToString(@onnectionState int state)117 private static String connectionStateToString(@ConnectionState int state) { 118 switch (state) { 119 case CONNECTING: return "CONNECTING"; 120 case CONNECTED: return "CONNECTED"; 121 case DISCONNECTING: return "DISCONNECTING"; 122 case DISCONNECTED: return "DISCONNECTED"; 123 default: 124 return "UNKNOWN:" + state; 125 } 126 } 127 128 @ConnectionState 129 private int mConnectionState = DISCONNECTED; 130 131 private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 200 * HW_TIMEOUT_MULTIPLIER; 132 133 private final Object mLock; 134 private final Context mContext; 135 @VisibleForTesting 136 @GuardedBy("mLock") 137 @Nullable 138 MagnificationConnectionWrapper mConnectionWrapper; 139 @GuardedBy("mLock") 140 private ConnectionCallback mConnectionCallback; 141 @GuardedBy("mLock") 142 private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>(); 143 // Whether the following typing focus feature for magnification is enabled. 144 private boolean mMagnificationFollowTypingEnabled = true; 145 @GuardedBy("mLock") 146 private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray(); 147 @GuardedBy("mLock") 148 private final SparseArray<Float> mLastActivatedScale = new SparseArray<>(); 149 150 private boolean mReceiverRegistered = false; 151 @VisibleForTesting 152 protected final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() { 153 @Override 154 public void onReceive(Context context, Intent intent) { 155 final int displayId = context.getDisplayId(); 156 removeMagnificationButton(displayId); 157 disableWindowMagnification(displayId, false, null); 158 } 159 }; 160 161 /** 162 * Callback to handle magnification actions from system UI. 163 */ 164 public interface Callback { 165 166 /** 167 * Called when the accessibility action of scale requests to be performed. 168 * It is invoked from System UI. And the action is provided by the mirror window. 169 * 170 * @param displayId The logical display id. 171 * @param scale the target scale, or {@link Float#NaN} to leave unchanged 172 * @param updatePersistence whether the scale should be persisted 173 */ onPerformScaleAction(int displayId, float scale, boolean updatePersistence)174 void onPerformScaleAction(int displayId, float scale, boolean updatePersistence); 175 176 /** 177 * Called when the accessibility action is performed. 178 * 179 * @param displayId The logical display id. 180 */ onAccessibilityActionPerformed(int displayId)181 void onAccessibilityActionPerformed(int displayId); 182 183 /** 184 * Called when the state of the magnification activation is changed. 185 * 186 * @param displayId The logical display id. 187 * @param activated {@code true} if the magnification is activated, otherwise {@code false}. 188 */ onWindowMagnificationActivationState(int displayId, boolean activated)189 void onWindowMagnificationActivationState(int displayId, boolean activated); 190 191 /** 192 * Called when the magnification source bounds are changed. 193 * 194 * @param displayId The logical display id. 195 * @param bounds The magnified source bounds on the display. 196 */ onSourceBoundsChanged(int displayId, Rect bounds)197 void onSourceBoundsChanged(int displayId, Rect bounds); 198 199 /** 200 * Called from {@link IMagnificationConnection} to request changing the magnification 201 * mode on the given display. 202 * 203 * @param displayId the logical display id 204 * @param magnificationMode the target magnification mode 205 */ onChangeMagnificationMode(int displayId, int magnificationMode)206 void onChangeMagnificationMode(int displayId, int magnificationMode); 207 } 208 209 private final Callback mCallback; 210 private final AccessibilityTraceManager mTrace; 211 private final MagnificationScaleProvider mScaleProvider; 212 MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback, AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider)213 public MagnificationConnectionManager(Context context, Object lock, @NonNull Callback callback, 214 AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) { 215 mContext = context; 216 mLock = lock; 217 mCallback = callback; 218 mTrace = trace; 219 mScaleProvider = scaleProvider; 220 } 221 222 /** 223 * Sets {@link IMagnificationConnection}. 224 * 225 * @param connection {@link IMagnificationConnection} 226 */ setConnection(@ullable IMagnificationConnection connection)227 public void setConnection(@Nullable IMagnificationConnection connection) { 228 if (DBG) { 229 Slog.d(TAG, "setConnection :" + connection + ", mConnectionState=" 230 + connectionStateToString(mConnectionState)); 231 } 232 synchronized (mLock) { 233 // Reset connectionWrapper. 234 if (mConnectionWrapper != null) { 235 mConnectionWrapper.setConnectionCallback(null); 236 if (mConnectionCallback != null) { 237 mConnectionCallback.mExpiredDeathRecipient = true; 238 } 239 mConnectionWrapper.unlinkToDeath(mConnectionCallback); 240 mConnectionWrapper = null; 241 // The connection is still connecting so it is no need to reset the 242 // connection state to disconnected. 243 // TODO b/220086369 will reset the connection immediately when requestConnection 244 // is called 245 if (mConnectionState != CONNECTING) { 246 setConnectionState(DISCONNECTED); 247 } 248 } 249 if (connection != null) { 250 mConnectionWrapper = new MagnificationConnectionWrapper(connection, mTrace); 251 } 252 253 if (mConnectionWrapper != null) { 254 try { 255 mConnectionCallback = new ConnectionCallback(); 256 mConnectionWrapper.linkToDeath(mConnectionCallback); 257 mConnectionWrapper.setConnectionCallback(mConnectionCallback); 258 setConnectionState(CONNECTED); 259 } catch (RemoteException e) { 260 Slog.e(TAG, "setConnection failed", e); 261 mConnectionWrapper = null; 262 setConnectionState(DISCONNECTED); 263 } finally { 264 mLock.notify(); 265 } 266 } 267 } 268 } 269 270 /** 271 * @return {@code true} if {@link IMagnificationConnection} is available 272 */ isConnected()273 public boolean isConnected() { 274 synchronized (mLock) { 275 return mConnectionWrapper != null; 276 } 277 } 278 279 /** 280 * Requests {@link IMagnificationConnection} through 281 * {@link StatusBarManagerInternal#requestMagnificationConnection(boolean)} and 282 * destroys all window magnifications if necessary. 283 * 284 * @param connect {@code true} if needs connection, otherwise set the connection to null and 285 * destroy all window magnifications. 286 * @return {@code true} if {@link IMagnificationConnection} state is going to change. 287 */ requestConnection(boolean connect)288 public boolean requestConnection(boolean connect) { 289 if (DBG) { 290 Slog.d(TAG, "requestConnection :" + connect); 291 } 292 if (mTrace.isA11yTracingEnabledForTypes(FLAGS_MAGNIFICATION_CONNECTION)) { 293 mTrace.logTrace(TAG + ".requestMagnificationConnection", 294 FLAGS_MAGNIFICATION_CONNECTION, "connect=" + connect); 295 } 296 synchronized (mLock) { 297 if ((connect && (mConnectionState == CONNECTED || mConnectionState == CONNECTING)) 298 || (!connect && (mConnectionState == DISCONNECTED 299 || mConnectionState == DISCONNECTING))) { 300 Slog.w(TAG, "requestConnection duplicated request: connect=" + connect 301 + ", mConnectionState=" + connectionStateToString(mConnectionState)); 302 return false; 303 } 304 305 if (connect) { 306 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 307 if (!mReceiverRegistered) { 308 mContext.registerReceiver(mScreenStateReceiver, intentFilter); 309 mReceiverRegistered = true; 310 } 311 } else { 312 disableAllWindowMagnifiers(); 313 if (mReceiverRegistered) { 314 mContext.unregisterReceiver(mScreenStateReceiver); 315 mReceiverRegistered = false; 316 } 317 } 318 } 319 if (requestConnectionInternal(connect)) { 320 setConnectionState(connect ? CONNECTING : DISCONNECTING); 321 return true; 322 } else { 323 setConnectionState(DISCONNECTED); 324 return false; 325 } 326 } 327 requestConnectionInternal(boolean connect)328 private boolean requestConnectionInternal(boolean connect) { 329 final long identity = Binder.clearCallingIdentity(); 330 try { 331 final StatusBarManagerInternal service = LocalServices.getService( 332 StatusBarManagerInternal.class); 333 if (service != null) { 334 return service.requestMagnificationConnection(connect); 335 } 336 } finally { 337 Binder.restoreCallingIdentity(identity); 338 } 339 return false; 340 } 341 342 /** 343 * Returns window magnification connection state. 344 */ getConnectionState()345 public String getConnectionState() { 346 return connectionStateToString(mConnectionState); 347 } 348 setConnectionState(@onnectionState int state)349 private void setConnectionState(@ConnectionState int state) { 350 if (DBG) { 351 Slog.d(TAG, "setConnectionState : state=" + state + ", mConnectionState=" 352 + connectionStateToString(mConnectionState)); 353 } 354 mConnectionState = state; 355 } 356 357 /** 358 * Disables window magnifier on all displays without animation. 359 */ disableAllWindowMagnifiers()360 void disableAllWindowMagnifiers() { 361 synchronized (mLock) { 362 for (int i = 0; i < mWindowMagnifiers.size(); i++) { 363 final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); 364 magnifier.disableWindowMagnificationInternal(null); 365 } 366 mWindowMagnifiers.clear(); 367 } 368 } 369 370 /** 371 * Resets the window magnifier on all displays that had been controlled by the 372 * specified service connection. Called when the service connection is unbound 373 * or binder died. 374 * 375 * @param connectionId The connection id 376 */ resetAllIfNeeded(int connectionId)377 public void resetAllIfNeeded(int connectionId) { 378 synchronized (mLock) { 379 for (int i = 0; i < mWindowMagnifiers.size(); i++) { 380 final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); 381 if (magnifier != null 382 && magnifier.mEnabled 383 && connectionId == magnifier.getIdOfLastServiceToControl()) { 384 magnifier.disableWindowMagnificationInternal(null); 385 } 386 } 387 } 388 } 389 resetWindowMagnifiers()390 private void resetWindowMagnifiers() { 391 synchronized (mLock) { 392 for (int i = 0; i < mWindowMagnifiers.size(); i++) { 393 WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); 394 magnifier.reset(); 395 } 396 } 397 } 398 399 @Override onRectangleOnScreenRequested(int displayId, int left, int top, int right, int bottom)400 public void onRectangleOnScreenRequested(int displayId, int left, int top, int right, 401 int bottom) { 402 if (!mMagnificationFollowTypingEnabled) { 403 return; 404 } 405 406 float toCenterX = (float) (left + right) / 2; 407 float toCenterY = (float) (top + bottom) / 2; 408 409 synchronized (mLock) { 410 if (mIsImeVisibleArray.get(displayId, false) 411 && !isPositionInSourceBounds(displayId, toCenterX, toCenterY) 412 && isTrackingTypingFocusEnabled(displayId)) { 413 moveWindowMagnifierToPositionInternal(displayId, toCenterX, toCenterY, 414 STUB_ANIMATION_CALLBACK); 415 } 416 } 417 } 418 setMagnificationFollowTypingEnabled(boolean enabled)419 void setMagnificationFollowTypingEnabled(boolean enabled) { 420 mMagnificationFollowTypingEnabled = enabled; 421 } 422 isMagnificationFollowTypingEnabled()423 boolean isMagnificationFollowTypingEnabled() { 424 return mMagnificationFollowTypingEnabled; 425 } 426 427 /** 428 * Get the ID of the last service that changed the magnification config. 429 * 430 * @param displayId The logical display id. 431 * @return The id 432 */ getIdOfLastServiceToMagnify(int displayId)433 public int getIdOfLastServiceToMagnify(int displayId) { 434 synchronized (mLock) { 435 final WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 436 if (magnifier != null) { 437 return magnifier.mIdOfLastServiceToControl; 438 } 439 } 440 return INVALID_SERVICE_ID; 441 } 442 443 /** 444 * Enable or disable tracking typing focus for the specific magnification window. 445 * 446 * The tracking typing focus should be set to enabled with the following conditions: 447 * 1. IME is shown. 448 * 449 * The tracking typing focus should be set to disabled with the following conditions: 450 * 1. A user drags the magnification window by 1 finger. 451 * 2. A user scroll the magnification window by 2 fingers. 452 * 453 * @param displayId The logical display id. 454 * @param trackingTypingFocusEnabled Enabled or disable the function of tracking typing focus. 455 */ setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled)456 void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) { 457 synchronized (mLock) { 458 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 459 if (magnifier == null) { 460 return; 461 } 462 magnifier.setTrackingTypingFocusEnabled(trackingTypingFocusEnabled); 463 } 464 } 465 466 /** 467 * Enable tracking typing focus function for all magnifications. 468 */ enableAllTrackingTypingFocus()469 private void enableAllTrackingTypingFocus() { 470 synchronized (mLock) { 471 for (int i = 0; i < mWindowMagnifiers.size(); i++) { 472 WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); 473 magnifier.setTrackingTypingFocusEnabled(true); 474 } 475 } 476 } 477 pauseTrackingTypingFocusRecord(int displayId)478 private void pauseTrackingTypingFocusRecord(int displayId) { 479 WindowMagnifier magnifier; 480 synchronized (mLock) { 481 magnifier = mWindowMagnifiers.get(displayId); 482 if (magnifier == null) { 483 return; 484 } 485 } 486 magnifier.pauseTrackingTypingFocusRecord(); 487 } 488 489 /** 490 * Called when the IME window visibility changed. 491 * 492 * @param shown {@code true} means the IME window shows on the screen. Otherwise, it's hidden. 493 */ onImeWindowVisibilityChanged(int displayId, boolean shown)494 void onImeWindowVisibilityChanged(int displayId, boolean shown) { 495 synchronized (mLock) { 496 mIsImeVisibleArray.put(displayId, shown); 497 } 498 if (shown) { 499 enableAllTrackingTypingFocus(); 500 } else { 501 pauseTrackingTypingFocusRecord(displayId); 502 } 503 } 504 isImeVisible(int displayId)505 boolean isImeVisible(int displayId) { 506 synchronized (mLock) { 507 return mIsImeVisibleArray.get(displayId); 508 } 509 } 510 logTrackingTypingFocus(long duration)511 void logTrackingTypingFocus(long duration) { 512 AccessibilityStatsLogUtils.logMagnificationFollowTypingFocusSession(duration); 513 } 514 515 @Override processScroll(int displayId, float distanceX, float distanceY)516 public boolean processScroll(int displayId, float distanceX, float distanceY) { 517 moveWindowMagnification(displayId, -distanceX, -distanceY); 518 setTrackingTypingFocusEnabled(displayId, false); 519 return /* event consumed: */ true; 520 } 521 522 /** 523 * Scales the magnified region on the specified display if window magnification is initiated. 524 * 525 * @param displayId The logical display id. 526 * @param scale The target scale, must be >= 1 527 */ 528 @Override setScale(int displayId, float scale)529 public void setScale(int displayId, float scale) { 530 synchronized (mLock) { 531 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 532 if (magnifier == null) { 533 return; 534 } 535 magnifier.setScale(scale); 536 mLastActivatedScale.put(displayId, scale); 537 } 538 } 539 540 /** 541 * Enables window magnification with specified center and scale on the given display and 542 * animating the transition. 543 * 544 * @param displayId The logical display id. 545 * @param scale The target scale, must be >= 1. 546 * @param centerX The screen-relative X coordinate around which to center, 547 * or {@link Float#NaN} to leave unchanged. 548 * @param centerY The screen-relative Y coordinate around which to center, 549 * or {@link Float#NaN} to leave unchanged. 550 * @return {@code true} if the magnification is enabled successfully. 551 */ enableWindowMagnification(int displayId, float scale, float centerX, float centerY)552 public boolean enableWindowMagnification(int displayId, float scale, float centerX, 553 float centerY) { 554 return enableWindowMagnification(displayId, scale, centerX, centerY, 555 STUB_ANIMATION_CALLBACK, MAGNIFICATION_GESTURE_HANDLER_ID); 556 } 557 558 /** 559 * Enables window magnification with specified center and scale on the given display and 560 * animating the transition. 561 * 562 * @param displayId The logical display id. 563 * @param scale The target scale, must be >= 1. 564 * @param centerX The screen-relative X coordinate around which to center for magnification, 565 * or {@link Float#NaN} to leave unchanged. 566 * @param centerY The screen-relative Y coordinate around which to center for magnification, 567 * or {@link Float#NaN} to leave unchanged. 568 * @param animationCallback Called when the animation result is valid. 569 * @param id The connection ID 570 * @return {@code true} if the magnification is enabled successfully. 571 */ enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, int id)572 public boolean enableWindowMagnification(int displayId, float scale, float centerX, 573 float centerY, @Nullable MagnificationAnimationCallback animationCallback, int id) { 574 return enableWindowMagnification(displayId, scale, centerX, centerY, animationCallback, 575 WINDOW_POSITION_AT_CENTER, id); 576 } 577 578 /** 579 * Enables window magnification with specified center and scale on the given display and 580 * animating the transition. 581 * 582 * @param displayId The logical display id. 583 * @param scale The target scale, must be >= 1. 584 * @param centerX The screen-relative X coordinate around which to center for magnification, 585 * or {@link Float#NaN} to leave unchanged. 586 * @param centerY The screen-relative Y coordinate around which to center for magnification, 587 * or {@link Float#NaN} to leave unchanged. 588 * @param windowPosition Indicate the offset between window position and (centerX, centerY). 589 * @return {@code true} if the magnification is enabled successfully. 590 */ enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @WindowPosition int windowPosition)591 public boolean enableWindowMagnification(int displayId, float scale, float centerX, 592 float centerY, @WindowPosition int windowPosition) { 593 return enableWindowMagnification(displayId, scale, centerX, centerY, 594 STUB_ANIMATION_CALLBACK, windowPosition, MAGNIFICATION_GESTURE_HANDLER_ID); 595 } 596 597 /** 598 * Enables window magnification with specified center and scale on the given display and 599 * animating the transition. 600 * 601 * @param displayId The logical display id. 602 * @param scale The target scale, must be >= 1. 603 * @param centerX The screen-relative X coordinate around which to center for 604 * magnification, or {@link Float#NaN} to leave unchanged. 605 * @param centerY The screen-relative Y coordinate around which to center for 606 * magnification, or {@link Float#NaN} to leave unchanged. 607 * @param animationCallback Called when the animation result is valid. 608 * @param windowPosition Indicate the offset between window position and (centerX, centerY). 609 * @return {@code true} if the magnification is enabled successfully. 610 */ enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, @WindowPosition int windowPosition, int id)611 public boolean enableWindowMagnification(int displayId, float scale, float centerX, 612 float centerY, @Nullable MagnificationAnimationCallback animationCallback, 613 @WindowPosition int windowPosition, int id) { 614 final boolean enabled; 615 boolean previousEnabled; 616 synchronized (mLock) { 617 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 618 if (magnifier == null) { 619 magnifier = createWindowMagnifier(displayId); 620 } 621 previousEnabled = magnifier.mEnabled; 622 enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, 623 animationCallback, windowPosition, id); 624 if (enabled) { 625 mLastActivatedScale.put(displayId, getScale(displayId)); 626 } 627 } 628 629 if (enabled) { 630 setTrackingTypingFocusEnabled(displayId, true); 631 if (!previousEnabled) { 632 mCallback.onWindowMagnificationActivationState(displayId, true); 633 } 634 } 635 return enabled; 636 } 637 638 /** 639 * Disables window magnification on the given display. 640 * 641 * @param displayId The logical display id. 642 * @param clear {@true} Clears the state of window magnification. 643 * @return {@code true} if the magnification is turned to be disabled successfully 644 */ disableWindowMagnification(int displayId, boolean clear)645 public boolean disableWindowMagnification(int displayId, boolean clear) { 646 return disableWindowMagnification(displayId, clear, STUB_ANIMATION_CALLBACK); 647 } 648 649 /** 650 * Disables window magnification on the specified display and animating the transition. 651 * 652 * @param displayId The logical display id. 653 * @param clear {@true} Clears the state of window magnification. 654 * @param animationCallback Called when the animation result is valid. 655 * @return {@code true} if the magnification is turned to be disabled successfully 656 */ disableWindowMagnification(int displayId, boolean clear, MagnificationAnimationCallback animationCallback)657 public boolean disableWindowMagnification(int displayId, boolean clear, 658 MagnificationAnimationCallback animationCallback) { 659 final boolean disabled; 660 synchronized (mLock) { 661 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 662 if (magnifier == null) { 663 return false; 664 } 665 666 disabled = magnifier.disableWindowMagnificationInternal(animationCallback); 667 if (clear) { 668 mWindowMagnifiers.delete(displayId); 669 } 670 } 671 672 if (disabled) { 673 mCallback.onWindowMagnificationActivationState(displayId, false); 674 } 675 return disabled; 676 } 677 678 /** 679 * Notify Fullscreen magnification activation changes. 680 */ onFullscreenMagnificationActivationChanged(int displayId, boolean activated)681 public boolean onFullscreenMagnificationActivationChanged(int displayId, boolean activated) { 682 synchronized (mLock) { 683 if (!waitConnectionWithTimeoutIfNeeded()) { 684 Slog.w(TAG, 685 "onFullscreenMagnificationActivationChanged mConnectionWrapper is null. " 686 + "mConnectionState=" + connectionStateToString(mConnectionState)); 687 return false; 688 } 689 return mConnectionWrapper 690 .onFullscreenMagnificationActivationChanged(displayId, activated); 691 } 692 } 693 694 /** 695 * Calculates the number of fingers in the window. 696 * 697 * @param displayId The logical display id. 698 * @param motionEvent The motion event 699 * @return the number of fingers in the window. 700 */ pointersInWindow(int displayId, MotionEvent motionEvent)701 int pointersInWindow(int displayId, MotionEvent motionEvent) { 702 synchronized (mLock) { 703 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 704 if (magnifier == null) { 705 return 0; 706 } 707 return magnifier.pointersInWindow(motionEvent); 708 } 709 } 710 711 @GuardedBy("mLock") isPositionInSourceBounds(int displayId, float x, float y)712 boolean isPositionInSourceBounds(int displayId, float x, float y) { 713 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 714 if (magnifier == null) { 715 return false; 716 } 717 return magnifier.isPositionInSourceBounds(x, y); 718 } 719 720 /** 721 * Indicates whether window magnification is enabled on specified display. 722 * 723 * @param displayId The logical display id. 724 * @return {@code true} if the window magnification is enabled. 725 */ isWindowMagnifierEnabled(int displayId)726 public boolean isWindowMagnifierEnabled(int displayId) { 727 synchronized (mLock) { 728 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 729 if (magnifier == null) { 730 return false; 731 } 732 return magnifier.isEnabled(); 733 } 734 } 735 736 /** 737 * Retrieves a previously magnification scale from the current 738 * user's settings. Only the value of the default display is persisted. 739 * 740 * @return the previously magnification scale, or the default 741 * scale if none is available 742 */ getPersistedScale(int displayId)743 float getPersistedScale(int displayId) { 744 return MathUtils.constrain(mScaleProvider.getScale(displayId), 745 MagnificationConstants.PERSISTED_SCALE_MIN_VALUE, 746 MagnificationScaleProvider.MAX_SCALE); 747 } 748 749 /** 750 * Persists the default display magnification scale to the current user's settings 751 * <strong>if scale is >= {@link MagnificationConstants.PERSISTED_SCALE_MIN_VALUE}</strong>. 752 * We assume if the scale is < {@link MagnificationConstants.PERSISTED_SCALE_MIN_VALUE}, there 753 * will be no obvious magnification effect. 754 * Only the value of the default display is persisted in user's settings. 755 */ persistScale(int displayId)756 void persistScale(int displayId) { 757 float scale = getScale(displayId); 758 if (scale < MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) { 759 return; 760 } 761 mScaleProvider.putScale(scale, displayId); 762 } 763 764 /** 765 * Returns the magnification scale. 766 * 767 * @param displayId The logical display id. 768 * @return the scale 769 */ getScale(int displayId)770 public float getScale(int displayId) { 771 synchronized (mLock) { 772 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 773 if (magnifier == null || !magnifier.mEnabled) { 774 return 1.0f; 775 } 776 return magnifier.getScale(); 777 } 778 } 779 getLastActivatedScale(int displayId)780 protected float getLastActivatedScale(int displayId) { 781 synchronized (mLock) { 782 if (!mLastActivatedScale.contains(displayId)) { 783 return -1.0f; 784 } 785 return mLastActivatedScale.get(displayId); 786 } 787 } 788 789 /** 790 * Moves window magnification on the specified display with the specified offset. 791 * 792 * @param displayId The logical display id. 793 * @param offsetX the amount in pixels to offset the region in the X direction, in current 794 * screen pixels. 795 * @param offsetY the amount in pixels to offset the region in the Y direction, in current 796 * screen pixels. 797 */ moveWindowMagnification(int displayId, float offsetX, float offsetY)798 void moveWindowMagnification(int displayId, float offsetX, float offsetY) { 799 synchronized (mLock) { 800 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 801 if (magnifier == null) { 802 return; 803 } 804 magnifier.move(offsetX, offsetY); 805 } 806 } 807 808 /** 809 * Requests System UI show magnification mode button UI on the specified display. 810 * 811 * @param displayId The logical display id. 812 * @param magnificationMode the current magnification mode. 813 * @return {@code true} if the event was handled, {@code false} otherwise 814 */ showMagnificationButton(int displayId, int magnificationMode)815 public boolean showMagnificationButton(int displayId, int magnificationMode) { 816 synchronized (mLock) { 817 return mConnectionWrapper != null 818 && mConnectionWrapper.showMagnificationButton(displayId, magnificationMode); 819 } 820 } 821 822 /** 823 * Requests System UI remove magnification mode button UI on the specified display. 824 * 825 * @param displayId The logical display id. 826 * @return {@code true} if the event was handled, {@code false} otherwise 827 */ removeMagnificationButton(int displayId)828 public boolean removeMagnificationButton(int displayId) { 829 synchronized (mLock) { 830 return mConnectionWrapper != null 831 && mConnectionWrapper.removeMagnificationButton(displayId); 832 } 833 } 834 835 /** 836 * Requests System UI remove magnification settings panel on the specified display. 837 * 838 * @param displayId The logical display id. 839 * @return {@code true} if the event was handled, {@code false} otherwise 840 */ removeMagnificationSettingsPanel(int displayId)841 public boolean removeMagnificationSettingsPanel(int displayId) { 842 synchronized (mLock) { 843 return mConnectionWrapper != null 844 && mConnectionWrapper.removeMagnificationSettingsPanel(displayId); 845 } 846 } 847 848 /** 849 * Notify System UI the magnification scale on the specified display for userId is changed. 850 * 851 * @param userId the user id. 852 * @param displayId the logical display id. 853 * @param scale magnification scale. 854 */ onUserMagnificationScaleChanged(int userId, int displayId, float scale)855 public boolean onUserMagnificationScaleChanged(int userId, int displayId, float scale) { 856 synchronized (mLock) { 857 return mConnectionWrapper != null 858 && mConnectionWrapper.onUserMagnificationScaleChanged(userId, displayId, scale); 859 } 860 } 861 862 /** 863 * Returns the screen-relative X coordinate of the center of the magnified bounds. 864 * 865 * @param displayId The logical display id 866 * @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled. 867 */ getCenterX(int displayId)868 public float getCenterX(int displayId) { 869 synchronized (mLock) { 870 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 871 if (magnifier == null || !magnifier.mEnabled) { 872 return Float.NaN; 873 } 874 return magnifier.getCenterX(); 875 } 876 } 877 878 /** 879 * Returns the screen-relative Y coordinate of the center of the magnified bounds. 880 * 881 * @param displayId The logical display id 882 * @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled. 883 */ getCenterY(int displayId)884 public float getCenterY(int displayId) { 885 synchronized (mLock) { 886 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 887 if (magnifier == null || !magnifier.mEnabled) { 888 return Float.NaN; 889 } 890 return magnifier.getCenterY(); 891 } 892 } 893 isTrackingTypingFocusEnabled(int displayId)894 boolean isTrackingTypingFocusEnabled(int displayId) { 895 synchronized (mLock) { 896 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 897 if (magnifier == null) { 898 return false; 899 } 900 return magnifier.isTrackingTypingFocusEnabled(); 901 } 902 } 903 904 /** 905 * Populates magnified bounds on the screen. And the populated magnified bounds would be 906 * empty If window magnifier is not activated. 907 * 908 * @param displayId The logical display id. 909 * @param outRegion the region to populate 910 */ getMagnificationSourceBounds(int displayId, @NonNull Region outRegion)911 public void getMagnificationSourceBounds(int displayId, @NonNull Region outRegion) { 912 synchronized (mLock) { 913 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 914 if (magnifier == null || !magnifier.mEnabled) { 915 outRegion.setEmpty(); 916 } else { 917 outRegion.set(magnifier.mSourceBounds); 918 } 919 } 920 } 921 922 /** 923 * Creates the windowMagnifier based on the specified display and stores it. 924 * 925 * @param displayId logical display id. 926 */ 927 @GuardedBy("mLock") createWindowMagnifier(int displayId)928 private WindowMagnifier createWindowMagnifier(int displayId) { 929 final WindowMagnifier magnifier = new WindowMagnifier(displayId, this); 930 mWindowMagnifiers.put(displayId, magnifier); 931 return magnifier; 932 } 933 934 /** 935 * Removes the window magnifier with given id. 936 * 937 * @param displayId The logical display id. 938 */ onDisplayRemoved(int displayId)939 public void onDisplayRemoved(int displayId) { 940 disableWindowMagnification(displayId, true); 941 } 942 943 private class ConnectionCallback extends IMagnificationConnectionCallback.Stub implements 944 IBinder.DeathRecipient { 945 private boolean mExpiredDeathRecipient = false; 946 947 @RequiresNoPermission 948 @Override onWindowMagnifierBoundsChanged(int displayId, Rect bounds)949 public void onWindowMagnifierBoundsChanged(int displayId, Rect bounds) { 950 if (mTrace.isA11yTracingEnabledForTypes( 951 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK)) { 952 mTrace.logTrace(TAG + "ConnectionCallback.onWindowMagnifierBoundsChanged", 953 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK, 954 "displayId=" + displayId + ";bounds=" + bounds); 955 } 956 synchronized (mLock) { 957 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 958 if (magnifier == null) { 959 magnifier = createWindowMagnifier(displayId); 960 } 961 if (DBG) { 962 Slog.i(TAG, 963 "onWindowMagnifierBoundsChanged -" + displayId + " bounds = " + bounds); 964 } 965 magnifier.setMagnifierLocation(bounds); 966 } 967 } 968 969 @RequiresNoPermission 970 @Override onChangeMagnificationMode(int displayId, int magnificationMode)971 public void onChangeMagnificationMode(int displayId, int magnificationMode) 972 throws RemoteException { 973 if (mTrace.isA11yTracingEnabledForTypes( 974 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK)) { 975 mTrace.logTrace(TAG + "ConnectionCallback.onChangeMagnificationMode", 976 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK, 977 "displayId=" + displayId + ";mode=" + magnificationMode); 978 } 979 mCallback.onChangeMagnificationMode(displayId, magnificationMode); 980 } 981 982 @RequiresNoPermission 983 @Override onSourceBoundsChanged(int displayId, Rect sourceBounds)984 public void onSourceBoundsChanged(int displayId, Rect sourceBounds) { 985 if (mTrace.isA11yTracingEnabledForTypes( 986 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK)) { 987 mTrace.logTrace(TAG + "ConnectionCallback.onSourceBoundsChanged", 988 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK, 989 "displayId=" + displayId + ";source=" + sourceBounds); 990 } 991 synchronized (mLock) { 992 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 993 if (magnifier == null) { 994 magnifier = createWindowMagnifier(displayId); 995 } 996 magnifier.onSourceBoundsChanged(sourceBounds); 997 } 998 mCallback.onSourceBoundsChanged(displayId, sourceBounds); 999 } 1000 1001 @RequiresNoPermission 1002 @Override onPerformScaleAction(int displayId, float scale, boolean updatePersistence)1003 public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) { 1004 if (mTrace.isA11yTracingEnabledForTypes( 1005 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK)) { 1006 mTrace.logTrace(TAG + "ConnectionCallback.onPerformScaleAction", 1007 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK, 1008 "displayId=" + displayId + ";scale=" + scale 1009 + ";updatePersistence=" + updatePersistence); 1010 } 1011 mCallback.onPerformScaleAction(displayId, scale, updatePersistence); 1012 } 1013 1014 @RequiresNoPermission 1015 @Override onAccessibilityActionPerformed(int displayId)1016 public void onAccessibilityActionPerformed(int displayId) { 1017 if (mTrace.isA11yTracingEnabledForTypes( 1018 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK)) { 1019 mTrace.logTrace(TAG + "ConnectionCallback.onAccessibilityActionPerformed", 1020 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK, 1021 "displayId=" + displayId); 1022 } 1023 mCallback.onAccessibilityActionPerformed(displayId); 1024 } 1025 1026 @RequiresNoPermission 1027 @Override onMove(int displayId)1028 public void onMove(int displayId) { 1029 if (mTrace.isA11yTracingEnabledForTypes( 1030 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK)) { 1031 mTrace.logTrace(TAG + "ConnectionCallback.onMove", 1032 FLAGS_MAGNIFICATION_CONNECTION_CALLBACK, 1033 "displayId=" + displayId); 1034 } 1035 setTrackingTypingFocusEnabled(displayId, false); 1036 } 1037 1038 @Override binderDied()1039 public void binderDied() { 1040 synchronized (mLock) { 1041 Slog.w(TAG, "binderDied DeathRecipient :" + mExpiredDeathRecipient); 1042 if (mExpiredDeathRecipient) { 1043 return; 1044 } 1045 mConnectionWrapper.unlinkToDeath(this); 1046 mConnectionWrapper = null; 1047 mConnectionCallback = null; 1048 setConnectionState(DISCONNECTED); 1049 resetWindowMagnifiers(); 1050 } 1051 } 1052 } 1053 1054 /** 1055 * A class manipulates window magnification per display and contains the magnification 1056 * information. 1057 * <p> 1058 * This class requires to hold the lock when controlling the magnifier. 1059 * </p> 1060 */ 1061 private static class WindowMagnifier { 1062 1063 private final int mDisplayId; 1064 private float mScale = MagnificationScaleProvider.MIN_SCALE; 1065 private boolean mEnabled; 1066 1067 private final MagnificationConnectionManager mMagnificationConnectionManager; 1068 // Records the bounds of window magnification. 1069 private final Rect mBounds = new Rect(); 1070 // The magnified bounds on the screen. 1071 private final Rect mSourceBounds = new Rect(); 1072 1073 private int mIdOfLastServiceToControl = INVALID_SERVICE_ID; 1074 1075 private final PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f); 1076 1077 private boolean mTrackingTypingFocusEnabled = true; 1078 1079 private volatile long mTrackingTypingFocusStartTime = 0; 1080 private static final AtomicLongFieldUpdater<WindowMagnifier> SUM_TIME_UPDATER = 1081 AtomicLongFieldUpdater.newUpdater(WindowMagnifier.class, 1082 "mTrackingTypingFocusSumTime"); 1083 private volatile long mTrackingTypingFocusSumTime = 0; 1084 WindowMagnifier(int displayId, MagnificationConnectionManager magnificationConnectionManager)1085 WindowMagnifier(int displayId, 1086 MagnificationConnectionManager magnificationConnectionManager) { 1087 mDisplayId = displayId; 1088 mMagnificationConnectionManager = magnificationConnectionManager; 1089 } 1090 1091 // TODO(b/312324808): Investigating whether 1092 // mMagnificationConnectionManager#enableWindowMagnificationInternal requires a sync lock 1093 @SuppressWarnings("GuardedBy") enableWindowMagnificationInternal(float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, @WindowPosition int windowPosition, int id)1094 boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY, 1095 @Nullable MagnificationAnimationCallback animationCallback, 1096 @WindowPosition int windowPosition, int id) { 1097 // Handle defaults. The scale may be NAN when just updating magnification center. 1098 if (Float.isNaN(scale)) { 1099 scale = getScale(); 1100 } 1101 final float normScale = MagnificationScaleProvider.constrainScale(scale); 1102 setMagnificationFrameOffsetRatioByWindowPosition(windowPosition); 1103 if (mMagnificationConnectionManager.enableWindowMagnificationInternal(mDisplayId, 1104 normScale, centerX, centerY, mMagnificationFrameOffsetRatio.x, 1105 mMagnificationFrameOffsetRatio.y, animationCallback)) { 1106 mScale = normScale; 1107 mEnabled = true; 1108 mIdOfLastServiceToControl = id; 1109 return true; 1110 } 1111 return false; 1112 } 1113 setMagnificationFrameOffsetRatioByWindowPosition(@indowPosition int windowPosition)1114 void setMagnificationFrameOffsetRatioByWindowPosition(@WindowPosition int windowPosition) { 1115 switch (windowPosition) { 1116 case WINDOW_POSITION_AT_CENTER: { 1117 mMagnificationFrameOffsetRatio.set(0f, 0f); 1118 } 1119 break; 1120 case WINDOW_POSITION_AT_TOP_LEFT: { 1121 mMagnificationFrameOffsetRatio.set(-1f, -1f); 1122 } 1123 break; 1124 } 1125 } 1126 1127 // TODO(b/312324808): Investigating whether 1128 // mMagnificationConnectionManager#disableWindowMagnificationInternal requires a sync lock 1129 @SuppressWarnings("GuardedBy") disableWindowMagnificationInternal( @ullable MagnificationAnimationCallback animationResultCallback)1130 boolean disableWindowMagnificationInternal( 1131 @Nullable MagnificationAnimationCallback animationResultCallback) { 1132 if (!mEnabled) { 1133 return false; 1134 } 1135 if (mMagnificationConnectionManager.disableWindowMagnificationInternal( 1136 mDisplayId, animationResultCallback)) { 1137 mEnabled = false; 1138 mIdOfLastServiceToControl = INVALID_SERVICE_ID; 1139 mTrackingTypingFocusEnabled = false; 1140 pauseTrackingTypingFocusRecord(); 1141 return true; 1142 } 1143 return false; 1144 } 1145 1146 // ErrorProne says the access of mMagnificationConnectionManager#setScaleInternal should 1147 // be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one as 1148 // 'mLock'. Therefore, we'll put @SuppressWarnings here. 1149 @SuppressWarnings("GuardedBy") 1150 @GuardedBy("mLock") setScale(float scale)1151 void setScale(float scale) { 1152 if (!mEnabled) { 1153 return; 1154 } 1155 final float normScale = MagnificationScaleProvider.constrainScale(scale); 1156 if (Float.compare(mScale, normScale) != 0 1157 && mMagnificationConnectionManager 1158 .setScaleForWindowMagnificationInternal(mDisplayId, scale)) { 1159 mScale = normScale; 1160 } 1161 } 1162 1163 @GuardedBy("mLock") getScale()1164 float getScale() { 1165 return mScale; 1166 } 1167 1168 @GuardedBy("mLock") setMagnifierLocation(Rect rect)1169 void setMagnifierLocation(Rect rect) { 1170 mBounds.set(rect); 1171 } 1172 1173 /** 1174 * Returns the ID of the last service that changed the magnification config. 1175 */ getIdOfLastServiceToControl()1176 int getIdOfLastServiceToControl() { 1177 return mIdOfLastServiceToControl; 1178 } 1179 pointersInWindow(MotionEvent motionEvent)1180 int pointersInWindow(MotionEvent motionEvent) { 1181 int count = 0; 1182 final int pointerCount = motionEvent.getPointerCount(); 1183 for (int i = 0; i < pointerCount; i++) { 1184 final float x = motionEvent.getX(i); 1185 final float y = motionEvent.getY(i); 1186 if (mBounds.contains((int) x, (int) y)) { 1187 count++; 1188 } 1189 } 1190 return count; 1191 } 1192 isPositionInSourceBounds(float x, float y)1193 boolean isPositionInSourceBounds(float x, float y) { 1194 return mSourceBounds.contains((int) x, (int) y); 1195 } 1196 setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled)1197 void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) { 1198 if (mMagnificationConnectionManager.isWindowMagnifierEnabled(mDisplayId) 1199 && mMagnificationConnectionManager.isImeVisible(mDisplayId) 1200 && trackingTypingFocusEnabled) { 1201 startTrackingTypingFocusRecord(); 1202 } 1203 if (mTrackingTypingFocusEnabled && !trackingTypingFocusEnabled) { 1204 stopAndLogTrackingTypingFocusRecordIfNeeded(); 1205 } 1206 mTrackingTypingFocusEnabled = trackingTypingFocusEnabled; 1207 } 1208 isTrackingTypingFocusEnabled()1209 boolean isTrackingTypingFocusEnabled() { 1210 return mTrackingTypingFocusEnabled; 1211 } 1212 startTrackingTypingFocusRecord()1213 void startTrackingTypingFocusRecord() { 1214 if (mTrackingTypingFocusStartTime == 0) { 1215 mTrackingTypingFocusStartTime = SystemClock.uptimeMillis(); 1216 if (DBG) { 1217 Slog.d(TAG, "start: mTrackingTypingFocusStartTime = " 1218 + mTrackingTypingFocusStartTime); 1219 } 1220 } 1221 } 1222 pauseTrackingTypingFocusRecord()1223 void pauseTrackingTypingFocusRecord() { 1224 if (mTrackingTypingFocusStartTime != 0) { 1225 final long elapsed = (SystemClock.uptimeMillis() - mTrackingTypingFocusStartTime); 1226 // update mTrackingTypingFocusSumTime value in an atomic operation 1227 SUM_TIME_UPDATER.addAndGet(this, elapsed); 1228 mTrackingTypingFocusStartTime = 0; 1229 if (DBG) { 1230 Slog.d(TAG, "pause: mTrackingTypingFocusSumTime = " 1231 + mTrackingTypingFocusSumTime + ", elapsed = " + elapsed); 1232 } 1233 } 1234 } 1235 stopAndLogTrackingTypingFocusRecordIfNeeded()1236 void stopAndLogTrackingTypingFocusRecordIfNeeded() { 1237 if (mTrackingTypingFocusStartTime != 0 || mTrackingTypingFocusSumTime != 0) { 1238 final long elapsed = mTrackingTypingFocusStartTime != 0 1239 ? (SystemClock.uptimeMillis() - mTrackingTypingFocusStartTime) : 0; 1240 final long duration = mTrackingTypingFocusSumTime + elapsed; 1241 if (DBG) { 1242 Slog.d(TAG, "stop and log: session duration = " + duration 1243 + ", elapsed = " + elapsed); 1244 } 1245 mMagnificationConnectionManager.logTrackingTypingFocus(duration); 1246 mTrackingTypingFocusStartTime = 0; 1247 mTrackingTypingFocusSumTime = 0; 1248 } 1249 } 1250 isEnabled()1251 boolean isEnabled() { 1252 return mEnabled; 1253 } 1254 1255 // ErrorProne says the access of mMagnificationConnectionManager#moveWindowMagnifierInternal 1256 // should be guarded by 'this.mMagnificationConnectionManager.mLock' which is the same one 1257 // as 'mLock'. Therefore, we'll put @SuppressWarnings here. 1258 @SuppressWarnings("GuardedBy") 1259 @GuardedBy("mLock") move(float offsetX, float offsetY)1260 void move(float offsetX, float offsetY) { 1261 mMagnificationConnectionManager.moveWindowMagnifierInternal( 1262 mDisplayId, offsetX, offsetY); 1263 } 1264 1265 @GuardedBy("mLock") reset()1266 void reset() { 1267 mEnabled = false; 1268 mIdOfLastServiceToControl = INVALID_SERVICE_ID; 1269 mSourceBounds.setEmpty(); 1270 } 1271 1272 @GuardedBy("mLock") onSourceBoundsChanged(Rect sourceBounds)1273 public void onSourceBoundsChanged(Rect sourceBounds) { 1274 mSourceBounds.set(sourceBounds); 1275 } 1276 1277 @GuardedBy("mLock") getCenterX()1278 float getCenterX() { 1279 return mSourceBounds.exactCenterX(); 1280 } 1281 1282 @GuardedBy("mLock") getCenterY()1283 float getCenterY() { 1284 return mSourceBounds.exactCenterY(); 1285 } 1286 } 1287 1288 @GuardedBy("mLock") enableWindowMagnificationInternal(int displayId, float scale, float centerX, float centerY, float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, MagnificationAnimationCallback animationCallback)1289 private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX, 1290 float centerY, float magnificationFrameOffsetRatioX, 1291 float magnificationFrameOffsetRatioY, 1292 MagnificationAnimationCallback animationCallback) { 1293 if (!waitConnectionWithTimeoutIfNeeded()) { 1294 Slog.w(TAG, 1295 "enableWindowMagnificationInternal mConnectionWrapper is null. " 1296 + "mConnectionState=" + connectionStateToString(mConnectionState)); 1297 return false; 1298 } 1299 return mConnectionWrapper.enableWindowMagnification( 1300 displayId, scale, centerX, centerY, 1301 magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, 1302 animationCallback); 1303 } 1304 1305 @GuardedBy("mLock") setScaleForWindowMagnificationInternal(int displayId, float scale)1306 private boolean setScaleForWindowMagnificationInternal(int displayId, float scale) { 1307 return mConnectionWrapper != null 1308 && mConnectionWrapper.setScaleForWindowMagnification(displayId, scale); 1309 } 1310 1311 @GuardedBy("mLock") disableWindowMagnificationInternal(int displayId, MagnificationAnimationCallback animationCallback)1312 private boolean disableWindowMagnificationInternal(int displayId, 1313 MagnificationAnimationCallback animationCallback) { 1314 if (mConnectionWrapper == null) { 1315 Slog.w(TAG, "mConnectionWrapper is null"); 1316 return false; 1317 } 1318 return mConnectionWrapper.disableWindowMagnification( 1319 displayId, animationCallback); 1320 } 1321 1322 @GuardedBy("mLock") moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY)1323 private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) { 1324 return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifier( 1325 displayId, offsetX, offsetY); 1326 } 1327 1328 @GuardedBy("mLock") moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY, MagnificationAnimationCallback animationCallback)1329 private boolean moveWindowMagnifierToPositionInternal(int displayId, float positionX, 1330 float positionY, MagnificationAnimationCallback animationCallback) { 1331 return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifierToPosition( 1332 displayId, positionX, positionY, animationCallback); 1333 } 1334 waitConnectionWithTimeoutIfNeeded()1335 boolean waitConnectionWithTimeoutIfNeeded() { 1336 // Wait for the connection with a timeout. 1337 final long endMillis = SystemClock.uptimeMillis() + WAIT_CONNECTION_TIMEOUT_MILLIS; 1338 while (mConnectionState == CONNECTING && (SystemClock.uptimeMillis() < endMillis)) { 1339 try { 1340 mLock.wait(endMillis - SystemClock.uptimeMillis()); 1341 } catch (InterruptedException ie) { 1342 /* ignore */ 1343 } 1344 } 1345 return isConnected(); 1346 } 1347 } 1348