Loading core/java/android/app/ActivityManagerInternal.java +9 −3 Original line number Diff line number Diff line Loading @@ -429,11 +429,17 @@ public abstract class ActivityManagerInternal { int userId, int[] appIdWhitelist); /** * Add or delete uid from the ActivityManagerService PendingStartActivityUids list. * Add uid to the ActivityManagerService PendingStartActivityUids list. * @param uid uid * @param pending add to the list if true, delete from list if false. * @param pid pid of the ProcessRecord that is pending top. */ public abstract void updatePendingTopUid(int uid, boolean pending); public abstract void addPendingTopUid(int uid, int pid); /** * Delete uid from the ActivityManagerService PendingStartActivityUids list. * @param uid uid */ public abstract void deletePendingTopUid(int uid); /** * Is the uid in ActivityManagerService PendingStartActivityUids list? Loading services/core/java/com/android/server/am/ActivityManagerService.java +24 −64 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS; import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL; import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AppOpsManager.OP_NONE; Loading Loading @@ -299,7 +300,6 @@ import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; Loading Loading @@ -421,7 +421,6 @@ public class ActivityManagerService extends IActivityManager.Stub * Priority we boost main thread and RT of top app to. */ public static final int TOP_APP_PRIORITY_BOOST = -10; private static final String SYSTEM_PROPERTY_DEVICE_PROVISIONED = "persist.sys.device_provisioned"; Loading Loading @@ -823,45 +822,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } /** * While starting activity, WindowManager posts a runnable to DisplayThread to updateOomAdj. * The latency of the thread switch could cause client app failure when the app is checking * {@link #isUidActive} before updateOomAdj is done. * * Use PendingStartActivityUids to save uid after WindowManager start activity and before * updateOomAdj is done. * * <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock! */ final PendingStartActivityUids mPendingStartActivityUidsLocked = new PendingStartActivityUids(); final class PendingStartActivityUids { // Key is uid, value is SystemClock.elapsedRealtime() when the key is added. private final SparseLongArray mPendingUids = new SparseLongArray(); void add(int uid) { if (mPendingUids.indexOfKey(uid) < 0) { mPendingUids.put(uid, SystemClock.elapsedRealtime()); } } void delete(int uid) { if (mPendingUids.indexOfKey(uid) >= 0) { long delay = SystemClock.elapsedRealtime() - mPendingUids.get(uid); if (delay >= 1000) { Slog.wtf(TAG, "PendingStartActivityUids startActivity to updateOomAdj delay:" + delay + "ms," + " uid:" + uid + " packageName:" + Settings.getPackageNameForUid(mContext, uid)); } mPendingUids.delete(uid); } } boolean isPendingTopUid(int uid) { return mPendingUids.indexOfKey(uid) >= 0; } } private final PendingStartActivityUids mPendingStartActivityUids; /** * Puts the process record in the map. Loading Loading @@ -2585,6 +2546,7 @@ public class ActivityManagerService extends IActivityManager.Stub mFactoryTest = FACTORY_TEST_OFF; mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mInternal = new LocalService(); mPendingStartActivityUids = new PendingStartActivityUids(mContext); } // Note: This method is invoked on the main thread but may need to attach various Loading Loading @@ -2742,6 +2704,7 @@ public class ActivityManagerService extends IActivityManager.Stub } mInternal = new LocalService(); mPendingStartActivityUids = new PendingStartActivityUids(mContext); } public void setSystemServiceManager(SystemServiceManager mgr) { Loading Loading @@ -6093,9 +6056,18 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (mPidsSelfLocked) { for (int i = 0; i < pids.length; i++) { ProcessRecord pr = mPidsSelfLocked.get(pids[i]); states[i] = (pr == null) ? PROCESS_STATE_NONEXISTENT : pr.getCurProcState(); if (pr != null) { final boolean isPendingTop = mPendingStartActivityUids.isPendingTopPid(pr.uid, pids[i]); states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.getCurProcState(); if (scores != null) { scores[i] = isPendingTop ? ProcessList.FOREGROUND_APP_ADJ : pr.curAdj; } } else { states[i] = PROCESS_STATE_NONEXISTENT; if (scores != null) { scores[i] = (pr == null) ? ProcessList.INVALID_ADJ : pr.curAdj; scores[i] = ProcessList.INVALID_ADJ; } } } } Loading Loading @@ -8839,15 +8811,7 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } } if (mInternal.isPendingTopUid(uid)) { Slog.wtf(TAG, "PendingStartActivityUids isUidActive false but" + " isPendingTopUid true, uid:" + uid + " callingPackage:" + callingPackage); return true; } else { return false; } return mInternal.isPendingTopUid(uid); } boolean isUidActiveLocked(int uid) { Loading Loading @@ -19731,22 +19695,18 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override public void updatePendingTopUid(int uid, boolean pending) { synchronized (mPendingStartActivityUidsLocked) { if (pending) { mPendingStartActivityUidsLocked.add(uid); } else { mPendingStartActivityUidsLocked.delete(uid); } public void addPendingTopUid(int uid, int pid) { mPendingStartActivityUids.add(uid, pid); } @Override public void deletePendingTopUid(int uid) { mPendingStartActivityUids.delete(uid); } @Override public boolean isPendingTopUid(int uid) { synchronized (mPendingStartActivityUidsLocked) { return mPendingStartActivityUidsLocked.isPendingTopUid(uid); } return mPendingStartActivityUids.isPendingTopUid(uid); } } services/core/java/com/android/server/am/OomAdjuster.java +1 −1 Original line number Diff line number Diff line Loading @@ -951,7 +951,7 @@ public final class OomAdjuster { mService.mServices.foregroundServiceProcStateChangedLocked(uidRec); } } mService.mInternal.updatePendingTopUid(uidRec.uid, false); mService.mInternal.deletePendingTopUid(uidRec.uid); } if (mLocalPowerManager != null) { mLocalPowerManager.finishUidChanges(); Loading services/core/java/com/android/server/am/PendingStartActivityUids.java 0 → 100644 +80 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.am; import android.content.Context; import android.os.SystemClock; import android.provider.Settings; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; /** * While starting activity, WindowManager posts a runnable to DisplayThread to updateOomAdj. * The latency of the thread switch could cause client app failure when the app is checking * {@link ActivityManagerService#isUidActive} before updateOomAdj is done. * * Use PendingStartActivityUids to save uid after WindowManager start activity and before * updateOomAdj is done. * * <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock! */ final class PendingStartActivityUids { static final String TAG = ActivityManagerService.TAG; // Key is uid, value is Pair of pid and SystemClock.elapsedRealtime() when the // uid is added. private final SparseArray<Pair<Integer, Long>> mPendingUids = new SparseArray(); private Context mContext; PendingStartActivityUids(Context context) { mContext = context; } synchronized void add(int uid, int pid) { if (mPendingUids.get(uid) == null) { mPendingUids.put(uid, new Pair<>(pid, SystemClock.elapsedRealtime())); } } synchronized void delete(int uid) { final Pair<Integer, Long> pendingPid = mPendingUids.get(uid); if (pendingPid != null) { final long delay = SystemClock.elapsedRealtime() - pendingPid.second; if (delay >= 1000 /*ms*/) { Slog.i(TAG, "PendingStartActivityUids startActivity to updateOomAdj delay:" + delay + "ms," + " uid:" + uid + " packageName:" + Settings.getPackageNameForUid(mContext, uid)); } mPendingUids.delete(uid); } } synchronized boolean isPendingTopPid(int uid, int pid) { final Pair<Integer, Long> pendingPid = mPendingUids.get(uid); if (pendingPid != null) { return pendingPid.first == pid; } else { return false; } } synchronized boolean isPendingTopUid(int uid) { return mPendingUids.get(uid) != null; } } No newline at end of file services/core/java/com/android/server/appop/AppOpsService.java +1 −18 Original line number Diff line number Diff line Loading @@ -521,7 +521,6 @@ public class AppOpsService extends IAppOpsService.Stub { public boolean hasForegroundWatchers; public long lastTimeShowDebugToast; public long lastTimePendingTopUid; public UidState(int uid) { this.uid = uid; Loading @@ -545,7 +544,6 @@ public class AppOpsService extends IAppOpsService.Stub { return MODE_ALLOWED; } else if (mActivityManagerInternal != null && mActivityManagerInternal.isPendingTopUid(uid)) { maybeLogPendingTopUid(op, mode); return MODE_ALLOWED; } else if (state <= UID_STATE_TOP) { // process is in TOP. Loading Loading @@ -611,7 +609,6 @@ public class AppOpsService extends IAppOpsService.Stub { case OP_CAMERA: if (mActivityManagerInternal != null && mActivityManagerInternal.isPendingTopUid(uid)) { maybeLogPendingTopUid(op, mode); return MODE_ALLOWED; } else if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) { return MODE_ALLOWED; Loading @@ -629,7 +626,6 @@ public class AppOpsService extends IAppOpsService.Stub { case OP_RECORD_AUDIO: if (mActivityManagerInternal != null && mActivityManagerInternal.isPendingTopUid(uid)) { maybeLogPendingTopUid(op, mode); return MODE_ALLOWED; } else if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) { return MODE_ALLOWED; Loading Loading @@ -709,7 +705,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == DEBUG_FGS_ALLOW_WHILE_IN_USE && state != UID_STATE_FOREGROUND_SERVICE) { return; } final long now = System.currentTimeMillis(); final long now = SystemClock.elapsedRealtime(); if (lastTimeShowDebugToast == 0 || now - lastTimeShowDebugToast > 600000) { lastTimeShowDebugToast = now; mHandler.sendMessage(PooledLambda.obtainMessage( Loading @@ -717,19 +713,6 @@ public class AppOpsService extends IAppOpsService.Stub { mActivityManagerInternal, uid, op, mode)); } } void maybeLogPendingTopUid(int op, int mode) { final long now = System.currentTimeMillis(); if (lastTimePendingTopUid == 0 || now - lastTimePendingTopUid > 300000) { lastTimePendingTopUid = now; Slog.wtf(TAG, "PendingStartActivityUids evalMode, isPendingTopUid true, uid:" + uid + " packageName:" + Settings.getPackageNameForUid(mContext, uid) + " op:" + op + " mode:" + mode); } } } final static class Ops extends SparseArray<Op> { Loading Loading
core/java/android/app/ActivityManagerInternal.java +9 −3 Original line number Diff line number Diff line Loading @@ -429,11 +429,17 @@ public abstract class ActivityManagerInternal { int userId, int[] appIdWhitelist); /** * Add or delete uid from the ActivityManagerService PendingStartActivityUids list. * Add uid to the ActivityManagerService PendingStartActivityUids list. * @param uid uid * @param pending add to the list if true, delete from list if false. * @param pid pid of the ProcessRecord that is pending top. */ public abstract void updatePendingTopUid(int uid, boolean pending); public abstract void addPendingTopUid(int uid, int pid); /** * Delete uid from the ActivityManagerService PendingStartActivityUids list. * @param uid uid */ public abstract void deletePendingTopUid(int uid); /** * Is the uid in ActivityManagerService PendingStartActivityUids list? Loading
services/core/java/com/android/server/am/ActivityManagerService.java +24 −64 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS; import static android.app.ActivityManager.INSTR_FLAG_MOUNT_EXTERNAL_STORAGE_FULL; import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AppOpsManager.OP_NONE; Loading Loading @@ -299,7 +300,6 @@ import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; Loading Loading @@ -421,7 +421,6 @@ public class ActivityManagerService extends IActivityManager.Stub * Priority we boost main thread and RT of top app to. */ public static final int TOP_APP_PRIORITY_BOOST = -10; private static final String SYSTEM_PROPERTY_DEVICE_PROVISIONED = "persist.sys.device_provisioned"; Loading Loading @@ -823,45 +822,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } /** * While starting activity, WindowManager posts a runnable to DisplayThread to updateOomAdj. * The latency of the thread switch could cause client app failure when the app is checking * {@link #isUidActive} before updateOomAdj is done. * * Use PendingStartActivityUids to save uid after WindowManager start activity and before * updateOomAdj is done. * * <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock! */ final PendingStartActivityUids mPendingStartActivityUidsLocked = new PendingStartActivityUids(); final class PendingStartActivityUids { // Key is uid, value is SystemClock.elapsedRealtime() when the key is added. private final SparseLongArray mPendingUids = new SparseLongArray(); void add(int uid) { if (mPendingUids.indexOfKey(uid) < 0) { mPendingUids.put(uid, SystemClock.elapsedRealtime()); } } void delete(int uid) { if (mPendingUids.indexOfKey(uid) >= 0) { long delay = SystemClock.elapsedRealtime() - mPendingUids.get(uid); if (delay >= 1000) { Slog.wtf(TAG, "PendingStartActivityUids startActivity to updateOomAdj delay:" + delay + "ms," + " uid:" + uid + " packageName:" + Settings.getPackageNameForUid(mContext, uid)); } mPendingUids.delete(uid); } } boolean isPendingTopUid(int uid) { return mPendingUids.indexOfKey(uid) >= 0; } } private final PendingStartActivityUids mPendingStartActivityUids; /** * Puts the process record in the map. Loading Loading @@ -2585,6 +2546,7 @@ public class ActivityManagerService extends IActivityManager.Stub mFactoryTest = FACTORY_TEST_OFF; mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mInternal = new LocalService(); mPendingStartActivityUids = new PendingStartActivityUids(mContext); } // Note: This method is invoked on the main thread but may need to attach various Loading Loading @@ -2742,6 +2704,7 @@ public class ActivityManagerService extends IActivityManager.Stub } mInternal = new LocalService(); mPendingStartActivityUids = new PendingStartActivityUids(mContext); } public void setSystemServiceManager(SystemServiceManager mgr) { Loading Loading @@ -6093,9 +6056,18 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (mPidsSelfLocked) { for (int i = 0; i < pids.length; i++) { ProcessRecord pr = mPidsSelfLocked.get(pids[i]); states[i] = (pr == null) ? PROCESS_STATE_NONEXISTENT : pr.getCurProcState(); if (pr != null) { final boolean isPendingTop = mPendingStartActivityUids.isPendingTopPid(pr.uid, pids[i]); states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.getCurProcState(); if (scores != null) { scores[i] = isPendingTop ? ProcessList.FOREGROUND_APP_ADJ : pr.curAdj; } } else { states[i] = PROCESS_STATE_NONEXISTENT; if (scores != null) { scores[i] = (pr == null) ? ProcessList.INVALID_ADJ : pr.curAdj; scores[i] = ProcessList.INVALID_ADJ; } } } } Loading Loading @@ -8839,15 +8811,7 @@ public class ActivityManagerService extends IActivityManager.Stub return true; } } if (mInternal.isPendingTopUid(uid)) { Slog.wtf(TAG, "PendingStartActivityUids isUidActive false but" + " isPendingTopUid true, uid:" + uid + " callingPackage:" + callingPackage); return true; } else { return false; } return mInternal.isPendingTopUid(uid); } boolean isUidActiveLocked(int uid) { Loading Loading @@ -19731,22 +19695,18 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override public void updatePendingTopUid(int uid, boolean pending) { synchronized (mPendingStartActivityUidsLocked) { if (pending) { mPendingStartActivityUidsLocked.add(uid); } else { mPendingStartActivityUidsLocked.delete(uid); } public void addPendingTopUid(int uid, int pid) { mPendingStartActivityUids.add(uid, pid); } @Override public void deletePendingTopUid(int uid) { mPendingStartActivityUids.delete(uid); } @Override public boolean isPendingTopUid(int uid) { synchronized (mPendingStartActivityUidsLocked) { return mPendingStartActivityUidsLocked.isPendingTopUid(uid); } return mPendingStartActivityUids.isPendingTopUid(uid); } }
services/core/java/com/android/server/am/OomAdjuster.java +1 −1 Original line number Diff line number Diff line Loading @@ -951,7 +951,7 @@ public final class OomAdjuster { mService.mServices.foregroundServiceProcStateChangedLocked(uidRec); } } mService.mInternal.updatePendingTopUid(uidRec.uid, false); mService.mInternal.deletePendingTopUid(uidRec.uid); } if (mLocalPowerManager != null) { mLocalPowerManager.finishUidChanges(); Loading
services/core/java/com/android/server/am/PendingStartActivityUids.java 0 → 100644 +80 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.am; import android.content.Context; import android.os.SystemClock; import android.provider.Settings; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; /** * While starting activity, WindowManager posts a runnable to DisplayThread to updateOomAdj. * The latency of the thread switch could cause client app failure when the app is checking * {@link ActivityManagerService#isUidActive} before updateOomAdj is done. * * Use PendingStartActivityUids to save uid after WindowManager start activity and before * updateOomAdj is done. * * <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock! */ final class PendingStartActivityUids { static final String TAG = ActivityManagerService.TAG; // Key is uid, value is Pair of pid and SystemClock.elapsedRealtime() when the // uid is added. private final SparseArray<Pair<Integer, Long>> mPendingUids = new SparseArray(); private Context mContext; PendingStartActivityUids(Context context) { mContext = context; } synchronized void add(int uid, int pid) { if (mPendingUids.get(uid) == null) { mPendingUids.put(uid, new Pair<>(pid, SystemClock.elapsedRealtime())); } } synchronized void delete(int uid) { final Pair<Integer, Long> pendingPid = mPendingUids.get(uid); if (pendingPid != null) { final long delay = SystemClock.elapsedRealtime() - pendingPid.second; if (delay >= 1000 /*ms*/) { Slog.i(TAG, "PendingStartActivityUids startActivity to updateOomAdj delay:" + delay + "ms," + " uid:" + uid + " packageName:" + Settings.getPackageNameForUid(mContext, uid)); } mPendingUids.delete(uid); } } synchronized boolean isPendingTopPid(int uid, int pid) { final Pair<Integer, Long> pendingPid = mPendingUids.get(uid); if (pendingPid != null) { return pendingPid.first == pid; } else { return false; } } synchronized boolean isPendingTopUid(int uid) { return mPendingUids.get(uid) != null; } } No newline at end of file
services/core/java/com/android/server/appop/AppOpsService.java +1 −18 Original line number Diff line number Diff line Loading @@ -521,7 +521,6 @@ public class AppOpsService extends IAppOpsService.Stub { public boolean hasForegroundWatchers; public long lastTimeShowDebugToast; public long lastTimePendingTopUid; public UidState(int uid) { this.uid = uid; Loading @@ -545,7 +544,6 @@ public class AppOpsService extends IAppOpsService.Stub { return MODE_ALLOWED; } else if (mActivityManagerInternal != null && mActivityManagerInternal.isPendingTopUid(uid)) { maybeLogPendingTopUid(op, mode); return MODE_ALLOWED; } else if (state <= UID_STATE_TOP) { // process is in TOP. Loading Loading @@ -611,7 +609,6 @@ public class AppOpsService extends IAppOpsService.Stub { case OP_CAMERA: if (mActivityManagerInternal != null && mActivityManagerInternal.isPendingTopUid(uid)) { maybeLogPendingTopUid(op, mode); return MODE_ALLOWED; } else if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) { return MODE_ALLOWED; Loading @@ -629,7 +626,6 @@ public class AppOpsService extends IAppOpsService.Stub { case OP_RECORD_AUDIO: if (mActivityManagerInternal != null && mActivityManagerInternal.isPendingTopUid(uid)) { maybeLogPendingTopUid(op, mode); return MODE_ALLOWED; } else if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) { return MODE_ALLOWED; Loading Loading @@ -709,7 +705,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == DEBUG_FGS_ALLOW_WHILE_IN_USE && state != UID_STATE_FOREGROUND_SERVICE) { return; } final long now = System.currentTimeMillis(); final long now = SystemClock.elapsedRealtime(); if (lastTimeShowDebugToast == 0 || now - lastTimeShowDebugToast > 600000) { lastTimeShowDebugToast = now; mHandler.sendMessage(PooledLambda.obtainMessage( Loading @@ -717,19 +713,6 @@ public class AppOpsService extends IAppOpsService.Stub { mActivityManagerInternal, uid, op, mode)); } } void maybeLogPendingTopUid(int op, int mode) { final long now = System.currentTimeMillis(); if (lastTimePendingTopUid == 0 || now - lastTimePendingTopUid > 300000) { lastTimePendingTopUid = now; Slog.wtf(TAG, "PendingStartActivityUids evalMode, isPendingTopUid true, uid:" + uid + " packageName:" + Settings.getPackageNameForUid(mContext, uid) + " op:" + op + " mode:" + mode); } } } final static class Ops extends SparseArray<Op> { Loading