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