diff options
Diffstat (limited to 'src/com/android/settings/applications/RunningState.java')
-rw-r--r-- | src/com/android/settings/applications/RunningState.java | 365 |
1 files changed, 308 insertions, 57 deletions
diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java index 1b5310d..838e4fe 100644 --- a/src/com/android/settings/applications/RunningState.java +++ b/src/com/android/settings/applications/RunningState.java @@ -27,12 +27,16 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; import android.text.format.Formatter; import android.util.Log; import android.util.SparseArray; @@ -49,6 +53,9 @@ import java.util.List; * applications/processes/services. */ public class RunningState { + static final String TAG = "RunningState"; + static final boolean DEBUG_COMPARE = false; + static Object sGlobalLock = new Object(); static RunningState sInstance; @@ -65,6 +72,8 @@ public class RunningState { final Context mApplicationContext; final ActivityManager mAm; final PackageManager mPm; + final UserManager mUm; + final int mMyUserId; OnRefreshUiListener mRefreshUiListener; @@ -100,6 +109,17 @@ public class RunningState { // All processes, used for retrieving memory information. final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>(); + // If there are other users on the device, these are the merged items + // representing all items that would be put in mMergedItems for that user. + final SparseArray<MergedItem> mOtherUserMergedItems = new SparseArray<MergedItem>(); + + // If there are other users on the device, these are the merged items + // representing all items that would be put in mUserBackgroundItems for that user. + final SparseArray<MergedItem> mOtherUserBackgroundItems = new SparseArray<MergedItem>(); + + // Tracking of information about users. + final SparseArray<UserState> mUsers = new SparseArray<UserState>(); + static class AppProcessInfo { final ActivityManager.RunningAppProcessInfo info; boolean hasServices; @@ -114,7 +134,64 @@ public class RunningState { final SparseArray<AppProcessInfo> mTmpAppProcesses = new SparseArray<AppProcessInfo>(); int mSequence = 0; - + + final Comparator<RunningState.MergedItem> mBackgroundComparator + = new Comparator<RunningState.MergedItem>() { + @Override + public int compare(MergedItem lhs, MergedItem rhs) { + if (DEBUG_COMPARE) { + Log.i(TAG, "Comparing " + lhs + " with " + rhs); + Log.i(TAG, " Proc " + lhs.mProcess + " with " + rhs.mProcess); + Log.i(TAG, " UserId " + lhs.mUserId + " with " + rhs.mUserId); + } + if (lhs.mUserId != rhs.mUserId) { + if (lhs.mUserId == mMyUserId) return -1; + if (rhs.mUserId == mMyUserId) return 1; + return lhs.mUserId < rhs.mUserId ? -1 : 1; + } + if (lhs.mProcess == rhs.mProcess) { + if (lhs.mLabel == rhs.mLabel) { + return 0; + } + return lhs.mLabel != null ? lhs.mLabel.compareTo(rhs.mLabel) : -1; + } + if (lhs.mProcess == null) return -1; + if (rhs.mProcess == null) return 1; + if (DEBUG_COMPARE) Log.i(TAG, " Label " + lhs.mProcess.mLabel + + " with " + rhs.mProcess.mLabel); + final ActivityManager.RunningAppProcessInfo lhsInfo + = lhs.mProcess.mRunningProcessInfo; + final ActivityManager.RunningAppProcessInfo rhsInfo + = rhs.mProcess.mRunningProcessInfo; + final boolean lhsBg = lhsInfo.importance + >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; + final boolean rhsBg = rhsInfo.importance + >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND; + if (DEBUG_COMPARE) Log.i(TAG, " Bg " + lhsBg + " with " + rhsBg); + if (lhsBg != rhsBg) { + return lhsBg ? 1 : -1; + } + final boolean lhsA = (lhsInfo.flags + & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0; + final boolean rhsA = (rhsInfo.flags + & ActivityManager.RunningAppProcessInfo.FLAG_HAS_ACTIVITIES) != 0; + if (DEBUG_COMPARE) Log.i(TAG, " Act " + lhsA + " with " + rhsA); + if (lhsA != rhsA) { + return lhsA ? -1 : 1; + } + if (DEBUG_COMPARE) Log.i(TAG, " Lru " + lhsInfo.lru + " with " + rhsInfo.lru); + if (lhsInfo.lru != rhsInfo.lru) { + return lhsInfo.lru < rhsInfo.lru ? -1 : 1; + } + if (lhs.mProcess.mLabel == rhs.mProcess.mLabel) { + return 0; + } + if (lhs.mProcess.mLabel == null) return 1; + if (rhs.mProcess.mLabel == null) return -1; + return lhs.mProcess.mLabel.compareTo(rhs.mProcess.mLabel); + } + }; + // ----- following protected by mLock ----- // Lock for protecting the state that will be shared between the @@ -128,6 +205,7 @@ public class RunningState { ArrayList<BaseItem> mItems = new ArrayList<BaseItem>(); ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>(); ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>(); + ArrayList<MergedItem> mUserBackgroundItems = new ArrayList<MergedItem>(); int mNumBackgroundProcesses; long mBackgroundProcessMemory; @@ -211,25 +289,40 @@ public class RunningState { public void onRefreshUi(int what); } + static class UserState { + UserInfo mInfo; + String mLabel; + Drawable mIcon; + } + static class BaseItem { final boolean mIsProcess; - + final int mUserId; + PackageItemInfo mPackageInfo; CharSequence mDisplayLabel; String mLabel; String mDescription; - + int mCurSeq; - + long mActiveSince; long mSize; String mSizeStr; String mCurSizeStr; boolean mNeedDivider; boolean mBackground; - - public BaseItem(boolean isProcess) { + + public BaseItem(boolean isProcess, int userId) { mIsProcess = isProcess; + mUserId = userId; + } + + public Drawable loadIcon(Context context, RunningState state) { + if (mPackageInfo != null) { + return mPackageInfo.loadIcon(state.mPm); + } + return null; } } @@ -240,8 +333,8 @@ public class RunningState { MergedItem mMergedItem; - public ServiceItem() { - super(false); + public ServiceItem(int userId) { + super(false, userId); } } @@ -271,7 +364,7 @@ public class RunningState { long mActiveSince; public ProcessItem(Context context, int uid, String processName) { - super(true); + super(true, UserHandle.getUserId(uid)); mDescription = context.getResources().getString( R.string.service_process_name, processName); mUid = uid; @@ -332,8 +425,9 @@ public class RunningState { // If still don't have anything to display, just use the // service info. if (mServices.size() > 0) { - mPackageInfo = mServices.values().iterator().next() + ApplicationInfo ai = mServices.values().iterator().next() .mServiceInfo.applicationInfo; + mPackageInfo = ai; mDisplayLabel = mPackageInfo.loadLabel(pm); mLabel = mDisplayLabel.toString(); return; @@ -358,7 +452,7 @@ public class RunningState { ServiceItem si = mServices.get(service.service); if (si == null) { changed = true; - si = new ServiceItem(); + si = new ServiceItem(mUserId); si.mRunningService = service; try { si.mServiceInfo = pm.getServiceInfo(service.service, 0); @@ -456,55 +550,96 @@ public class RunningState { static class MergedItem extends BaseItem { ProcessItem mProcess; + UserState mUser; final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>(); final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>(); + final ArrayList<MergedItem> mChildren = new ArrayList<MergedItem>(); private int mLastNumProcesses = -1, mLastNumServices = -1; - MergedItem() { - super(false); + MergedItem(int userId) { + super(false, userId); } - + + private void setDescription(Context context, int numProcesses, int numServices) { + if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) { + mLastNumProcesses = numProcesses; + mLastNumServices = numServices; + int resid = R.string.running_processes_item_description_s_s; + if (numProcesses != 1) { + resid = numServices != 1 + ? R.string.running_processes_item_description_p_p + : R.string.running_processes_item_description_p_s; + } else if (numServices != 1) { + resid = R.string.running_processes_item_description_s_p; + } + mDescription = context.getResources().getString(resid, numProcesses, + numServices); + } + } + boolean update(Context context, boolean background) { - mPackageInfo = mProcess.mPackageInfo; - mDisplayLabel = mProcess.mDisplayLabel; - mLabel = mProcess.mLabel; mBackground = background; - - if (!mBackground) { - int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(); - int numServices = mServices.size(); - if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) { - mLastNumProcesses = numProcesses; - mLastNumServices = numServices; - int resid = R.string.running_processes_item_description_s_s; - if (numProcesses != 1) { - resid = numServices != 1 - ? R.string.running_processes_item_description_p_p - : R.string.running_processes_item_description_p_s; - } else if (numServices != 1) { - resid = R.string.running_processes_item_description_s_p; + + if (mUser != null) { + // This is a merged item that contains a child collection + // of items... that is, it is an entire user, containing + // everything associated with that user. So set it up as such. + // For concrete stuff we need about the process of this item, + // we will just use the info from the first child. + MergedItem child0 = mChildren.get(0); + mPackageInfo = child0.mProcess.mPackageInfo; + mLabel = mUser != null ? mUser.mLabel : null; + mDisplayLabel = mLabel; + int numProcesses = 0; + int numServices = 0; + mActiveSince = -1; + for (int i=0; i<mChildren.size(); i++) { + MergedItem child = mChildren.get(i); + numProcesses += child.mLastNumProcesses; + numServices += child.mLastNumServices; + if (child.mActiveSince >= 0 && mActiveSince < child.mActiveSince) { + mActiveSince = child.mActiveSince; } - mDescription = context.getResources().getString(resid, numProcesses, - numServices); } - } - - mActiveSince = -1; - for (int i=0; i<mServices.size(); i++) { - ServiceItem si = mServices.get(i); - if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) { - mActiveSince = si.mActiveSince; + if (!mBackground) { + setDescription(context, numProcesses, numServices); + } + } else { + mPackageInfo = mProcess.mPackageInfo; + mDisplayLabel = mProcess.mDisplayLabel; + mLabel = mProcess.mLabel; + + if (!mBackground) { + setDescription(context, (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(), + mServices.size()); + } + + mActiveSince = -1; + for (int i=0; i<mServices.size(); i++) { + ServiceItem si = mServices.get(i); + if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) { + mActiveSince = si.mActiveSince; + } } } - + return false; } boolean updateSize(Context context) { - mSize = mProcess.mSize; - for (int i=0; i<mOtherProcesses.size(); i++) { - mSize += mOtherProcesses.get(i).mSize; + if (mUser != null) { + mSize = 0; + for (int i=0; i<mChildren.size(); i++) { + MergedItem child = mChildren.get(i); + child.updateSize(context); + mSize += child.mSize; + } + } else { + mSize = mProcess.mSize; + for (int i=0; i<mOtherProcesses.size(); i++) { + mSize += mOtherProcesses.get(i).mSize; + } } String sizeStr = Formatter.formatShortFileSize( @@ -518,10 +653,26 @@ public class RunningState { } return false; } + + public Drawable loadIcon(Context context, RunningState state) { + if (mUser == null) { + return super.loadIcon(context, state); + } + if (mUser.mIcon != null) { + return mUser.mIcon.getConstantState().newDrawable(); + } + return context.getResources().getDrawable( + com.android.internal.R.drawable.ic_menu_cc); + } } - - static class ServiceProcessComparator implements Comparator<ProcessItem> { + + class ServiceProcessComparator implements Comparator<ProcessItem> { public int compare(ProcessItem object1, ProcessItem object2) { + if (object1.mUserId != object2.mUserId) { + if (object1.mUserId == mMyUserId) return -1; + if (object2.mUserId == mMyUserId) return 1; + return object1.mUserId < object2.mUserId ? -1 : 1; + } if (object1.mIsStarted != object2.mIsStarted) { // Non-started processes go last. return object1.mIsStarted ? -1 : 1; @@ -570,6 +721,8 @@ public class RunningState { mApplicationContext = context.getApplicationContext(); mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE); mPm = mApplicationContext.getPackageManager(); + mUm = (UserManager)mApplicationContext.getSystemService(Context.USER_SERVICE); + mMyUserId = UserHandle.myUserId(); mResumed = false; mBackgroundThread = new HandlerThread("RunningState:Background"); mBackgroundThread.start(); @@ -646,6 +799,42 @@ public class RunningState { mRunningProcesses.clear(); mProcessItems.clear(); mAllProcessItems.clear(); + mUsers.clear(); + } + + private void addOtherUserItem(Context context, ArrayList<MergedItem> newMergedItems, + SparseArray<MergedItem> userItems, MergedItem newItem) { + MergedItem userItem = userItems.get(newItem.mUserId); + boolean first = userItem == null || userItem.mCurSeq != mSequence; + if (first) { + if (userItem == null) { + userItem = new MergedItem(newItem.mUserId); + userItems.put(newItem.mUserId, userItem); + } else { + userItem.mChildren.clear(); + } + userItem.mCurSeq = mSequence; + if ((userItem.mUser=mUsers.get(newItem.mUserId)) == null) { + userItem.mUser = new UserState(); + UserInfo info = mUm.getUserInfo(newItem.mUserId); + userItem.mUser.mInfo = info; + if (info != null && info.iconPath != null) { + try { + userItem.mUser.mIcon = Drawable.createFromPath(info.iconPath); + } catch (Exception e) { + Log.w(TAG, "Failure loading user picture " + info.iconPath, e); + } + } + String name = info != null ? info.name : null; + if (name == null) { + name = Integer.toString(info.id); + } + userItem.mUser.mLabel = context.getResources().getString( + R.string.running_process_item_user_label, name); + } + newMergedItems.add(userItem); + } + userItem.mChildren.add(newItem); } private boolean update(Context context, ActivityManager am) { @@ -940,6 +1129,7 @@ public class RunningState { ArrayList<BaseItem> newItems = new ArrayList<BaseItem>(); ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>(); + SparseArray<MergedItem> otherUsers = null; mProcessItems.clear(); for (int i=0; i<sortedProcesses.size(); i++) { ProcessItem pi = sortedProcesses.get(i); @@ -975,7 +1165,7 @@ public class RunningState { if (!haveAllMerged || mergedItem == null || mergedItem.mServices.size() != pi.mServices.size()) { // Whoops, we need to build a new MergedItem! - mergedItem = new MergedItem(); + mergedItem = new MergedItem(pi.mUserId); for (ServiceItem si : pi.mServices.values()) { mergedItem.mServices.add(si); si.mMergedItem = mergedItem; @@ -988,9 +1178,13 @@ public class RunningState { } mergedItem.update(context, false); - newMergedItems.add(mergedItem); + if (mergedItem.mUserId != mMyUserId) { + addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, mergedItem); + } else { + newMergedItems.add(mergedItem); + } } - + // Finally, interesting processes need to be shown and will // go at the top. NHP = mInterestingProcesses.size(); @@ -998,15 +1192,30 @@ public class RunningState { ProcessItem proc = mInterestingProcesses.get(i); if (proc.mClient == null && proc.mServices.size() <= 0) { if (proc.mMergedItem == null) { - proc.mMergedItem = new MergedItem(); + proc.mMergedItem = new MergedItem(proc.mUserId); proc.mMergedItem.mProcess = proc; } proc.mMergedItem.update(context, false); - newMergedItems.add(0, proc.mMergedItem); + if (proc.mMergedItem.mUserId != mMyUserId) { + addOtherUserItem(context, newMergedItems, mOtherUserMergedItems, + proc.mMergedItem); + } else { + newMergedItems.add(0, proc.mMergedItem); + } mProcessItems.add(proc); } } - + + // Finally finally, user aggregated merged items need to be + // updated now that they have all of their children. + final int NU = mOtherUserMergedItems.size(); + for (int i=0; i<NU; i++) { + MergedItem user = mOtherUserMergedItems.valueAt(i); + if (user.mCurSeq == mSequence) { + user.update(context, false); + } + } + synchronized (mLock) { mItems = newItems; mMergedItems = newMergedItems; @@ -1047,6 +1256,8 @@ public class RunningState { long foregroundProcessMemory = 0; long serviceProcessMemory = 0; ArrayList<MergedItem> newBackgroundItems = null; + ArrayList<MergedItem> newUserBackgroundItems = null; + boolean diffUsers = false; try { final int numProc = mAllProcessItems.size(); int[] pids = new int[numProc]; @@ -1066,18 +1277,22 @@ public class RunningState { backgroundProcessMemory += proc.mSize; MergedItem mergedItem; if (newBackgroundItems != null) { - mergedItem = proc.mMergedItem = new MergedItem(); + mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId); proc.mMergedItem.mProcess = proc; + diffUsers |= mergedItem.mUserId != mMyUserId; newBackgroundItems.add(mergedItem); } else { if (bgIndex >= mBackgroundItems.size() || mBackgroundItems.get(bgIndex).mProcess != proc) { newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); for (int bgi=0; bgi<bgIndex; bgi++) { - newBackgroundItems.add(mBackgroundItems.get(bgi)); + mergedItem = mBackgroundItems.get(bgi); + diffUsers |= mergedItem.mUserId != mMyUserId; + newBackgroundItems.add(mergedItem); } - mergedItem = proc.mMergedItem = new MergedItem(); + mergedItem = proc.mMergedItem = new MergedItem(proc.mUserId); proc.mMergedItem.mProcess = proc; + diffUsers |= mergedItem.mUserId != mMyUserId; newBackgroundItems.add(mergedItem); } else { mergedItem = mBackgroundItems.get(bgIndex); @@ -1099,7 +1314,42 @@ public class RunningState { if (mBackgroundItems.size() > numBackgroundProcesses) { newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses); for (int bgi=0; bgi<numBackgroundProcesses; bgi++) { - newBackgroundItems.add(mBackgroundItems.get(bgi)); + MergedItem mergedItem = mBackgroundItems.get(bgi); + diffUsers |= mergedItem.mUserId != mMyUserId; + newBackgroundItems.add(mergedItem); + } + } + } + + if (newBackgroundItems != null) { + // The background items have changed; we need to re-build the + // per-user items. + if (!diffUsers) { + // Easy: there are no other users, we can just use the same array. + newUserBackgroundItems = newBackgroundItems; + } else { + // We now need to re-build the per-user list so that background + // items for users are collapsed together. + newUserBackgroundItems = new ArrayList<MergedItem>(); + final int NB = newBackgroundItems.size(); + for (int i=0; i<NB; i++) { + MergedItem mergedItem = newBackgroundItems.get(i); + if (mergedItem.mUserId != mMyUserId) { + addOtherUserItem(context, newUserBackgroundItems, + mOtherUserBackgroundItems, mergedItem); + } else { + newUserBackgroundItems.add(mergedItem); + } + } + // And user aggregated merged items need to be + // updated now that they have all of their children. + final int NU = mOtherUserBackgroundItems.size(); + for (int i=0; i<NU; i++) { + MergedItem user = mOtherUserBackgroundItems.valueAt(i); + if (user.mCurSeq == mSequence) { + user.update(context, true); + user.updateSize(context); + } } } } @@ -1117,6 +1367,7 @@ public class RunningState { mServiceProcessMemory = serviceProcessMemory; if (newBackgroundItems != null) { mBackgroundItems = newBackgroundItems; + mUserBackgroundItems = newUserBackgroundItems; if (mWatchingBackgroundItems) { changed = true; } @@ -1150,7 +1401,7 @@ public class RunningState { ArrayList<MergedItem> getCurrentBackgroundItems() { synchronized (mLock) { - return mBackgroundItems; + return mUserBackgroundItems; } } } |