Home | History | Annotate | Download | only in applications
      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 
     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 
    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 
    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 {
    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 
    215         ServiceListAdapter(RunningState state) {
    216             mState = state;
    217             mInflater = (LayoutInflater)getContext().getSystemService(
    218                     Context.LAYOUT_INFLATER_SERVICE);
    219             refreshItems();
    220         }
    221 
    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 
    232         boolean getShowBackground() {
    233             return mShowBackground;
    234         }
    235 
    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 
    254         public boolean hasStableIds() {
    255             return true;
    256         }
    257 
    258         public int getCount() {
    259             return mItems.size();
    260         }
    261 
    262         @Override
    263         public boolean isEmpty() {
    264             return mState.hasData() && mItems.size() == 0;
    265         }
    266 
    267         public Object getItem(int position) {
    268             return mItems.get(position);
    269         }
    270 
    271         public long getItemId(int position) {
    272             return mItems.get(position).hashCode();
    273         }
    274 
    275         public boolean areAllItemsEnabled() {
    276             return false;
    277         }
    278 
    279         public boolean isEnabled(int position) {
    280             return !mItems.get(position).mIsProcess;
    281         }
    282 
    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 
    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 
    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 
    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 
    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
    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 
    403     public void onMovedToScrapHeap(View view) {
    404         mActiveItems.remove(view);
    405     }
    406 
    407     public RunningProcessesView(Context context, AttributeSet attrs) {
    408         super(context, attrs);
    409         mMyUserId = UserHandle.myUserId();
    410     }
    411 
    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 
    448     public void doPause() {
    449         mState.pause();
    450         mDataAvail = null;
    451         mOwner = null;
    452     }
    453 
    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 
    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
    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