• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.wm.shell.splitscreen;
18 
19 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__LAUNCHER;
20 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__MULTI_INSTANCE;
21 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__UNKNOWN_ENTER;
22 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
23 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
24 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__CHILD_TASK_ENTER_PIP;
25 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
26 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
27 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_REQUEST;
28 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
29 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
30 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
31 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
32 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
33 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED;
34 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
35 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
36 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
37 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
38 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
39 import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_DRAG;
40 import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_LAUNCHER;
41 import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_MULTI_INSTANCE;
42 import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_UNKNOWN;
43 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
44 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
45 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
46 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
47 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
48 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE;
49 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_REQUEST;
50 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
51 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
52 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
53 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ROOT_TASK_VANISHED;
54 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED;
55 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
56 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
57 
58 import android.annotation.Nullable;
59 import android.util.Slog;
60 
61 import com.android.internal.logging.InstanceId;
62 import com.android.internal.logging.InstanceIdSequence;
63 import com.android.internal.protolog.ProtoLog;
64 import com.android.internal.util.FrameworkStatsLog;
65 import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
66 import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
67 
68 /**
69  * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent
70  */
71 public class SplitscreenEventLogger {
72 
73     // Used to generate instance ids for this drag if one is not provided
74     private final InstanceIdSequence mIdSequence;
75 
76     // The instance id for the current splitscreen session (from start to end)
77     private InstanceId mLoggerSessionId;
78 
79     // Drag info
80     private @SplitPosition int mDragEnterPosition;
81     private @Nullable InstanceId mEnterSessionId;
82 
83     // For deduping async events
84     private int mLastMainStagePosition = -1;
85     private int mLastMainStageUid = -1;
86     private int mLastSideStagePosition = -1;
87     private int mLastSideStageUid = -1;
88     private float mLastSplitRatio = -1f;
89     private @SplitScreenController.SplitEnterReason int mEnterReason = ENTER_REASON_UNKNOWN;
90 
SplitscreenEventLogger()91     public SplitscreenEventLogger() {
92         mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE);
93     }
94 
95     /**
96      * Return whether a splitscreen session has started.
97      */
hasStartedSession()98     public boolean hasStartedSession() {
99         return mLoggerSessionId != null;
100     }
101 
isEnterRequestedByDrag()102     public boolean isEnterRequestedByDrag() {
103         return mEnterReason == ENTER_REASON_DRAG;
104     }
105 
106     /**
107      * May be called before logEnter() to indicate that the session was started from a drag.
108      */
enterRequestedByDrag(@plitPosition int position, InstanceId enterSessionId)109     public void enterRequestedByDrag(@SplitPosition int position, InstanceId enterSessionId) {
110         mDragEnterPosition = position;
111         enterRequested(enterSessionId, ENTER_REASON_DRAG);
112     }
113 
114     /**
115      * May be called before logEnter() to indicate that the session was started from launcher.
116      * This specifically is for all the scenarios where split started without a drag interaction
117      */
enterRequested(@ullable InstanceId enterSessionId, @SplitScreenController.SplitEnterReason int enterReason)118     public void enterRequested(@Nullable InstanceId enterSessionId,
119             @SplitScreenController.SplitEnterReason int enterReason) {
120         mEnterSessionId = enterSessionId;
121         mEnterReason = enterReason;
122     }
123 
124     /**
125      * @return if an enterSessionId has been set via either
126      *         {@link #enterRequested(InstanceId, int)} or
127      *         {@link #enterRequestedByDrag(int, InstanceId)}
128      */
hasValidEnterSessionId()129     public boolean hasValidEnterSessionId() {
130         return mEnterSessionId != null;
131     }
132 
133     /**
134      * Logs when the user enters splitscreen.
135      */
logEnter(float splitRatio, @SplitPosition int mainStagePosition, int mainStageUid, @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape)136     public void logEnter(float splitRatio,
137             @SplitPosition int mainStagePosition, int mainStageUid,
138             @SplitPosition int sideStagePosition, int sideStageUid,
139             boolean isLandscape) {
140         if (hasStartedSession()) {
141             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logEnter: no-op, previous session has not ended");
142             return;
143         }
144 
145         mLoggerSessionId = mIdSequence.newInstanceId();
146         int enterReason = getLoggerEnterReason(isLandscape);
147         updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
148                 mainStageUid);
149         updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
150                 sideStageUid);
151         updateSplitRatioState(splitRatio);
152 
153         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logEnter: enterReason=%d splitRatio=%f "
154                         + "mainStagePosition=%d mainStageUid=%d sideStagePosition=%d "
155                         + "sideStageUid=%d isLandscape=%b mEnterSessionId=%d mLoggerSessionId=%d",
156                 enterReason, splitRatio, mLastMainStagePosition, mLastMainStageUid,
157                 mLastSideStagePosition, mLastSideStageUid, isLandscape,
158                 mEnterSessionId != null ? mEnterSessionId.getId() : 0, mLoggerSessionId.getId());
159 
160         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
161                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER,
162                 enterReason,
163                 0 /* exitReason */,
164                 splitRatio,
165                 mLastMainStagePosition,
166                 mLastMainStageUid,
167                 mLastSideStagePosition,
168                 mLastSideStageUid,
169                 mEnterSessionId != null ? mEnterSessionId.getId() : 0,
170                 mLoggerSessionId.getId());
171     }
172 
getLoggerEnterReason(boolean isLandscape)173     private int getLoggerEnterReason(boolean isLandscape) {
174         switch (mEnterReason) {
175             case ENTER_REASON_MULTI_INSTANCE:
176                 return SPLITSCREEN_UICHANGED__ENTER_REASON__MULTI_INSTANCE;
177             case ENTER_REASON_LAUNCHER:
178                 return SPLITSCREEN_UICHANGED__ENTER_REASON__LAUNCHER;
179             case ENTER_REASON_DRAG:
180                 return getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape);
181             case ENTER_REASON_UNKNOWN:
182             default:
183                 return SPLITSCREEN_UICHANGED__ENTER_REASON__UNKNOWN_ENTER;
184         }
185     }
186 
187     /**
188      * Returns the framework logging constant given a splitscreen exit reason.
189      */
getLoggerExitReason(@xitReason int exitReason)190     private int getLoggerExitReason(@ExitReason int exitReason) {
191         switch (exitReason) {
192             case EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW:
193                 return SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
194             case EXIT_REASON_APP_FINISHED:
195                 return SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
196             case EXIT_REASON_DEVICE_FOLDED:
197                 return SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
198             case EXIT_REASON_DRAG_DIVIDER:
199                 return SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
200             case EXIT_REASON_RETURN_HOME:
201                 return SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
202             case EXIT_REASON_ROOT_TASK_VANISHED:
203                 return SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
204             case EXIT_REASON_SCREEN_LOCKED:
205                 return SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED;
206             case EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP:
207                 return SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
208             case EXIT_REASON_CHILD_TASK_ENTER_PIP:
209                 return SPLITSCREEN_UICHANGED__EXIT_REASON__CHILD_TASK_ENTER_PIP;
210             case EXIT_REASON_RECREATE_SPLIT:
211                 return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
212             case EXIT_REASON_FULLSCREEN_SHORTCUT:
213                 return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
214             case EXIT_REASON_DESKTOP_MODE:
215                 return SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
216             case EXIT_REASON_FULLSCREEN_REQUEST:
217                 return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_REQUEST;
218             case EXIT_REASON_UNKNOWN:
219                 // Fall through
220             default:
221                 Slog.e("SplitscreenEventLogger", "Unknown exit reason: " + exitReason);
222                 return SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
223         }
224     }
225 
226     /**
227      * Logs when the user exits splitscreen.  Only one of the main or side stages should be
228      * specified to indicate which position was focused as a part of exiting (both can be unset).
229      */
logExit(@xitReason int exitReason, @SplitPosition int mainStagePosition, int mainStageUid, @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape)230     public void logExit(@ExitReason int exitReason,
231             @SplitPosition int mainStagePosition, int mainStageUid,
232             @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
233         if (mLoggerSessionId == null) {
234             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logExit: no-op, mLoggerSessionId is null");
235             // Ignore changes until we've started logging the session
236             return;
237         }
238         if ((mainStagePosition != SPLIT_POSITION_UNDEFINED
239                 && sideStagePosition != SPLIT_POSITION_UNDEFINED)
240                         || (mainStageUid != 0 && sideStageUid != 0)) {
241             ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
242                     "logExit: no-op, only main or side stage should be set, not both/none");
243             throw new IllegalArgumentException("Only main or side stage should be set");
244         }
245 
246         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logExit: exitReason=%d mainStagePosition=%d"
247                         + " mainStageUid=%d sideStagePosition=%d sideStageUid=%d isLandscape=%b"
248                         + " mLoggerSessionId=%d", getLoggerExitReason(exitReason),
249                 getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), mainStageUid,
250                 getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), sideStageUid,
251                 isLandscape, mLoggerSessionId.getId());
252 
253         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
254                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT,
255                 0 /* enterReason */,
256                 getLoggerExitReason(exitReason),
257                 0f /* splitRatio */,
258                 getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
259                 mainStageUid,
260                 getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
261                 sideStageUid,
262                 0 /* dragInstanceId */,
263                 mLoggerSessionId.getId());
264 
265         // Reset states
266         mLoggerSessionId = null;
267         mDragEnterPosition = SPLIT_POSITION_UNDEFINED;
268         mEnterSessionId = null;
269         mLastMainStagePosition = -1;
270         mLastMainStageUid = -1;
271         mLastSideStagePosition = -1;
272         mLastSideStageUid = -1;
273         mEnterReason = ENTER_REASON_UNKNOWN;
274     }
275 
276     /**
277      * Logs when an app in the main stage changes.
278      */
logMainStageAppChange(@plitPosition int mainStagePosition, int mainStageUid, boolean isLandscape)279     public void logMainStageAppChange(@SplitPosition int mainStagePosition, int mainStageUid,
280             boolean isLandscape) {
281         if (mLoggerSessionId == null) {
282             // Ignore changes until we've started logging the session
283             return;
284         }
285         if (!updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition,
286                 isLandscape), mainStageUid)) {
287             // Ignore if there are no user perceived changes
288             return;
289         }
290 
291         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
292                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE,
293                 0 /* enterReason */,
294                 0 /* exitReason */,
295                 0f /* splitRatio */,
296                 mLastMainStagePosition,
297                 mLastMainStageUid,
298                 0 /* sideStagePosition */,
299                 0 /* sideStageUid */,
300                 0 /* dragInstanceId */,
301                 mLoggerSessionId.getId());
302     }
303 
304     /**
305      * Logs when an app in the side stage changes.
306      */
logSideStageAppChange(@plitPosition int sideStagePosition, int sideStageUid, boolean isLandscape)307     public void logSideStageAppChange(@SplitPosition int sideStagePosition, int sideStageUid,
308             boolean isLandscape) {
309         if (mLoggerSessionId == null) {
310             // Ignore changes until we've started logging the session
311             return;
312         }
313         if (!updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition,
314                 isLandscape), sideStageUid)) {
315             // Ignore if there are no user perceived changes
316             return;
317         }
318 
319         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
320                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE,
321                 0 /* enterReason */,
322                 0 /* exitReason */,
323                 0f /* splitRatio */,
324                 0 /* mainStagePosition */,
325                 0 /* mainStageUid */,
326                 mLastSideStagePosition,
327                 mLastSideStageUid,
328                 0 /* dragInstanceId */,
329                 mLoggerSessionId.getId());
330     }
331 
332     /**
333      * Logs when the splitscreen ratio changes.
334      */
logResize(float splitRatio)335     public void logResize(float splitRatio) {
336         if (mLoggerSessionId == null) {
337             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: no-op, mLoggerSessionId is null");
338             // Ignore changes until we've started logging the session
339             return;
340         }
341         if (splitRatio <= 0f || splitRatio >= 1f) {
342             ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
343                     "logResize: no-op, splitRatio indicates that user is dismissing, not resizing");
344             // Don't bother reporting resizes that end up dismissing the split, that will be logged
345             // via the exit event
346             return;
347         }
348         if (!updateSplitRatioState(splitRatio)) {
349             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: no-op, split ratio was not changed");
350             // Ignore if there are no user perceived changes
351             return;
352         }
353 
354         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: splitRatio=%f mLoggerSessionId=%d",
355                 mLastSplitRatio, mLoggerSessionId.getId());
356         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
357                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE,
358                 0 /* enterReason */,
359                 0 /* exitReason */,
360                 mLastSplitRatio,
361                 mLastMainStagePosition,
362                 mLastMainStageUid,
363                 mLastSideStagePosition,
364                 mLastSideStageUid,
365                 0 /* dragInstanceId */,
366                 mLoggerSessionId.getId());
367     }
368 
369     /**
370      * Logs when the apps in splitscreen are swapped.
371      */
logSwap(@plitPosition int mainStagePosition, int mainStageUid, @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape)372     public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid,
373             @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
374         if (mLoggerSessionId == null) {
375             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logSwap: no-op, mLoggerSessionId is null");
376             // Ignore changes until we've started logging the session
377             return;
378         }
379 
380         updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
381                 mainStageUid);
382         updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
383                 sideStageUid);
384 
385         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logSwap: mainStagePosition=%d mainStageUid=%d "
386                 + "sideStagePosition=%d sideStageUid=%d mLoggerSessionId=%d",
387                 mLastMainStagePosition, mLastMainStageUid, mLastSideStagePosition,
388                 mLastSideStageUid, mLoggerSessionId.getId());
389         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
390                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP,
391                 0 /* enterReason */,
392                 0 /* exitReason */,
393                 0f /* splitRatio */,
394                 mLastMainStagePosition,
395                 mLastMainStageUid,
396                 mLastSideStagePosition,
397                 mLastSideStageUid,
398                 0 /* dragInstanceId */,
399                 mLoggerSessionId.getId());
400     }
401 
updateMainStageState(int mainStagePosition, int mainStageUid)402     private boolean updateMainStageState(int mainStagePosition, int mainStageUid) {
403         boolean changed = (mLastMainStagePosition != mainStagePosition)
404                 || (mLastMainStageUid != mainStageUid);
405         if (!changed) {
406             return false;
407         }
408 
409         mLastMainStagePosition = mainStagePosition;
410         mLastMainStageUid = mainStageUid;
411         return true;
412     }
413 
updateSideStageState(int sideStagePosition, int sideStageUid)414     private boolean updateSideStageState(int sideStagePosition, int sideStageUid) {
415         boolean changed = (mLastSideStagePosition != sideStagePosition)
416                 || (mLastSideStageUid != sideStageUid);
417         if (!changed) {
418             return false;
419         }
420 
421         mLastSideStagePosition = sideStagePosition;
422         mLastSideStageUid = sideStageUid;
423         return true;
424     }
425 
updateSplitRatioState(float splitRatio)426     private boolean updateSplitRatioState(float splitRatio) {
427         boolean changed = Float.compare(mLastSplitRatio, splitRatio) != 0;
428         if (!changed) {
429             return false;
430         }
431 
432         mLastSplitRatio = splitRatio;
433         return true;
434     }
435 
getDragEnterReasonFromSplitPosition(@plitPosition int position, boolean isLandscape)436     public int getDragEnterReasonFromSplitPosition(@SplitPosition int position,
437             boolean isLandscape) {
438         if (isLandscape) {
439             return position == SPLIT_POSITION_TOP_OR_LEFT
440                     ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_LEFT
441                     : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_RIGHT;
442         } else {
443             return position == SPLIT_POSITION_TOP_OR_LEFT
444                     ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_TOP
445                     : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_BOTTOM;
446         }
447     }
448 
getMainStagePositionFromSplitPosition(@plitPosition int position, boolean isLandscape)449     private int getMainStagePositionFromSplitPosition(@SplitPosition int position,
450             boolean isLandscape) {
451         if (position == SPLIT_POSITION_UNDEFINED) {
452             return 0;
453         }
454         if (isLandscape) {
455             return position == SPLIT_POSITION_TOP_OR_LEFT
456                     ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__LEFT
457                     : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__RIGHT;
458         } else {
459             return position == SPLIT_POSITION_TOP_OR_LEFT
460                     ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__TOP
461                     : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__BOTTOM;
462         }
463     }
464 
getSideStagePositionFromSplitPosition(@plitPosition int position, boolean isLandscape)465     private int getSideStagePositionFromSplitPosition(@SplitPosition int position,
466             boolean isLandscape) {
467         if (position == SPLIT_POSITION_UNDEFINED) {
468             return 0;
469         }
470         if (isLandscape) {
471             return position == SPLIT_POSITION_TOP_OR_LEFT
472                     ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__LEFT
473                     : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__RIGHT;
474         } else {
475             return position == SPLIT_POSITION_TOP_OR_LEFT
476                     ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__TOP
477                     : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__BOTTOM;
478         }
479     }
480 }
481