OSDN Git Service

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