• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.settings.applications;
18 
19 import android.app.ActivityManager;
20 import android.app.Dialog;
21 import android.app.Fragment;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.os.Bundle;
25 import android.os.SystemClock;
26 import android.os.UserHandle;
27 import android.text.BidiFormatter;
28 import android.text.format.DateUtils;
29 import android.text.format.Formatter;
30 import android.util.AttributeSet;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.AbsListView.RecyclerListener;
35 import android.widget.AdapterView;
36 import android.widget.BaseAdapter;
37 import android.widget.FrameLayout;
38 import android.widget.ImageView;
39 import android.widget.ListView;
40 import android.widget.TextView;
41 
42 import com.android.internal.util.MemInfoReader;
43 import com.android.settings.R;
44 import com.android.settings.SettingsActivity;
45 
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.HashMap;
49 import java.util.Iterator;
50 
51 public class RunningProcessesView extends FrameLayout
52         implements AdapterView.OnItemClickListener, RecyclerListener,
53         RunningState.OnRefreshUiListener {
54 
55     final int mMyUserId;
56 
57     long SECONDARY_SERVER_MEM;
58 
59     final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
60 
61     ActivityManager mAm;
62 
63     RunningState mState;
64 
65     Fragment mOwner;
66 
67     Runnable mDataAvail;
68 
69     StringBuilder mBuilder = new StringBuilder(128);
70 
71     RunningState.BaseItem mCurSelected;
72 
73     ListView mListView;
74     View mHeader;
75     ServiceListAdapter mAdapter;
76     LinearColorBar mColorBar;
77     TextView mBackgroundProcessPrefix;
78     TextView mAppsProcessPrefix;
79     TextView mForegroundProcessPrefix;
80     TextView mBackgroundProcessText;
81     TextView mAppsProcessText;
82     TextView mForegroundProcessText;
83 
84     long mCurTotalRam = -1;
85     long mCurHighRam = -1;      // "System" or "Used"
86     long mCurMedRam = -1;       // "Apps" or "Cached"
87     long mCurLowRam = -1;       // "Free"
88     boolean mCurShowCached = false;
89 
90     Dialog mCurDialog;
91 
92     MemInfoReader mMemInfoReader = new MemInfoReader();
93 
94     public static class ActiveItem {
95         View mRootView;
96         RunningState.BaseItem mItem;
97         ActivityManager.RunningServiceInfo mService;
98         ViewHolder mHolder;
99         long mFirstRunTime;
100         boolean mSetBackground;
101 
updateTime(Context context, StringBuilder builder)102         void updateTime(Context context, StringBuilder builder) {
103             TextView uptimeView = null;
104 
105             if (mItem instanceof RunningState.ServiceItem) {
106                 // If we are displaying a service, then the service
107                 // uptime goes at the top.
108                 uptimeView = mHolder.size;
109 
110             } else {
111                 String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
112                 if (!size.equals(mItem.mCurSizeStr)) {
113                     mItem.mCurSizeStr = size;
114                     mHolder.size.setText(size);
115                 }
116 
117                 if (mItem.mBackground) {
118                     // This is a background process; no uptime.
119                     if (!mSetBackground) {
120                         mSetBackground = true;
121                         mHolder.uptime.setText("");
122                     }
123                 } else if (mItem instanceof RunningState.MergedItem) {
124                     // This item represents both services and processes,
125                     // so show the service uptime below.
126                     uptimeView = mHolder.uptime;
127                 }
128             }
129 
130             if (uptimeView != null) {
131                 mSetBackground = false;
132                 if (mFirstRunTime >= 0) {
133                     //Log.i("foo", "Time for " + mItem.mDisplayLabel
134                     //        + ": " + (SystemClock.uptimeMillis()-mFirstRunTime));
135                     uptimeView.setText(DateUtils.formatElapsedTime(builder,
136                             (SystemClock.elapsedRealtime()-mFirstRunTime)/1000));
137                 } else {
138                     boolean isService = false;
139                     if (mItem instanceof RunningState.MergedItem) {
140                         isService = ((RunningState.MergedItem)mItem).mServices.size() > 0;
141                     }
142                     if (isService) {
143                         uptimeView.setText(context.getResources().getText(
144                                 R.string.service_restarting));
145                     } else {
146                         uptimeView.setText("");
147                     }
148                 }
149             }
150         }
151     }
152 
153     public static class ViewHolder {
154         public View rootView;
155         public ImageView icon;
156         public TextView name;
157         public TextView description;
158         public TextView size;
159         public TextView uptime;
160 
ViewHolder(View v)161         public ViewHolder(View v) {
162             rootView = v;
163             icon = (ImageView)v.findViewById(R.id.icon);
164             name = (TextView)v.findViewById(R.id.name);
165             description = (TextView)v.findViewById(R.id.description);
166             size = (TextView)v.findViewById(R.id.size);
167             uptime = (TextView)v.findViewById(R.id.uptime);
168             v.setTag(this);
169         }
170 
bind(RunningState state, RunningState.BaseItem item, StringBuilder builder)171         public ActiveItem bind(RunningState state, RunningState.BaseItem item,
172                 StringBuilder builder) {
173             synchronized (state.mLock) {
174                 PackageManager pm = rootView.getContext().getPackageManager();
175                 if (item.mPackageInfo == null && item instanceof RunningState.MergedItem) {
176                     // Items for background processes don't normally load
177                     // their labels for performance reasons.  Do it now.
178                     RunningState.MergedItem mergedItem = (RunningState.MergedItem)item;
179                     if (mergedItem.mProcess != null) {
180                         ((RunningState.MergedItem)item).mProcess.ensureLabel(pm);
181                         item.mPackageInfo = ((RunningState.MergedItem)item).mProcess.mPackageInfo;
182                         item.mDisplayLabel = ((RunningState.MergedItem)item).mProcess.mDisplayLabel;
183                     }
184                 }
185                 name.setText(item.mDisplayLabel);
186                 ActiveItem ai = new ActiveItem();
187                 ai.mRootView = rootView;
188                 ai.mItem = item;
189                 ai.mHolder = this;
190                 ai.mFirstRunTime = item.mActiveSince;
191                 if (item.mBackground) {
192                     description.setText(rootView.getContext().getText(R.string.cached));
193                 } else {
194                     description.setText(item.mDescription);
195                 }
196                 item.mCurSizeStr = null;
197                 icon.setImageDrawable(item.loadIcon(rootView.getContext(), state));
198                 icon.setVisibility(View.VISIBLE);
199                 ai.updateTime(rootView.getContext(), builder);
200                 return ai;
201             }
202         }
203     }
204 
205     static class TimeTicker extends TextView {
TimeTicker(Context context, AttributeSet attrs)206         public TimeTicker(Context context, AttributeSet attrs) {
207             super(context, attrs);
208         }
209     }
210 
211     class ServiceListAdapter extends BaseAdapter {
212         final RunningState mState;
213         final LayoutInflater mInflater;
214         boolean mShowBackground;
215         ArrayList<RunningState.MergedItem> mOrigItems;
216         final ArrayList<RunningState.MergedItem> mItems
217                 = new ArrayList<RunningState.MergedItem>();
218 
ServiceListAdapter(RunningState state)219         ServiceListAdapter(RunningState state) {
220             mState = state;
221             mInflater = (LayoutInflater)getContext().getSystemService(
222                     Context.LAYOUT_INFLATER_SERVICE);
223             refreshItems();
224         }
225 
setShowBackground(boolean showBackground)226         void setShowBackground(boolean showBackground) {
227             if (mShowBackground != showBackground) {
228                 mShowBackground = showBackground;
229                 mState.setWatchingBackgroundItems(showBackground);
230                 refreshItems();
231                 refreshUi(true);
232             }
233         }
234 
getShowBackground()235         boolean getShowBackground() {
236             return mShowBackground;
237         }
238 
refreshItems()239         void refreshItems() {
240             ArrayList<RunningState.MergedItem> newItems =
241                 mShowBackground ? mState.getCurrentBackgroundItems()
242                         : mState.getCurrentMergedItems();
243             if (mOrigItems != newItems) {
244                 mOrigItems = newItems;
245                 if (newItems == null) {
246                     mItems.clear();
247                 } else {
248                     mItems.clear();
249                     mItems.addAll(newItems);
250                     if (mShowBackground) {
251                         Collections.sort(mItems, mState.mBackgroundComparator);
252                     }
253                 }
254             }
255         }
256 
hasStableIds()257         public boolean hasStableIds() {
258             return true;
259         }
260 
getCount()261         public int getCount() {
262             return mItems.size();
263         }
264 
265         @Override
isEmpty()266         public boolean isEmpty() {
267             return mState.hasData() && mItems.size() == 0;
268         }
269 
getItem(int position)270         public Object getItem(int position) {
271             return mItems.get(position);
272         }
273 
getItemId(int position)274         public long getItemId(int position) {
275             return mItems.get(position).hashCode();
276         }
277 
areAllItemsEnabled()278         public boolean areAllItemsEnabled() {
279             return false;
280         }
281 
isEnabled(int position)282         public boolean isEnabled(int position) {
283             return !mItems.get(position).mIsProcess;
284         }
285 
getView(int position, View convertView, ViewGroup parent)286         public View getView(int position, View convertView, ViewGroup parent) {
287             View v;
288             if (convertView == null) {
289                 v = newView(parent);
290             } else {
291                 v = convertView;
292             }
293             bindView(v, position);
294             return v;
295         }
296 
newView(ViewGroup parent)297         public View newView(ViewGroup parent) {
298             View v = mInflater.inflate(R.layout.running_processes_item, parent, false);
299             new ViewHolder(v);
300             return v;
301         }
302 
bindView(View view, int position)303         public void bindView(View view, int position) {
304             synchronized (mState.mLock) {
305                 if (position >= mItems.size()) {
306                     // List must have changed since we last reported its
307                     // size...  ignore here, we will be doing a data changed
308                     // to refresh the entire list.
309                     return;
310                 }
311                 ViewHolder vh = (ViewHolder) view.getTag();
312                 RunningState.MergedItem item = mItems.get(position);
313                 ActiveItem ai = vh.bind(mState, item, mBuilder);
314                 mActiveItems.put(view, ai);
315             }
316         }
317     }
318 
refreshUi(boolean dataChanged)319     void refreshUi(boolean dataChanged) {
320         if (dataChanged) {
321             ServiceListAdapter adapter = mAdapter;
322             adapter.refreshItems();
323             adapter.notifyDataSetChanged();
324         }
325 
326         if (mDataAvail != null) {
327             mDataAvail.run();
328             mDataAvail = null;
329         }
330 
331         mMemInfoReader.readMemInfo();
332 
333         /*
334         // This is the amount of available memory until we start killing
335         // background services.
336         long availMem = mMemInfoReader.getFreeSize() + mMemInfoReader.getCachedSize()
337                 - SECONDARY_SERVER_MEM;
338         if (availMem < 0) {
339             availMem = 0;
340         }
341         */
342 
343         synchronized (mState.mLock) {
344             if (mCurShowCached != mAdapter.mShowBackground) {
345                 mCurShowCached = mAdapter.mShowBackground;
346                 if (mCurShowCached) {
347                     mForegroundProcessPrefix.setText(getResources().getText(
348                             R.string.running_processes_header_used_prefix));
349                     mAppsProcessPrefix.setText(getResources().getText(
350                             R.string.running_processes_header_cached_prefix));
351                 } else {
352                     mForegroundProcessPrefix.setText(getResources().getText(
353                             R.string.running_processes_header_system_prefix));
354                     mAppsProcessPrefix.setText(getResources().getText(
355                             R.string.running_processes_header_apps_prefix));
356                 }
357             }
358 
359             final long totalRam = mMemInfoReader.getTotalSize();
360             final long medRam;
361             final long lowRam;
362             if (mCurShowCached) {
363                 lowRam = mMemInfoReader.getFreeSize() + mMemInfoReader.getCachedSize();
364                 medRam = mState.mBackgroundProcessMemory;
365             } else {
366                 lowRam = mMemInfoReader.getFreeSize() + mMemInfoReader.getCachedSize()
367                         + mState.mBackgroundProcessMemory;
368                 medRam = mState.mServiceProcessMemory;
369 
370             }
371             final long highRam = totalRam - medRam - lowRam;
372 
373             if (mCurTotalRam != totalRam || mCurHighRam != highRam || mCurMedRam != medRam
374                     || mCurLowRam != lowRam) {
375                 mCurTotalRam = totalRam;
376                 mCurHighRam = highRam;
377                 mCurMedRam = medRam;
378                 mCurLowRam = lowRam;
379                 BidiFormatter bidiFormatter = BidiFormatter.getInstance();
380                 String sizeStr = bidiFormatter.unicodeWrap(
381                         Formatter.formatShortFileSize(getContext(), lowRam));
382                 mBackgroundProcessText.setText(getResources().getString(
383                         R.string.running_processes_header_ram, sizeStr));
384                 sizeStr = bidiFormatter.unicodeWrap(
385                         Formatter.formatShortFileSize(getContext(), medRam));
386                 mAppsProcessText.setText(getResources().getString(
387                         R.string.running_processes_header_ram, sizeStr));
388                 sizeStr = bidiFormatter.unicodeWrap(
389                         Formatter.formatShortFileSize(getContext(), highRam));
390                 mForegroundProcessText.setText(getResources().getString(
391                         R.string.running_processes_header_ram, sizeStr));
392                 mColorBar.setRatios(highRam/(float)totalRam,
393                         medRam/(float)totalRam,
394                         lowRam/(float)totalRam);
395             }
396         }
397     }
398 
onItemClick(AdapterView<?> parent, View v, int position, long id)399     public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
400         ListView l = (ListView)parent;
401         RunningState.MergedItem mi = (RunningState.MergedItem)l.getAdapter().getItem(position);
402         mCurSelected = mi;
403         startServiceDetailsActivity(mi);
404     }
405 
406     // utility method used to start sub activity
startServiceDetailsActivity(RunningState.MergedItem mi)407     private void startServiceDetailsActivity(RunningState.MergedItem mi) {
408         if (mOwner != null && mi != null) {
409             // start new fragment to display extended information
410             Bundle args = new Bundle();
411             if (mi.mProcess != null) {
412                 args.putInt(RunningServiceDetails.KEY_UID, mi.mProcess.mUid);
413                 args.putString(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName);
414             }
415             args.putInt(RunningServiceDetails.KEY_USER_ID, mi.mUserId);
416             args.putBoolean(RunningServiceDetails.KEY_BACKGROUND, mAdapter.mShowBackground);
417 
418             SettingsActivity sa = (SettingsActivity) mOwner.getActivity();
419             sa.startPreferencePanel(RunningServiceDetails.class.getName(), args,
420                     R.string.runningservicedetails_settings_title, null, null, 0);
421         }
422     }
423 
onMovedToScrapHeap(View view)424     public void onMovedToScrapHeap(View view) {
425         mActiveItems.remove(view);
426     }
427 
RunningProcessesView(Context context, AttributeSet attrs)428     public RunningProcessesView(Context context, AttributeSet attrs) {
429         super(context, attrs);
430         mMyUserId = UserHandle.myUserId();
431     }
432 
doCreate()433     public void doCreate() {
434         mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
435         mState = RunningState.getInstance(getContext());
436         LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
437                 Context.LAYOUT_INFLATER_SERVICE);
438         inflater.inflate(R.layout.running_processes_view, this);
439         mListView = (ListView)findViewById(android.R.id.list);
440         View emptyView = findViewById(com.android.internal.R.id.empty);
441         if (emptyView != null) {
442             mListView.setEmptyView(emptyView);
443         }
444         mListView.setOnItemClickListener(this);
445         mListView.setRecyclerListener(this);
446         mAdapter = new ServiceListAdapter(mState);
447         mListView.setAdapter(mAdapter);
448         mHeader = inflater.inflate(R.layout.running_processes_header, null);
449         mListView.addHeaderView(mHeader, null, false /* set as not selectable */);
450         mColorBar = (LinearColorBar)mHeader.findViewById(R.id.color_bar);
451         final Context context = getContext();
452         mColorBar.setColors(context.getColor(R.color.running_processes_system_ram),
453                 context.getColor(R.color.running_processes_apps_ram),
454                 context.getColor(R.color.running_processes_free_ram));
455         mBackgroundProcessPrefix = (TextView)mHeader.findViewById(R.id.freeSizePrefix);
456         mAppsProcessPrefix = (TextView)mHeader.findViewById(R.id.appsSizePrefix);
457         mForegroundProcessPrefix = (TextView)mHeader.findViewById(R.id.systemSizePrefix);
458         mBackgroundProcessText = (TextView)mHeader.findViewById(R.id.freeSize);
459         mAppsProcessText = (TextView)mHeader.findViewById(R.id.appsSize);
460         mForegroundProcessText = (TextView)mHeader.findViewById(R.id.systemSize);
461 
462         ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
463         mAm.getMemoryInfo(memInfo);
464         SECONDARY_SERVER_MEM = memInfo.secondaryServerThreshold;
465     }
466 
doPause()467     public void doPause() {
468         mState.pause();
469         mDataAvail = null;
470         mOwner = null;
471     }
472 
doResume(Fragment owner, Runnable dataAvail)473     public boolean doResume(Fragment owner, Runnable dataAvail) {
474         mOwner = owner;
475         mState.resume(this);
476         if (mState.hasData()) {
477             // If the state already has its data, then let's populate our
478             // list right now to avoid flicker.
479             refreshUi(true);
480             return true;
481         }
482         mDataAvail = dataAvail;
483         return false;
484     }
485 
updateTimes()486     void updateTimes() {
487         Iterator<ActiveItem> it = mActiveItems.values().iterator();
488         while (it.hasNext()) {
489             ActiveItem ai = it.next();
490             if (ai.mRootView.getWindowToken() == null) {
491                 // Clean out any dead views, just in case.
492                 it.remove();
493                 continue;
494             }
495             ai.updateTime(getContext(), mBuilder);
496         }
497     }
498 
499     @Override
onRefreshUi(int what)500     public void onRefreshUi(int what) {
501         switch (what) {
502             case REFRESH_TIME:
503                 updateTimes();
504                 break;
505             case REFRESH_DATA:
506                 refreshUi(false);
507                 updateTimes();
508                 break;
509             case REFRESH_STRUCTURE:
510                 refreshUi(true);
511                 updateTimes();
512                 break;
513         }
514     }
515 }
516