2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.applications;
19 import com.android.settings.R;
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;
44 import java.io.FileInputStream;
45 import java.util.ArrayList;
46 import java.util.HashMap;
47 import java.util.Iterator;
49 public class RunningProcessesView extends FrameLayout
50 implements AdapterView.OnItemClickListener, RecyclerListener,
51 RunningState.OnRefreshUiListener {
53 // Memory pages are 4K.
54 static final long PAGE_SIZE = 4*1024;
56 long SECONDARY_SERVER_MEM;
58 final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
66 StringBuilder mBuilder = new StringBuilder(128);
68 RunningState.BaseItem mCurSelected;
71 ServiceListAdapter mAdapter;
72 LinearColorBar mColorBar;
73 TextView mBackgroundProcessText;
74 TextView mForegroundProcessText;
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;
86 byte[] mBuffer = new byte[1024];
88 public static class ActiveItem {
90 RunningState.BaseItem mItem;
91 ActivityManager.RunningServiceInfo mService;
94 boolean mSetBackground;
96 void updateTime(Context context, StringBuilder builder) {
97 TextView uptimeView = null;
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;
105 String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
106 if (!size.equals(mItem.mCurSizeStr)) {
107 mItem.mCurSizeStr = size;
108 mHolder.size.setText(size);
111 if (mItem.mBackground) {
112 // This is a background process; no uptime.
113 if (!mSetBackground) {
114 mSetBackground = true;
115 mHolder.uptime.setText("");
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;
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));
132 boolean isService = false;
133 if (mItem instanceof RunningState.MergedItem) {
134 isService = ((RunningState.MergedItem)mItem).mServices.size() > 0;
137 uptimeView.setText(context.getResources().getText(
138 R.string.service_restarting));
140 uptimeView.setText("");
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;
155 public ViewHolder(View 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);
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;
176 name.setText(item.mDisplayLabel);
177 ActiveItem ai = new ActiveItem();
178 ai.mRootView = rootView;
181 ai.mFirstRunTime = item.mActiveSince;
182 if (item.mBackground) {
183 description.setText(rootView.getContext().getText(R.string.cached));
185 description.setText(item.mDescription);
187 item.mCurSizeStr = null;
188 if (item.mPackageInfo != null) {
189 icon.setImageDrawable(item.mPackageInfo.loadIcon(pm));
191 icon.setVisibility(View.VISIBLE);
192 ai.updateTime(rootView.getContext(), builder);
198 static class TimeTicker extends TextView {
199 public TimeTicker(Context context, AttributeSet attrs) {
200 super(context, attrs);
204 class ServiceListAdapter extends BaseAdapter {
205 final RunningState mState;
206 final LayoutInflater mInflater;
207 boolean mShowBackground;
208 ArrayList<RunningState.MergedItem> mItems;
210 ServiceListAdapter(RunningState state) {
212 mInflater = (LayoutInflater)getContext().getSystemService(
213 Context.LAYOUT_INFLATER_SERVICE);
217 void setShowBackground(boolean showBackground) {
218 if (mShowBackground != showBackground) {
219 mShowBackground = showBackground;
220 mState.setWatchingBackgroundItems(showBackground);
222 notifyDataSetChanged();
223 mColorBar.setShowingGreen(mShowBackground);
227 boolean getShowBackground() {
228 return mShowBackground;
231 void refreshItems() {
232 ArrayList<RunningState.MergedItem> newItems =
233 mShowBackground ? mState.getCurrentBackgroundItems()
234 : mState.getCurrentMergedItems();
235 if (mItems != newItems) {
238 if (mItems == null) {
239 mItems = new ArrayList<RunningState.MergedItem>();
243 public boolean hasStableIds() {
247 public int getCount() {
248 return mItems.size();
252 public boolean isEmpty() {
253 return mState.hasData() && mItems.size() == 0;
256 public Object getItem(int position) {
257 return mItems.get(position);
260 public long getItemId(int position) {
261 return mItems.get(position).hashCode();
264 public boolean areAllItemsEnabled() {
268 public boolean isEnabled(int position) {
269 return !mItems.get(position).mIsProcess;
272 public View getView(int position, View convertView, ViewGroup parent) {
274 if (convertView == null) {
279 bindView(v, position);
283 public View newView(ViewGroup parent) {
284 View v = mInflater.inflate(R.layout.running_processes_item, parent, false);
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.
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);
305 private boolean matchText(byte[] buffer, int index, String text) {
306 int N = text.length();
307 if ((index+N) >= buffer.length) {
310 for (int i=0; i<N; i++) {
311 if (buffer[index+i] != text.charAt(i)) {
318 private long extractMemValue(byte[] buffer, int index) {
319 while (index < buffer.length && buffer[index] != '\n') {
320 if (buffer[index] >= '0' && buffer[index] <= '9') {
323 while (index < buffer.length && buffer[index] >= '0'
324 && buffer[index] <= '9') {
327 String str = new String(buffer, 0, start, index-start);
328 return ((long)Integer.parseInt(str)) * 1024;
335 private long readAvailMem() {
339 FileInputStream is = new FileInputStream("/proc/meminfo");
340 int len = is.read(mBuffer);
342 final int BUFLEN = mBuffer.length;
343 for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
344 if (matchText(mBuffer, i, "MemFree")) {
346 memFree = extractMemValue(mBuffer, i);
347 } else if (matchText(mBuffer, i, "Cached")) {
349 memCached = extractMemValue(mBuffer, i);
351 while (i < BUFLEN && mBuffer[i] != '\n') {
355 return memFree + memCached;
356 } catch (java.io.FileNotFoundException e) {
357 } catch (java.io.IOException e) {
363 void refreshUi(boolean dataChanged) {
365 ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
366 adapter.refreshItems();
367 adapter.notifyDataSetChanged();
370 if (mDataAvail != null) {
375 // This is the amount of available memory until we start killing
376 // background services.
377 long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
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));
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));
408 float totalMem = availMem + mLastBackgroundProcessMemory
409 + mLastForegroundProcessMemory + mLastServiceProcessMemory;
410 mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
411 mLastServiceProcessMemory/totalMem,
412 mLastBackgroundProcessMemory/totalMem);
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);
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);
428 public void onMovedToScrapHeap(View view) {
429 mActiveItems.remove(view);
432 public RunningProcessesView(Context context, AttributeSet attrs) {
433 super(context, attrs);
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);
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() {
455 public void onClick(View v) {
456 mAdapter.setShowBackground(true);
459 mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
460 mForegroundProcessText.setOnClickListener(new View.OnClickListener() {
462 public void onClick(View v) {
463 mAdapter.setShowBackground(false);
467 // Magic! Implementation detail! Don't count on this!
468 SECONDARY_SERVER_MEM =
469 Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
472 public void doPause() {
477 public boolean doResume(Runnable dataAvail) {
479 if (mState.hasData()) {
480 // If the state already has its data, then let's populate our
481 // list right now to avoid flicker.
485 mDataAvail = dataAvail;
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.
498 ai.updateTime(getContext(), mBuilder);
503 public void onRefreshUi(int what) {
512 case REFRESH_STRUCTURE: