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