1 /*
2  * Copyright (C) 2011 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.wm;
18 
19 import static android.Manifest.permission.ALWAYS_UPDATE_WALLPAPER;
20 import static android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
21 import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS;
22 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
23 import static android.Manifest.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION;
24 import static android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS;
25 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
26 import static android.Manifest.permission.STATUS_BAR_SERVICE;
27 import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
28 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
29 import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
30 import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
31 import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
32 import static android.content.Intent.EXTRA_PACKAGE_NAME;
33 import static android.content.Intent.EXTRA_SHORTCUT_ID;
34 import static android.content.Intent.EXTRA_TASK_ID;
35 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
36 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
37 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
38 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
39 
40 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
41 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
42 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
43 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
44 import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
45 
46 import android.annotation.NonNull;
47 import android.annotation.Nullable;
48 import android.app.PendingIntent;
49 import android.content.ClipData;
50 import android.content.ClipDescription;
51 import android.content.Intent;
52 import android.content.pm.ActivityInfo;
53 import android.content.pm.ShortcutServiceInternal;
54 import android.graphics.Rect;
55 import android.graphics.Region;
56 import android.os.Binder;
57 import android.os.Bundle;
58 import android.os.IBinder;
59 import android.os.Parcel;
60 import android.os.Process;
61 import android.os.RemoteCallback;
62 import android.os.RemoteException;
63 import android.os.Trace;
64 import android.os.UserHandle;
65 import android.text.TextUtils;
66 import android.util.ArraySet;
67 import android.util.MergedConfiguration;
68 import android.util.Slog;
69 import android.view.IWindow;
70 import android.view.IWindowId;
71 import android.view.IWindowSession;
72 import android.view.IWindowSessionCallback;
73 import android.view.InputChannel;
74 import android.view.InsetsSourceControl;
75 import android.view.InsetsState;
76 import android.view.SurfaceControl;
77 import android.view.SurfaceSession;
78 import android.view.View;
79 import android.view.View.FocusDirection;
80 import android.view.WindowInsets;
81 import android.view.WindowInsets.Type.InsetsType;
82 import android.view.WindowManager;
83 import android.view.WindowRelayoutResult;
84 import android.window.ClientWindowFrames;
85 import android.window.InputTransferToken;
86 import android.window.OnBackInvokedCallbackInfo;
87 
88 import com.android.internal.annotations.VisibleForTesting;
89 import com.android.internal.os.logging.MetricsLoggerWrapper;
90 import com.android.internal.protolog.common.ProtoLog;
91 import com.android.server.LocalServices;
92 import com.android.server.wm.WindowManagerService.H;
93 import com.android.window.flags.Flags;
94 
95 import java.io.PrintWriter;
96 import java.util.ArrayList;
97 import java.util.Collections;
98 import java.util.List;
99 import java.util.function.BiConsumer;
100 
101 /**
102  * This class represents an active client session.  There is generally one
103  * Session object per process that is interacting with the window manager.
104  */
105 class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
106     final WindowManagerService mService;
107     final IWindowSessionCallback mCallback;
108     final int mUid;
109     final int mPid;
110     @NonNull
111     final WindowProcessController mProcess;
112     private final String mStringName;
113     SurfaceSession mSurfaceSession;
114     private final ArrayList<WindowState> mAddedWindows = new ArrayList<>();
115     /** Set of visible alert/app-overlay window surfaces connected to this session. */
116     private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
117     private final DragDropController mDragDropController;
118     final boolean mCanAddInternalSystemWindow;
119     boolean mCanForceShowingInsets;
120     private final boolean mCanStartTasksFromRecents;
121 
122     final boolean mCanCreateSystemApplicationOverlay;
123     final boolean mCanHideNonSystemOverlayWindows;
124     final boolean mCanSetUnrestrictedGestureExclusion;
125     final boolean mCanAlwaysUpdateWallpaper;
126     private AlertWindowNotification mAlertWindowNotification;
127     private boolean mShowingAlertWindowNotificationAllowed;
128     private boolean mClientDead = false;
129     private float mLastReportedAnimatorScale;
130     protected String mPackageName;
131     private String mRelayoutTag;
132     private final InsetsSourceControl.Array mDummyControls =  new InsetsSourceControl.Array();
133     final boolean mSetsUnrestrictedKeepClearAreas;
134 
Session(WindowManagerService service, IWindowSessionCallback callback)135     public Session(WindowManagerService service, IWindowSessionCallback callback) {
136         this(service, callback, Binder.getCallingPid(), Binder.getCallingUid());
137     }
138 
139     @VisibleForTesting
Session(WindowManagerService service, IWindowSessionCallback callback, int callingPid, int callingUid)140     Session(WindowManagerService service, IWindowSessionCallback callback,
141             int callingPid, int callingUid) {
142         mService = service;
143         mCallback = callback;
144         mPid = callingPid;
145         mUid = callingUid;
146         synchronized (service.mGlobalLock) {
147             mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
148             mProcess = service.mAtmService.mProcessMap.getProcess(mPid);
149         }
150         if (mProcess == null) {
151             throw new IllegalStateException("Unknown pid=" + mPid + " uid=" + mUid);
152         }
153         mCanAddInternalSystemWindow = service.mContext.checkCallingOrSelfPermission(
154                 INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED;
155         mCanForceShowingInsets = service.mAtmService.isCallerRecents(mUid)
156                 || service.mContext.checkCallingOrSelfPermission(STATUS_BAR_SERVICE)
157                 == PERMISSION_GRANTED;
158         mCanHideNonSystemOverlayWindows = service.mContext.checkCallingOrSelfPermission(
159                 HIDE_NON_SYSTEM_OVERLAY_WINDOWS) == PERMISSION_GRANTED
160                 || service.mContext.checkCallingOrSelfPermission(HIDE_OVERLAY_WINDOWS)
161                 == PERMISSION_GRANTED;
162         mCanCreateSystemApplicationOverlay =
163                 service.mContext.checkCallingOrSelfPermission(SYSTEM_APPLICATION_OVERLAY)
164                         == PERMISSION_GRANTED;
165         mCanStartTasksFromRecents = service.mContext.checkCallingOrSelfPermission(
166                 START_TASKS_FROM_RECENTS) == PERMISSION_GRANTED;
167         mSetsUnrestrictedKeepClearAreas =
168                 service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_KEEP_CLEAR_AREAS)
169                         == PERMISSION_GRANTED;
170         mCanSetUnrestrictedGestureExclusion =
171                 service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_GESTURE_EXCLUSION)
172                         == PERMISSION_GRANTED;
173         mCanAlwaysUpdateWallpaper = Flags.alwaysUpdateWallpaperPermission()
174                 && service.mContext.checkCallingOrSelfPermission(ALWAYS_UPDATE_WALLPAPER)
175                         == PERMISSION_GRANTED;
176         mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
177         mDragDropController = mService.mDragDropController;
178         StringBuilder sb = new StringBuilder();
179         sb.append("Session{");
180         sb.append(Integer.toHexString(System.identityHashCode(this)));
181         sb.append(" ");
182         sb.append(mPid);
183         if (mUid < Process.FIRST_APPLICATION_UID) {
184             sb.append(":");
185             sb.append(mUid);
186         } else {
187             sb.append(":u");
188             sb.append(UserHandle.getUserId(mUid));
189             sb.append('a');
190             sb.append(UserHandle.getAppId(mUid));
191         }
192         sb.append("}");
193         mStringName = sb.toString();
194 
195         try {
196             mCallback.asBinder().linkToDeath(this, 0);
197         } catch (RemoteException e) {
198             mClientDead = true;
199         }
200     }
201 
202     @Override
onTransact(int code, Parcel data, Parcel reply, int flags)203     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
204             throws RemoteException {
205         try {
206             return super.onTransact(code, data, reply, flags);
207         } catch (RuntimeException e) {
208             // Log all 'real' exceptions thrown to the caller
209             if (!(e instanceof SecurityException)) {
210                 Slog.wtf(TAG_WM, "Window Session Crash", e);
211             }
212             throw e;
213         }
214     }
215 
isClientDead()216     boolean isClientDead() {
217         return mClientDead;
218     }
219 
220     @Override
binderDied()221     public void binderDied() {
222         synchronized (mService.mGlobalLock) {
223             mCallback.asBinder().unlinkToDeath(this, 0);
224             mClientDead = true;
225             try {
226                 for (int i = mAddedWindows.size() - 1; i >= 0; i--) {
227                     final WindowState w = mAddedWindows.get(i);
228                     Slog.i(TAG_WM, "WIN DEATH: " + w);
229                     if (w.mActivityRecord != null && w.mActivityRecord.findMainWindow() == w) {
230                         mService.mSnapshotController.onAppDied(w.mActivityRecord);
231                     }
232                     w.removeIfPossible();
233                 }
234             } finally {
235                 killSessionLocked();
236             }
237         }
238     }
239 
240     @Override
addToDisplay(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, @InsetsType int requestedVisibleTypes, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame, float[] outSizeCompatScale)241     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
242             int viewVisibility, int displayId, @InsetsType int requestedVisibleTypes,
243             InputChannel outInputChannel, InsetsState outInsetsState,
244             InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
245             float[] outSizeCompatScale) {
246         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
247                 UserHandle.getUserId(mUid), requestedVisibleTypes, outInputChannel, outInsetsState,
248                 outActiveControls, outAttachedFrame, outSizeCompatScale);
249     }
250 
251     @Override
addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, int userId, @InsetsType int requestedVisibleTypes, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame, float[] outSizeCompatScale)252     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
253             int viewVisibility, int displayId, int userId, @InsetsType int requestedVisibleTypes,
254             InputChannel outInputChannel, InsetsState outInsetsState,
255             InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
256             float[] outSizeCompatScale) {
257         return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
258                 requestedVisibleTypes, outInputChannel, outInsetsState, outActiveControls,
259                 outAttachedFrame, outSizeCompatScale);
260     }
261 
262     @Override
addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, InsetsState outInsetsState, Rect outAttachedFrame, float[] outSizeCompatScale)263     public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
264             int viewVisibility, int displayId, InsetsState outInsetsState, Rect outAttachedFrame,
265             float[] outSizeCompatScale) {
266         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
267                 UserHandle.getUserId(mUid), WindowInsets.Type.defaultVisible(),
268                 null /* outInputChannel */, outInsetsState, mDummyControls, outAttachedFrame,
269                 outSizeCompatScale);
270     }
271 
272     @Override
remove(IBinder clientToken)273     public void remove(IBinder clientToken) {
274         mService.removeClientToken(this, clientToken);
275     }
276 
277     @Override
cancelDraw(IWindow window)278     public boolean cancelDraw(IWindow window) {
279         return mService.cancelDraw(this, window);
280     }
281 
282     @Override
relayout(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq, int lastSyncSeqId, WindowRelayoutResult outRelayoutResult)283     public int relayout(IWindow window, WindowManager.LayoutParams attrs,
284             int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
285             int lastSyncSeqId, WindowRelayoutResult outRelayoutResult) {
286         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
287         int res = mService.relayoutWindow(this, window, attrs, requestedWidth,
288                 requestedHeight, viewFlags, flags, seq, lastSyncSeqId, outRelayoutResult);
289         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
290         return res;
291     }
292 
293     /** @deprecated */
294     @Deprecated
295     @Override
relayoutLegacy(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq, int lastSyncSeqId, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Bundle outBundle)296     public int relayoutLegacy(IWindow window, WindowManager.LayoutParams attrs,
297             int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
298             int lastSyncSeqId, ClientWindowFrames outFrames,
299             MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl,
300             InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
301             Bundle outBundle) {
302         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
303         int res = mService.relayoutWindow(this, window, attrs,
304                 requestedWidth, requestedHeight, viewFlags, flags, seq,
305                 lastSyncSeqId, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
306                 outActiveControls, outBundle);
307         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
308         return res;
309     }
310 
311     @Override
relayoutAsync(IWindow window, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq, int lastSyncSeqId)312     public void relayoutAsync(IWindow window, WindowManager.LayoutParams attrs,
313             int requestedWidth, int requestedHeight, int viewFlags, int flags, int seq,
314             int lastSyncSeqId) {
315         if (windowSessionRelayoutInfo()) {
316             relayout(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
317                     lastSyncSeqId, null /* outRelayoutResult */);
318         } else {
319             relayoutLegacy(window, attrs, requestedWidth, requestedHeight, viewFlags, flags, seq,
320                     lastSyncSeqId, null /* outFrames */, null /* mergedConfiguration */,
321                     null /* outSurfaceControl */, null /* outInsetsState */,
322                     null /* outActiveControls */, null /* outSyncIdBundle */);
323         }
324     }
325 
326     @Override
outOfMemory(IWindow window)327     public boolean outOfMemory(IWindow window) {
328         return mService.outOfMemoryWindow(this, window);
329     }
330 
331     @Override
setInsets(IWindow window, int touchableInsets, Rect contentInsets, Rect visibleInsets, Region touchableArea)332     public void setInsets(IWindow window, int touchableInsets,
333             Rect contentInsets, Rect visibleInsets, Region touchableArea) {
334         mService.setInsetsWindow(this, window, touchableInsets, contentInsets,
335                 visibleInsets, touchableArea);
336     }
337 
338     @Override
clearTouchableRegion(IWindow window)339     public void clearTouchableRegion(IWindow window) {
340         mService.clearTouchableRegion(this, window);
341     }
342 
343     @Override
finishDrawing(IWindow window, @Nullable SurfaceControl.Transaction postDrawTransaction, int seqId)344     public void finishDrawing(IWindow window,
345             @Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
346         if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window);
347         if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
348             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishDrawing: " + mPackageName);
349         }
350         mService.finishDrawingWindow(this, window, postDrawTransaction, seqId);
351         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
352     }
353 
354     @Override
performHapticFeedback(int effectId, boolean always, boolean fromIme)355     public boolean performHapticFeedback(int effectId, boolean always, boolean fromIme) {
356         final long ident = Binder.clearCallingIdentity();
357         try {
358             return mService.mPolicy.performHapticFeedback(mUid, mPackageName,
359                         effectId, always, null, fromIme);
360         } finally {
361             Binder.restoreCallingIdentity(ident);
362         }
363     }
364 
365     @Override
performHapticFeedbackAsync(int effectId, boolean always, boolean fromIme)366     public void performHapticFeedbackAsync(int effectId, boolean always, boolean fromIme) {
367         performHapticFeedback(effectId, always, fromIme);
368     }
369 
370     /* Drag/drop */
371 
372     @Override
performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data)373     public IBinder performDrag(IWindow window, int flags, SurfaceControl surface, int touchSource,
374             int touchDeviceId, int touchPointerId, float touchX, float touchY, float thumbCenterX,
375             float thumbCenterY, ClipData data) {
376         final int callingUid = Binder.getCallingUid();
377         final int callingPid = Binder.getCallingPid();
378         // Validate and resolve ClipDescription data before clearing the calling identity
379         validateAndResolveDragMimeTypeExtras(data, callingUid, callingPid, mPackageName);
380         validateDragFlags(flags);
381         final long ident = Binder.clearCallingIdentity();
382         try {
383             return mDragDropController.performDrag(mPid, mUid, window, flags, surface, touchSource,
384                     touchDeviceId, touchPointerId, touchX, touchY, thumbCenterX, thumbCenterY,
385                     data);
386         } finally {
387             Binder.restoreCallingIdentity(ident);
388         }
389     }
390 
391 
392     @Override
dropForAccessibility(IWindow window, int x, int y)393     public boolean dropForAccessibility(IWindow window, int x, int y) {
394         final long ident = Binder.clearCallingIdentity();
395         try {
396             return mDragDropController.dropForAccessibility(window, x, y);
397         } finally {
398             Binder.restoreCallingIdentity(ident);
399         }
400     }
401 
402     /**
403      * Validates the given drag flags.
404      */
405     @VisibleForTesting
validateDragFlags(int flags)406     void validateDragFlags(int flags) {
407         if ((flags & View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
408             if (!mCanStartTasksFromRecents) {
409                 throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
410             }
411         }
412     }
413 
414     /**
415      * Validates the given drag data.
416      */
417     @VisibleForTesting
validateAndResolveDragMimeTypeExtras(ClipData data, int callingUid, int callingPid, String callingPackage)418     void validateAndResolveDragMimeTypeExtras(ClipData data, int callingUid, int callingPid,
419             String callingPackage) {
420         final ClipDescription desc = data != null ? data.getDescription() : null;
421         if (desc == null) {
422             return;
423         }
424         // Ensure that only one of the app mime types are set
425         final boolean hasActivity = desc.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY);
426         final boolean hasShortcut = desc.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
427         final boolean hasTask = desc.hasMimeType(MIMETYPE_APPLICATION_TASK);
428         int appMimeTypeCount = (hasActivity ? 1 : 0)
429                 + (hasShortcut ? 1 : 0)
430                 + (hasTask ? 1 : 0);
431         if (appMimeTypeCount == 0) {
432             return;
433         } else if (appMimeTypeCount > 1) {
434             throw new IllegalArgumentException("Can not specify more than one of activity, "
435                     + "shortcut, or task mime types");
436         }
437         // Ensure that data is provided and that they are intents
438         if (data.getItemCount() == 0) {
439             throw new IllegalArgumentException("Unexpected number of items (none)");
440         }
441         for (int i = 0; i < data.getItemCount(); i++) {
442             if (data.getItemAt(i).getIntent() == null) {
443                 throw new IllegalArgumentException("Unexpected item, expected an intent");
444             }
445         }
446 
447         if (hasActivity) {
448             long origId = Binder.clearCallingIdentity();
449             try {
450                 // Resolve the activity info for each intent
451                 for (int i = 0; i < data.getItemCount(); i++) {
452                     final ClipData.Item item = data.getItemAt(i);
453                     final Intent intent = item.getIntent();
454                     final PendingIntent pi = intent.getParcelableExtra(
455                             ClipDescription.EXTRA_PENDING_INTENT);
456                     final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
457                     if (pi == null || user == null) {
458                         throw new IllegalArgumentException("Clip data must include the pending "
459                                 + "intent to launch and its associated user to launch for.");
460                     }
461                     final Intent launchIntent = mService.mAmInternal.getIntentForIntentSender(
462                             pi.getIntentSender().getTarget());
463                     final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
464                             launchIntent, null /* resolvedType */, user.getIdentifier(),
465                             callingUid, callingPid);
466                     item.setActivityInfo(info);
467                 }
468             } finally {
469                 Binder.restoreCallingIdentity(origId);
470             }
471         } else if (hasShortcut) {
472             // Restrict who can start a shortcut drag since it will start the shortcut as the
473             // target shortcut package
474             if (!mCanStartTasksFromRecents) {
475                 throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
476             }
477             for (int i = 0; i < data.getItemCount(); i++) {
478                 final ClipData.Item item = data.getItemAt(i);
479                 final Intent intent = item.getIntent();
480                 final String shortcutId = intent.getStringExtra(EXTRA_SHORTCUT_ID);
481                 final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
482                 final UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
483                 if (TextUtils.isEmpty(shortcutId)
484                         || TextUtils.isEmpty(packageName)
485                         || user == null) {
486                     throw new IllegalArgumentException("Clip item must include the package name, "
487                             + "shortcut id, and the user to launch for.");
488                 }
489                 final ShortcutServiceInternal shortcutService =
490                         LocalServices.getService(ShortcutServiceInternal.class);
491                 final Intent[] shortcutIntents = shortcutService.createShortcutIntents(
492                         UserHandle.getUserId(callingUid), callingPackage, packageName, shortcutId,
493                         user.getIdentifier(), callingPid, callingUid);
494                 if (shortcutIntents == null || shortcutIntents.length == 0) {
495                     throw new IllegalArgumentException("Invalid shortcut id");
496                 }
497                 final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
498                         shortcutIntents[0], null /* resolvedType */, user.getIdentifier(),
499                         callingUid, callingPid);
500                 item.setActivityInfo(info);
501             }
502         } else if (hasTask) {
503             // TODO(b/169894807): Consider opening this up for tasks from the same app as the caller
504             if (!mCanStartTasksFromRecents) {
505                 throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
506             }
507             for (int i = 0; i < data.getItemCount(); i++) {
508                 final ClipData.Item item = data.getItemAt(i);
509                 final Intent intent = item.getIntent();
510                 final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
511                 if (taskId == INVALID_TASK_ID) {
512                     throw new IllegalArgumentException("Clip item must include the task id.");
513                 }
514                 final Task task = mService.mRoot.anyTaskForId(taskId);
515                 if (task == null) {
516                     throw new IllegalArgumentException("Invalid task id.");
517                 }
518                 if (task.getRootActivity() != null) {
519                     item.setActivityInfo(task.getRootActivity().info);
520                 } else {
521                     // Resolve the activity info manually if the task was restored after reboot
522                     final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
523                             task.intent, null /* resolvedType */, task.mUserId, callingUid,
524                             callingPid);
525                     item.setActivityInfo(info);
526                 }
527             }
528         }
529     }
530 
531     @Override
reportDropResult(IWindow window, boolean consumed)532     public void reportDropResult(IWindow window, boolean consumed) {
533         final long ident = Binder.clearCallingIdentity();
534         try {
535             mDragDropController.reportDropResult(window, consumed);
536         } finally {
537             Binder.restoreCallingIdentity(ident);
538         }
539     }
540 
541     @Override
cancelDragAndDrop(IBinder dragToken, boolean skipAnimation)542     public void cancelDragAndDrop(IBinder dragToken, boolean skipAnimation) {
543         final long ident = Binder.clearCallingIdentity();
544         try {
545             mDragDropController.cancelDragAndDrop(dragToken, skipAnimation);
546         } finally {
547             Binder.restoreCallingIdentity(ident);
548         }
549     }
550 
551     @Override
dragRecipientEntered(IWindow window)552     public void dragRecipientEntered(IWindow window) {
553         mDragDropController.dragRecipientEntered(window);
554     }
555 
556     @Override
dragRecipientExited(IWindow window)557     public void dragRecipientExited(IWindow window) {
558         mDragDropController.dragRecipientExited(window);
559     }
560 
561     @Override
startMovingTask(IWindow window, float startX, float startY)562     public boolean startMovingTask(IWindow window, float startX, float startY) {
563         if (DEBUG_TASK_POSITIONING) Slog.d(
564                 TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
565 
566         final long ident = Binder.clearCallingIdentity();
567         try {
568             return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
569         } finally {
570             Binder.restoreCallingIdentity(ident);
571         }
572     }
573 
574     @Override
finishMovingTask(IWindow window)575     public void finishMovingTask(IWindow window) {
576         if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishMovingTask");
577 
578         final long ident = Binder.clearCallingIdentity();
579         try {
580             mService.mTaskPositioningController.finishTaskPositioning(window);
581         } finally {
582             Binder.restoreCallingIdentity(ident);
583         }
584     }
585 
586     @Override
reportSystemGestureExclusionChanged(IWindow window, List<Rect> exclusionRects)587     public void reportSystemGestureExclusionChanged(IWindow window, List<Rect> exclusionRects) {
588         final long ident = Binder.clearCallingIdentity();
589         try {
590             mService.reportSystemGestureExclusionChanged(this, window, exclusionRects);
591         } finally {
592             Binder.restoreCallingIdentity(ident);
593         }
594     }
595 
596     @Override
reportDecorViewGestureInterceptionChanged(IWindow window, boolean intercepted)597     public void reportDecorViewGestureInterceptionChanged(IWindow window, boolean intercepted) {
598         final long ident = Binder.clearCallingIdentity();
599         try {
600             mService.reportDecorViewGestureChanged(this, window, intercepted);
601         } finally {
602             Binder.restoreCallingIdentity(ident);
603         }
604     }
605 
606     @Override
reportKeepClearAreasChanged(IWindow window, List<Rect> restricted, List<Rect> unrestricted)607     public void reportKeepClearAreasChanged(IWindow window, List<Rect> restricted,
608             List<Rect> unrestricted) {
609         if (!mSetsUnrestrictedKeepClearAreas && !unrestricted.isEmpty()) {
610             unrestricted = Collections.emptyList();
611         }
612 
613         final long ident = Binder.clearCallingIdentity();
614         try {
615             mService.reportKeepClearAreasChanged(this, window, restricted, unrestricted);
616         } finally {
617             Binder.restoreCallingIdentity(ident);
618         }
619     }
620 
actionOnWallpaper(IBinder window, BiConsumer<WallpaperController, WindowState> action)621     private void actionOnWallpaper(IBinder window,
622             BiConsumer<WallpaperController, WindowState> action) {
623         final WindowState windowState = mService.windowForClientLocked(this, window, true);
624         action.accept(windowState.getDisplayContent().mWallpaperController, windowState);
625     }
626 
627     @Override
setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep)628     public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
629         synchronized (mService.mGlobalLock) {
630             final long ident = Binder.clearCallingIdentity();
631             try {
632                 actionOnWallpaper(window, (wpController, windowState) ->
633                         wpController.setWindowWallpaperPosition(windowState, x, y, xStep, yStep));
634             } finally {
635                 Binder.restoreCallingIdentity(ident);
636             }
637         }
638     }
639 
640     @Override
setWallpaperZoomOut(IBinder window, float zoom)641     public void setWallpaperZoomOut(IBinder window, float zoom) {
642         if (Float.compare(0f, zoom) > 0 || Float.compare(1f, zoom) < 0 || Float.isNaN(zoom)) {
643             throw new IllegalArgumentException("Zoom must be a valid float between 0 and 1: "
644                     + zoom);
645         }
646         synchronized (mService.mGlobalLock) {
647             final long ident = Binder.clearCallingIdentity();
648             try {
649                 actionOnWallpaper(window, (wpController, windowState) ->
650                         wpController.setWallpaperZoomOut(windowState, zoom));
651             } finally {
652                 Binder.restoreCallingIdentity(ident);
653             }
654         }
655     }
656 
657     @Override
setShouldZoomOutWallpaper(IBinder window, boolean shouldZoom)658     public void setShouldZoomOutWallpaper(IBinder window, boolean shouldZoom) {
659         synchronized (mService.mGlobalLock) {
660             actionOnWallpaper(window, (wpController, windowState) ->
661                     wpController.setShouldZoomOutWallpaper(windowState, shouldZoom));
662         }
663     }
664 
665     @Override
wallpaperOffsetsComplete(IBinder window)666     public void wallpaperOffsetsComplete(IBinder window) {
667         synchronized (mService.mGlobalLock) {
668             actionOnWallpaper(window, (wpController, windowState) ->
669                     wpController.wallpaperOffsetsComplete(window));
670         }
671     }
672 
673     @Override
setWallpaperDisplayOffset(IBinder window, int x, int y)674     public void setWallpaperDisplayOffset(IBinder window, int x, int y) {
675         synchronized (mService.mGlobalLock) {
676             final long ident = Binder.clearCallingIdentity();
677             try {
678                 actionOnWallpaper(window, (wpController, windowState) ->
679                         wpController.setWindowWallpaperDisplayOffset(windowState, x, y));
680             } finally {
681                 Binder.restoreCallingIdentity(ident);
682             }
683         }
684     }
685 
686     @Override
sendWallpaperCommand(IBinder window, String action, int x, int y, int z, Bundle extras, boolean sync)687     public void sendWallpaperCommand(IBinder window, String action, int x, int y,
688             int z, Bundle extras, boolean sync) {
689         synchronized (mService.mGlobalLock) {
690             final long ident = Binder.clearCallingIdentity();
691             try {
692                 final WindowState windowState = mService.windowForClientLocked(this, window, true);
693                 WallpaperController wallpaperController =
694                         windowState.getDisplayContent().mWallpaperController;
695                 if (mCanAlwaysUpdateWallpaper
696                         || windowState == wallpaperController.getWallpaperTarget()
697                         || windowState == wallpaperController.getPrevWallpaperTarget()) {
698                     wallpaperController.sendWindowWallpaperCommandUnchecked(
699                             windowState, action, x, y, z, extras, sync);
700                 }
701             } finally {
702                 Binder.restoreCallingIdentity(ident);
703             }
704         }
705     }
706 
707     @Override
wallpaperCommandComplete(IBinder window, Bundle result)708     public void wallpaperCommandComplete(IBinder window, Bundle result) {
709         synchronized (mService.mGlobalLock) {
710             actionOnWallpaper(window, (wpController, windowState) ->
711                     wpController.wallpaperCommandComplete(window));
712         }
713     }
714 
715     @Override
onRectangleOnScreenRequested(IBinder token, Rect rectangle)716     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
717         synchronized (mService.mGlobalLock) {
718             final long identity = Binder.clearCallingIdentity();
719             try {
720                 mService.onRectangleOnScreenRequested(token, rectangle);
721             } finally {
722                 Binder.restoreCallingIdentity(identity);
723             }
724         }
725     }
726 
727     @Override
getWindowId(IBinder window)728     public IWindowId getWindowId(IBinder window) {
729         return mService.getWindowId(window);
730     }
731 
732     @Override
pokeDrawLock(IBinder window)733     public void pokeDrawLock(IBinder window) {
734         final long identity = Binder.clearCallingIdentity();
735         try {
736             mService.pokeDrawLock(this, window);
737         } finally {
738             Binder.restoreCallingIdentity(identity);
739         }
740     }
741 
742     @Override
updateTapExcludeRegion(IWindow window, Region region)743     public void updateTapExcludeRegion(IWindow window, Region region) {
744         final long identity = Binder.clearCallingIdentity();
745         try {
746             mService.updateTapExcludeRegion(window, region);
747         } finally {
748             Binder.restoreCallingIdentity(identity);
749         }
750     }
751 
752     @Override
updateRequestedVisibleTypes(IWindow window, @InsetsType int requestedVisibleTypes)753     public void updateRequestedVisibleTypes(IWindow window, @InsetsType int requestedVisibleTypes) {
754         synchronized (mService.mGlobalLock) {
755             final WindowState win = mService.windowForClientLocked(this, window,
756                     false /* throwOnError */);
757             if (win != null) {
758                 win.setRequestedVisibleTypes(requestedVisibleTypes);
759                 win.getDisplayContent().getInsetsPolicy().onRequestedVisibleTypesChanged(win);
760             }
761         }
762     }
763 
onWindowAdded(WindowState w)764     void onWindowAdded(WindowState w) {
765         if (mPackageName == null) {
766             mPackageName = mProcess.mInfo.packageName;
767             mRelayoutTag = "relayoutWindow: " + mPackageName;
768         }
769         if (mSurfaceSession == null) {
770             if (DEBUG) {
771                 Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
772             }
773             mSurfaceSession = new SurfaceSession();
774             ProtoLog.i(WM_SHOW_TRANSACTIONS, "  NEW SURFACE SESSION %s", mSurfaceSession);
775             mService.mSessions.add(this);
776             if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
777                 mService.dispatchNewAnimatorScaleLocked(this);
778             }
779             mProcess.mWindowSession = this;
780         }
781         mAddedWindows.add(w);
782     }
783 
onWindowRemoved(WindowState w)784     void onWindowRemoved(WindowState w) {
785         mAddedWindows.remove(w);
786         if (mAddedWindows.isEmpty()) {
787             killSessionLocked();
788         }
789     }
790 
hasWindow()791     boolean hasWindow() {
792         return !mAddedWindows.isEmpty();
793     }
794 
onWindowSurfaceVisibilityChanged(WindowSurfaceController surfaceController, boolean visible, int type)795     void onWindowSurfaceVisibilityChanged(WindowSurfaceController surfaceController,
796             boolean visible, int type) {
797 
798         if (!isSystemAlertWindowType(type)) {
799             return;
800         }
801 
802         boolean changed;
803         // Track non-system apps adding overlay/alert windows, so a notification can post for the
804         // user to control their visibility.
805         final boolean noSystemOverlayPermission =
806                 !mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay;
807         if (visible) {
808             changed = mAlertWindowSurfaces.add(surfaceController);
809             if (type == TYPE_APPLICATION_OVERLAY) {
810                 MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
811                         false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
812             } else if (noSystemOverlayPermission) {
813                 MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
814                         true /* only log for non-TYPE_APPLICATION_OVERLAY */);
815             }
816         } else {
817             changed = mAlertWindowSurfaces.remove(surfaceController);
818             if (type == TYPE_APPLICATION_OVERLAY) {
819                 MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
820                         false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
821             } else if (noSystemOverlayPermission) {
822                 MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
823                         true /* only log for non-TYPE_APPLICATION_OVERLAY */);
824             }
825         }
826 
827         if (changed && noSystemOverlayPermission) {
828             if (mAlertWindowSurfaces.isEmpty()) {
829                 cancelAlertWindowNotification();
830             } else if (mAlertWindowNotification == null && !isSatellitePointingUiPackage()) {
831                 mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
832                 if (mShowingAlertWindowNotificationAllowed) {
833                     mAlertWindowNotification.post();
834                 }
835             }
836         }
837 
838         if (changed && mPid != WindowManagerService.MY_PID) {
839             // Notify activity manager that the process contains overlay/alert windows, so it can
840             // adjust the importance score for the process.
841             setHasOverlayUi(!mAlertWindowSurfaces.isEmpty());
842         }
843     }
844 
845     // TODO b/349195999 - short term solution to not show the satellite pointing ui notification.
isSatellitePointingUiPackage()846     private boolean isSatellitePointingUiPackage() {
847         if (mPackageName == null || !mPackageName.equals(mService.mContext.getString(
848             com.android.internal.R.string.config_pointing_ui_package))) {
849             return false;
850         }
851         return ActivityTaskManagerService.checkPermission(
852             android.Manifest.permission.SATELLITE_COMMUNICATION, mPid, mUid) == PERMISSION_GRANTED;
853     }
854 
setShowingAlertWindowNotificationAllowed(boolean allowed)855     void setShowingAlertWindowNotificationAllowed(boolean allowed) {
856         mShowingAlertWindowNotificationAllowed = allowed;
857         if (mAlertWindowNotification != null) {
858             if (allowed) {
859                 mAlertWindowNotification.post();
860             } else {
861                 mAlertWindowNotification.cancel(false /* deleteChannel */);
862             }
863         }
864     }
865 
killSessionLocked()866     private void killSessionLocked() {
867         if (!mClientDead) {
868             return;
869         }
870 
871         mService.mSessions.remove(this);
872         if (mSurfaceSession == null) {
873             return;
874         }
875 
876         ProtoLog.i(WM_SHOW_TRANSACTIONS, "  KILL SURFACE SESSION %s", mSurfaceSession);
877         try {
878             mSurfaceSession.kill();
879         } catch (Exception e) {
880             Slog.w(TAG_WM, "Exception thrown when killing surface session " + mSurfaceSession
881                     + " in session " + this + ": " + e.toString());
882         }
883         mSurfaceSession = null;
884         mAddedWindows.clear();
885         mAlertWindowSurfaces.clear();
886         setHasOverlayUi(false);
887         cancelAlertWindowNotification();
888     }
889 
890     @VisibleForTesting
setHasOverlayUi(boolean hasOverlayUi)891     void setHasOverlayUi(boolean hasOverlayUi) {
892         mService.mH.obtainMessage(H.SET_HAS_OVERLAY_UI, mPid, hasOverlayUi ? 1 : 0).sendToTarget();
893     }
894 
cancelAlertWindowNotification()895     private void cancelAlertWindowNotification() {
896         if (mAlertWindowNotification == null) {
897             return;
898         }
899         mAlertWindowNotification.cancel(true /* deleteChannel */);
900         mAlertWindowNotification = null;
901     }
902 
dump(PrintWriter pw, String prefix)903     void dump(PrintWriter pw, String prefix) {
904         pw.print(prefix); pw.print("numWindow="); pw.print(mAddedWindows.size());
905                 pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
906                 pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
907                 pw.print(" mClientDead="); pw.print(mClientDead);
908                 pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
909         pw.print(prefix); pw.print("mPackageName="); pw.println(mPackageName);
910         if (isSatellitePointingUiPackage()) {
911             pw.print(prefix); pw.println("mIsSatellitePointingUiPackage=true");
912         }
913     }
914 
915     @Override
toString()916     public String toString() {
917         return mStringName;
918     }
919 
920     /** @return {@code true} if there is an alert window surface on the given display. */
hasAlertWindowSurfaces(DisplayContent displayContent)921     boolean hasAlertWindowSurfaces(DisplayContent displayContent) {
922         for (int i = mAlertWindowSurfaces.size() - 1; i >= 0; i--) {
923             final WindowSurfaceController surfaceController = mAlertWindowSurfaces.valueAt(i);
924             if (surfaceController.mAnimator.mWin.getDisplayContent() == displayContent) {
925                 return true;
926             }
927         }
928         return false;
929     }
930 
931     @Override
grantInputChannel(int displayId, SurfaceControl surface, IBinder clientToken, @Nullable InputTransferToken hostInputTransferToken, int flags, int privateFlags, int inputFeatures, int type, IBinder windowToken, InputTransferToken inputTransferToken, String inputHandleName, InputChannel outInputChannel)932     public void grantInputChannel(int displayId, SurfaceControl surface,
933             IBinder clientToken, @Nullable InputTransferToken hostInputTransferToken, int flags,
934             int privateFlags, int inputFeatures, int type, IBinder windowToken,
935             InputTransferToken inputTransferToken, String inputHandleName,
936             InputChannel outInputChannel) {
937         if (hostInputTransferToken == null && !mCanAddInternalSystemWindow) {
938             // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to
939             // embedded windows without providing a host window input token
940             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
941         }
942 
943         final long identity = Binder.clearCallingIdentity();
944         try {
945             mService.grantInputChannel(this, mUid, mPid, displayId, surface, clientToken,
946                     hostInputTransferToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0,
947                     inputFeatures, type, windowToken, inputTransferToken, inputHandleName,
948                     outInputChannel);
949         } finally {
950             Binder.restoreCallingIdentity(identity);
951         }
952     }
953 
954     @Override
updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, int flags, int privateFlags, int inputFeatures, Region region)955     public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
956             int flags, int privateFlags, int inputFeatures, Region region) {
957         final long identity = Binder.clearCallingIdentity();
958         try {
959             mService.updateInputChannel(channelToken, displayId, surface, flags,
960                     mCanAddInternalSystemWindow ? privateFlags : 0, inputFeatures, region);
961         } finally {
962             Binder.restoreCallingIdentity(identity);
963         }
964     }
965 
966     @Override
grantEmbeddedWindowFocus(IWindow callingWindow, InputTransferToken targetInputToken, boolean grantFocus)967     public void grantEmbeddedWindowFocus(IWindow callingWindow, InputTransferToken targetInputToken,
968                                          boolean grantFocus) {
969         final long identity = Binder.clearCallingIdentity();
970         try {
971             if (callingWindow == null) {
972                 if (!mCanAddInternalSystemWindow) {
973                     // Callers without INTERNAL_SYSTEM_WINDOW permission cannot request focus on
974                     // embedded windows without providing the calling window
975                     throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
976                 }
977                 mService.grantEmbeddedWindowFocus(this, targetInputToken, grantFocus);
978             } else {
979                 mService.grantEmbeddedWindowFocus(this, callingWindow, targetInputToken,
980                         grantFocus);
981             }
982         } finally {
983             Binder.restoreCallingIdentity(identity);
984         }
985     }
986 
987     @Override
moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction)988     public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
989         final long identity = Binder.clearCallingIdentity();
990         try {
991             synchronized (mService.mGlobalLock) {
992                 final WindowState win =
993                         mService.windowForClientLocked(this, fromWindow, false /* throwOnError */);
994                 if (win == null) {
995                     return false;
996                 }
997                 return mService.moveFocusToAdjacentWindow(win, direction);
998             }
999         } finally {
1000             Binder.restoreCallingIdentity(identity);
1001         }
1002     }
1003 
1004     @Override
generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback)1005     public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
1006             RemoteCallback callback) {
1007         final long origId = Binder.clearCallingIdentity();
1008         try {
1009             mService.generateDisplayHash(this, window, boundsInWindow, hashAlgorithm, callback);
1010         } finally {
1011             Binder.restoreCallingIdentity(origId);
1012         }
1013     }
1014 
1015     @Override
setOnBackInvokedCallbackInfo( IWindow window, OnBackInvokedCallbackInfo callbackInfo)1016     public void setOnBackInvokedCallbackInfo(
1017             IWindow window,
1018             OnBackInvokedCallbackInfo callbackInfo) {
1019         synchronized (mService.mGlobalLock) {
1020             WindowState windowState = mService.windowForClientLocked(this, window, false);
1021             if (windowState == null) {
1022                 Slog.i(TAG_WM,
1023                         "setOnBackInvokedCallback(): No window state for package:" + mPackageName);
1024             } else {
1025                 windowState.setOnBackInvokedCallbackInfo(callbackInfo);
1026             }
1027         }
1028     }
1029 }
1030