diff options
Diffstat (limited to 'src')
4 files changed, 495 insertions, 155 deletions
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 364f96a..f8c23d7 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -102,6 +102,7 @@ public class Settings extends PreferenceActivity R.id.device_section, R.id.sound_settings, R.id.display_settings, + R.id.application_settings, R.id.personal_section, R.id.security_settings, R.id.account_settings, diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java index 7c3ebb0..67221ae 100644 --- a/src/com/android/settings/applications/RunningProcessesView.java +++ b/src/com/android/settings/applications/RunningProcessesView.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.SystemClock; +import android.os.UserHandle; import android.preference.PreferenceActivity; import android.text.format.DateUtils; import android.text.format.Formatter; @@ -42,6 +43,7 @@ import android.widget.TextView; import android.widget.AbsListView.RecyclerListener; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -49,10 +51,12 @@ public class RunningProcessesView extends FrameLayout implements AdapterView.OnItemClickListener, RecyclerListener, RunningState.OnRefreshUiListener { + final int mMyUserId; + long SECONDARY_SERVER_MEM; final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>(); - + ActivityManager mAm; RunningState mState; @@ -167,9 +171,12 @@ public class RunningProcessesView extends FrameLayout if (item.mPackageInfo == null && item instanceof RunningState.MergedItem) { // Items for background processes don't normally load // their labels for performance reasons. Do it now. - ((RunningState.MergedItem)item).mProcess.ensureLabel(pm); - item.mPackageInfo = ((RunningState.MergedItem)item).mProcess.mPackageInfo; - item.mDisplayLabel = ((RunningState.MergedItem)item).mProcess.mDisplayLabel; + RunningState.MergedItem mergedItem = (RunningState.MergedItem)item; + if (mergedItem.mProcess != null) { + ((RunningState.MergedItem)item).mProcess.ensureLabel(pm); + item.mPackageInfo = ((RunningState.MergedItem)item).mProcess.mPackageInfo; + item.mDisplayLabel = ((RunningState.MergedItem)item).mProcess.mDisplayLabel; + } } name.setText(item.mDisplayLabel); ActiveItem ai = new ActiveItem(); @@ -183,9 +190,7 @@ public class RunningProcessesView extends FrameLayout description.setText(item.mDescription); } item.mCurSizeStr = null; - if (item.mPackageInfo != null) { - icon.setImageDrawable(item.mPackageInfo.loadIcon(pm)); - } + icon.setImageDrawable(item.loadIcon(rootView.getContext(), state)); icon.setVisibility(View.VISIBLE); ai.updateTime(rootView.getContext(), builder); return ai; @@ -203,7 +208,9 @@ public class RunningProcessesView extends FrameLayout final RunningState mState; final LayoutInflater mInflater; boolean mShowBackground; - ArrayList<RunningState.MergedItem> mItems; + ArrayList<RunningState.MergedItem> mOrigItems; + final ArrayList<RunningState.MergedItem> mItems + = new ArrayList<RunningState.MergedItem>(); ServiceListAdapter(RunningState state) { mState = state; @@ -230,11 +237,17 @@ public class RunningProcessesView extends FrameLayout ArrayList<RunningState.MergedItem> newItems = mShowBackground ? mState.getCurrentBackgroundItems() : mState.getCurrentMergedItems(); - if (mItems != newItems) { - mItems = newItems; - } - if (mItems == null) { - mItems = new ArrayList<RunningState.MergedItem>(); + if (mOrigItems != newItems) { + mOrigItems = newItems; + if (newItems == null) { + mItems.clear(); + } else { + mItems.clear(); + mItems.addAll(newItems); + if (mShowBackground) { + Collections.sort(mItems, mState.mBackgroundComparator); + } + } } } @@ -374,8 +387,11 @@ public class RunningProcessesView extends FrameLayout if (mOwner != null) { // start new fragment to display extended information Bundle args = new Bundle(); - args.putInt(RunningServiceDetails.KEY_UID, mi.mProcess.mUid); - args.putString(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName); + if (mi.mProcess != null) { + args.putInt(RunningServiceDetails.KEY_UID, mi.mProcess.mUid); + args.putString(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName); + } + args.putInt(RunningServiceDetails.KEY_USER_ID, mi.mUserId); args.putBoolean(RunningServiceDetails.KEY_BACKGROUND, mAdapter.mShowBackground); PreferenceActivity pa = (PreferenceActivity)mOwner.getActivity(); @@ -390,6 +406,7 @@ public class RunningProcessesView extends FrameLayout public RunningProcessesView(Context context, AttributeSet attrs) { super(context, attrs); + mMyUserId = UserHandle.myUserId(); } public void doCreate(Bundle savedInstanceState) { diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java index 2087f21..52ed458 100644 --- a/src/com/android/settings/applications/RunningServiceDetails.java +++ b/src/com/android/settings/applications/RunningServiceDetails.java @@ -26,6 +26,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.SystemClock; +import android.os.UserHandle; import android.provider.Settings; import android.util.Log; import android.view.LayoutInflater; @@ -39,35 +40,38 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; public class RunningServiceDetails extends Fragment implements RunningState.OnRefreshUiListener { static final String TAG = "RunningServicesDetails"; - + static final String KEY_UID = "uid"; + static final String KEY_USER_ID = "user_id"; static final String KEY_PROCESS = "process"; static final String KEY_BACKGROUND = "background"; - + static final int DIALOG_CONFIRM_STOP = 1; - + ActivityManager mAm; LayoutInflater mInflater; - + RunningState mState; boolean mHaveData; - + int mUid; + int mUserId; String mProcessName; boolean mShowBackground; - + RunningState.MergedItem mMergedItem; - + View mRootView; ViewGroup mAllDetails; ViewGroup mSnippet; RunningProcessesView.ActiveItem mSnippetActiveItem; RunningProcessesView.ViewHolder mSnippetViewHolder; - + int mNumServices, mNumProcesses; TextView mServicesHeader; @@ -195,8 +199,14 @@ public class RunningServiceDetails extends Fragment if (newItems != null) { for (int i=0; i<newItems.size(); i++) { RunningState.MergedItem mi = newItems.get(i); - if (mi.mProcess.mUid == mUid - && mi.mProcess.mProcessName.equals(mProcessName)) { + if (mi.mUserId != mUserId) { + continue; + } + if (mUid >= 0 && mi.mProcess != null && mi.mProcess.mUid != mUid) { + continue; + } + if (mProcessName == null || (mi.mProcess != null + && mProcessName.equals(mi.mProcess.mProcessName))) { item = mi; break; } @@ -209,8 +219,8 @@ public class RunningServiceDetails extends Fragment } return false; } - - void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi) { + + void addServicesHeader() { if (mNumServices == 0) { mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label, mAllDetails, false); @@ -218,7 +228,30 @@ public class RunningServiceDetails extends Fragment mAllDetails.addView(mServicesHeader); } mNumServices++; - + } + + void addProcessesHeader() { + if (mNumProcesses == 0) { + mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label, + mAllDetails, false); + mProcessesHeader.setText(R.string.runningservicedetails_processes_title); + mAllDetails.addView(mProcessesHeader); + } + mNumProcesses++; + } + + void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi, + boolean isService, boolean inclDetails) { + if (isService) { + addServicesHeader(); + } else if (mi.mUserId != UserHandle.myUserId()) { + // This is being called for another user, and is not a service... + // That is, it is a background processes, being added for the + // details of a user. In this case we want a header for processes, + // since the top subject line is for the user. + addProcessesHeader(); + } + RunningState.BaseItem bi = si != null ? si : mi; ActiveDetail detail = new ActiveDetail(); @@ -229,68 +262,74 @@ public class RunningServiceDetails extends Fragment detail.mServiceItem = si; detail.mViewHolder = new RunningProcessesView.ViewHolder(root); detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder); - + + if (!inclDetails) { + root.findViewById(R.id.service).setVisibility(View.GONE); + } + if (si != null && si.mRunningService.clientLabel != 0) { detail.mManageIntent = mAm.getRunningServiceControlPanel( si.mRunningService.service); } TextView description = (TextView)root.findViewById(R.id.comp_description); - if (si != null && si.mServiceInfo.descriptionRes != 0) { - description.setText(getActivity().getPackageManager().getText( - si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes, - si.mServiceInfo.applicationInfo)); + detail.mStopButton = (Button)root.findViewById(R.id.left_button); + detail.mReportButton = (Button)root.findViewById(R.id.right_button); + + if (isService && mi.mUserId != UserHandle.myUserId()) { + // For services from other users, we don't show any description or + // controls, because the current user can not perform + // actions on them. + description.setVisibility(View.GONE); + root.findViewById(R.id.control_buttons_panel).setVisibility(View.GONE); } else { - if (mi.mBackground) { - description.setText(R.string.background_process_stop_description); - } else if (detail.mManageIntent != null) { - try { - Resources clientr = getActivity().getPackageManager().getResourcesForApplication( - si.mRunningService.clientPackage); - String label = clientr.getString(si.mRunningService.clientLabel); - description.setText(getActivity().getString(R.string.service_manage_description, - label)); - } catch (PackageManager.NameNotFoundException e) { + if (si != null && si.mServiceInfo.descriptionRes != 0) { + description.setText(getActivity().getPackageManager().getText( + si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes, + si.mServiceInfo.applicationInfo)); + } else { + if (mi.mBackground) { + description.setText(R.string.background_process_stop_description); + } else if (detail.mManageIntent != null) { + try { + Resources clientr = getActivity().getPackageManager().getResourcesForApplication( + si.mRunningService.clientPackage); + String label = clientr.getString(si.mRunningService.clientLabel); + description.setText(getActivity().getString(R.string.service_manage_description, + label)); + } catch (PackageManager.NameNotFoundException e) { + } + } else { + description.setText(getActivity().getText(si != null + ? R.string.service_stop_description + : R.string.heavy_weight_stop_description)); } + } + + detail.mStopButton.setOnClickListener(detail); + detail.mStopButton.setText(getActivity().getText(detail.mManageIntent != null + ? R.string.service_manage : R.string.service_stop)); + detail.mReportButton.setOnClickListener(detail); + detail.mReportButton.setText(com.android.internal.R.string.report); + // check if error reporting is enabled in secure settings + int enabled = Settings.Secure.getInt(getActivity().getContentResolver(), + Settings.Secure.SEND_ACTION_APP_ERROR, 0); + if (enabled != 0 && si != null) { + detail.mInstaller = ApplicationErrorReport.getErrorReportReceiver( + getActivity(), si.mServiceInfo.packageName, + si.mServiceInfo.applicationInfo.flags); + detail.mReportButton.setEnabled(detail.mInstaller != null); } else { - description.setText(getActivity().getText(si != null - ? R.string.service_stop_description - : R.string.heavy_weight_stop_description)); + detail.mReportButton.setEnabled(false); } } - - detail.mStopButton = (Button)root.findViewById(R.id.left_button); - detail.mStopButton.setOnClickListener(detail); - detail.mStopButton.setText(getActivity().getText(detail.mManageIntent != null - ? R.string.service_manage : R.string.service_stop)); - detail.mReportButton = (Button)root.findViewById(R.id.right_button); - detail.mReportButton.setOnClickListener(detail); - detail.mReportButton.setText(com.android.internal.R.string.report); - // check if error reporting is enabled in secure settings - int enabled = Settings.Secure.getInt(getActivity().getContentResolver(), - Settings.Secure.SEND_ACTION_APP_ERROR, 0); - if (enabled != 0 && si != null) { - detail.mInstaller = ApplicationErrorReport.getErrorReportReceiver( - getActivity(), si.mServiceInfo.packageName, - si.mServiceInfo.applicationInfo.flags); - detail.mReportButton.setEnabled(detail.mInstaller != null); - } else { - detail.mReportButton.setEnabled(false); - } - mActiveDetails.add(detail); } - + void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) { - if (mNumProcesses == 0) { - mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label, - mAllDetails, false); - mProcessesHeader.setText(R.string.runningservicedetails_processes_title); - mAllDetails.addView(mProcessesHeader); - } - mNumProcesses++; - + addProcessesHeader(); + ActiveDetail detail = new ActiveDetail(); View root = mInflater.inflate(R.layout.running_service_details_process, mAllDetails, false); @@ -300,7 +339,11 @@ public class RunningServiceDetails extends Fragment detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder); TextView description = (TextView)root.findViewById(R.id.comp_description); - if (isMain) { + if (pi.mUserId != UserHandle.myUserId()) { + // Processes for another user are all shown batched together; there is + // no reason to have a description. + description.setVisibility(View.GONE); + } else if (isMain) { description.setText(R.string.main_running_process_description); } else { int textid = 0; @@ -342,7 +385,39 @@ public class RunningServiceDetails extends Fragment mActiveDetails.add(detail); } + + void addDetailsViews(RunningState.MergedItem item, boolean inclServices, + boolean inclProcesses) { + if (item != null) { + if (inclServices) { + for (int i=0; i<item.mServices.size(); i++) { + addServiceDetailsView(item.mServices.get(i), item, true, true); + } + } + + if (inclProcesses) { + if (item.mServices.size() <= 0) { + // This item does not have any services, so it must be + // another interesting process... we will put a fake service + // entry for it, to allow the user to "stop" it. + addServiceDetailsView(null, item, false, item.mUserId != UserHandle.myUserId()); + } else { + // This screen is actually showing services, so also show + // the process details. + for (int i=-1; i<item.mOtherProcesses.size(); i++) { + RunningState.ProcessItem pi = i < 0 ? item.mProcess + : item.mOtherProcesses.get(i); + if (pi != null && pi.mPid <= 0) { + continue; + } + addProcessDetailsView(pi, i < 0); + } + } + } + } + } + void addDetailViews() { for (int i=mActiveDetails.size()-1; i>=0; i--) { mAllDetails.removeView(mActiveDetails.get(i).mRootView); @@ -358,30 +433,25 @@ public class RunningServiceDetails extends Fragment mAllDetails.removeView(mProcessesHeader); mProcessesHeader = null; } - + mNumServices = mNumProcesses = 0; - - if (mMergedItem != null) { - for (int i=0; i<mMergedItem.mServices.size(); i++) { - addServiceDetailsView(mMergedItem.mServices.get(i), mMergedItem); + + if (mMergedItem.mUser != null) { + ArrayList<RunningState.MergedItem> items; + if (mShowBackground) { + items = new ArrayList<RunningState.MergedItem>(mMergedItem.mChildren); + Collections.sort(items, mState.mBackgroundComparator); + } else { + items = mMergedItem.mChildren; } - - if (mMergedItem.mServices.size() <= 0) { - // This item does not have any services, so it must be - // another interesting process... we will put a fake service - // entry for it, to allow the user to "stop" it. - addServiceDetailsView(null, mMergedItem); + for (int i=0; i<items.size(); i++) { + addDetailsViews(items.get(i), true, false); } - - for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) { - RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess - : mMergedItem.mOtherProcesses.get(i); - if (pi.mPid <= 0) { - continue; - } - - addProcessDetailsView(pi, i < 0); + for (int i=0; i<items.size(); i++) { + addDetailsViews(items.get(i), false, true); } + } else { + addDetailsViews(mMergedItem, true, true); } } @@ -423,8 +493,9 @@ public class RunningServiceDetails extends Fragment public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mUid = getArguments().getInt(KEY_UID, 0); - mProcessName = getArguments().getString(KEY_PROCESS); + mUid = getArguments().getInt(KEY_UID, -1); + mUserId = getArguments().getInt(KEY_USER_ID, 0); + mProcessName = getArguments().getString(KEY_PROCESS, null); mShowBackground = getArguments().getBoolean(KEY_BACKGROUND, false); mAm = (ActivityManager)getActivity().getSystemService(Context.ACTIVITY_SERVICE); 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; } } } |