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