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