diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 78c0f5b3540..c3b0236541c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -350,7 +350,7 @@ - @@ -365,15 +365,22 @@ - + + + + - + - + - @@ -384,6 +391,24 @@ + --> + + + + + + + + + + + + + + - + android:layout_height="match_parent" > + + + + + + \ No newline at end of file diff --git a/res/layout/manage_applications_item.xml b/res/layout/manage_applications_item.xml index cdf4c9de58e..48274ce0f98 100755 --- a/res/layout/manage_applications_item.xml +++ b/res/layout/manage_applications_item.xml @@ -31,7 +31,6 @@ diff --git a/res/layout/running_processes_item.xml b/res/layout/running_processes_item.xml new file mode 100644 index 00000000000..dacee9e5e51 --- /dev/null +++ b/res/layout/running_processes_item.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/running_processes_view.xml b/res/layout/running_processes_view.xml new file mode 100644 index 00000000000..6810ba0003f --- /dev/null +++ b/res/layout/running_processes_view.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + diff --git a/res/layout/running_service_details.xml b/res/layout/running_service_details.xml new file mode 100644 index 00000000000..98c144c1350 --- /dev/null +++ b/res/layout/running_service_details.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/res/layout/running_service_details_process.xml b/res/layout/running_service_details_process.xml new file mode 100644 index 00000000000..d12b4865493 --- /dev/null +++ b/res/layout/running_service_details_process.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/res/layout/running_service_details_service.xml b/res/layout/running_service_details_service.xml new file mode 100644 index 00000000000..b9f9bbe3dbc --- /dev/null +++ b/res/layout/running_service_details_service.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + diff --git a/res/layout/running_services.xml b/res/layout/running_services.xml index 5c0da6f1a96..faca22c39db 100644 --- a/res/layout/running_services.xml +++ b/res/layout/running_services.xml @@ -35,7 +35,7 @@ android:text="@string/no_running_services" android:textAppearance="?android:attr/textAppearanceLarge" /> - + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 6b426c8febe..ebd11f87a6d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1766,16 +1766,55 @@ found in the list of installed applications. Cancel - Started by application: touch to stop + Started by application. - %1$s: touch to manage + %1$s - Avail: %2$s+%3$s in %1$d + %1$s available - Other: %2$s in %1$d + %1$s in use - Process: %1$s + %1$s + + %1$d + process and %2$d service + + %1$d + process and %2$d services + + %1$d + processes and %2$d service + + %1$d + processes and %2$d services + + Running application + + Not active + + Services + + Processes + + Stop + + Manage + + This service was started by its + application. Stopping it may cause the application to misbehave. + + %1$s: + currently in use. You can manage your settings to stop it. + + Main process that is in use. + + Service %1$s + is in use. + + Provider %1$s + is in use. + Language & keyboard diff --git a/res/xml/application_settings.xml b/res/xml/application_settings.xml index 4df21da43ba..6ac58d1acd6 100644 --- a/res/xml/application_settings.xml +++ b/res/xml/application_settings.xml @@ -59,6 +59,15 @@ android:targetClass="com.android.settings.RunningServices" /> + + + + diff --git a/src/com/android/settings/RunningServices.java b/src/com/android/settings/RunningServices.java deleted file mode 100644 index e67adf06a03..00000000000 --- a/src/com/android/settings/RunningServices.java +++ /dev/null @@ -1,1172 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings; - -import com.android.settings.R; -import android.app.ActivityManager; -import android.app.ActivityManagerNative; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ListActivity; -import android.app.PendingIntent; -import android.content.ActivityNotFoundException; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentSender; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageItemInfo; -import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.Debug; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.text.format.DateUtils; -import android.text.format.Formatter; -import android.util.AttributeSet; -import android.util.Log; -import android.util.SparseArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.TextView; - -import java.io.FileInputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -public class RunningServices extends ListActivity - implements AbsListView.RecyclerListener, - DialogInterface.OnClickListener { - static final String TAG = "RunningServices"; - - /** Maximum number of services to retrieve */ - static final int MAX_SERVICES = 100; - - static final int MSG_UPDATE_TIMES = 1; - static final int MSG_UPDATE_CONTENTS = 2; - static final int MSG_REFRESH_UI = 3; - - static final long TIME_UPDATE_DELAY = 1000; - static final long CONTENTS_UPDATE_DELAY = 2000; - - // Memory pages are 4K. - static final long PAGE_SIZE = 4*1024; - - long SECONDARY_SERVER_MEM; - - final HashMap mActiveItems = new HashMap(); - - ActivityManager mAm; - - State mState; - - StringBuilder mBuilder = new StringBuilder(128); - - BaseItem mCurSelected; - - int mProcessBgColor; - - LinearColorBar mColorBar; - TextView mBackgroundProcessText; - TextView mForegroundProcessText; - - int mLastNumBackgroundProcesses = -1; - int mLastNumForegroundProcesses = -1; - int mLastNumServiceProcesses = -1; - long mLastBackgroundProcessMemory = -1; - long mLastForegroundProcessMemory = -1; - long mLastServiceProcessMemory = -1; - long mLastAvailMemory = -1; - - Dialog mCurDialog; - - byte[] mBuffer = new byte[1024]; - - class ActiveItem { - View mRootView; - BaseItem mItem; - ActivityManager.RunningServiceInfo mService; - ViewHolder mHolder; - long mFirstRunTime; - - void updateTime(Context context) { - if (mItem.mIsProcess) { - String size = mItem.mSizeStr != null ? mItem.mSizeStr : ""; - if (!size.equals(mItem.mCurSizeStr)) { - mItem.mCurSizeStr = size; - mHolder.size.setText(size); - } - } else { - if (mItem.mActiveSince >= 0) { - mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder, - (SystemClock.uptimeMillis()-mFirstRunTime)/1000)); - } else { - mHolder.size.setText(context.getResources().getText( - R.string.service_restarting)); - } - } - } - } - - static class BaseItem { - final boolean mIsProcess; - - PackageItemInfo mPackageInfo; - CharSequence mDisplayLabel; - String mLabel; - String mDescription; - - int mCurSeq; - - long mActiveSince; - long mSize; - String mSizeStr; - String mCurSizeStr; - boolean mNeedDivider; - - public BaseItem(boolean isProcess) { - mIsProcess = isProcess; - } - } - - static class ServiceItem extends BaseItem { - ActivityManager.RunningServiceInfo mRunningService; - ServiceInfo mServiceInfo; - boolean mShownAsStarted; - - public ServiceItem() { - super(false); - } - } - - static class ProcessItem extends BaseItem { - final HashMap mServices - = new HashMap(); - final SparseArray mDependentProcesses - = new SparseArray(); - - final int mUid; - final String mProcessName; - int mPid; - - ProcessItem mClient; - int mLastNumDependentProcesses; - - int mRunningSeq; - ActivityManager.RunningAppProcessInfo mRunningProcessInfo; - - // Purely for sorting. - boolean mIsSystem; - boolean mIsStarted; - long mActiveSince; - - public ProcessItem(Context context, int uid, String processName) { - super(true); - mDescription = context.getResources().getString( - R.string.service_process_name, processName); - mUid = uid; - mProcessName = processName; - } - - void ensureLabel(PackageManager pm) { - if (mLabel != null) { - return; - } - - try { - ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0); - if (ai.uid == mUid) { - mDisplayLabel = ai.loadLabel(pm); - mLabel = mDisplayLabel.toString(); - mPackageInfo = ai; - return; - } - } catch (PackageManager.NameNotFoundException e) { - } - - // If we couldn't get information about the overall - // process, try to find something about the uid. - String[] pkgs = pm.getPackagesForUid(mUid); - - // If there is one package with this uid, that is what we want. - if (pkgs.length == 1) { - try { - ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); - mDisplayLabel = ai.loadLabel(pm); - mLabel = mDisplayLabel.toString(); - mPackageInfo = ai; - return; - } catch (PackageManager.NameNotFoundException e) { - } - } - - // If there are multiple, see if one gives us the official name - // for this uid. - for (String name : pkgs) { - try { - PackageInfo pi = pm.getPackageInfo(name, 0); - if (pi.sharedUserLabel != 0) { - CharSequence nm = pm.getText(name, - pi.sharedUserLabel, pi.applicationInfo); - if (nm != null) { - mDisplayLabel = nm; - mLabel = nm.toString(); - mPackageInfo = pi.applicationInfo; - return; - } - } - } catch (PackageManager.NameNotFoundException e) { - } - } - - // If still don't have anything to display, just use the - // service info. - if (mServices.size() > 0) { - mPackageInfo = mServices.values().iterator().next() - .mServiceInfo.applicationInfo; - mDisplayLabel = mPackageInfo.loadLabel(pm); - mLabel = mDisplayLabel.toString(); - return; - } - - // Finally... whatever, just pick the first package's name. - try { - ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); - mDisplayLabel = ai.loadLabel(pm); - mLabel = mDisplayLabel.toString(); - mPackageInfo = ai; - return; - } catch (PackageManager.NameNotFoundException e) { - } - } - - boolean updateService(Context context, - ActivityManager.RunningServiceInfo service) { - final PackageManager pm = context.getPackageManager(); - - boolean changed = false; - ServiceItem si = mServices.get(service.service); - if (si == null) { - changed = true; - si = new ServiceItem(); - si.mRunningService = service; - try { - si.mServiceInfo = pm.getServiceInfo(service.service, 0); - } catch (PackageManager.NameNotFoundException e) { - } - if (si.mServiceInfo != null && (si.mServiceInfo.labelRes != 0 - || si.mServiceInfo.nonLocalizedLabel != null)) { - si.mDisplayLabel = si.mServiceInfo.loadLabel(pm); - si.mLabel = si.mDisplayLabel.toString(); - } else { - si.mLabel = si.mRunningService.service.getClassName(); - int tail = si.mLabel.lastIndexOf('.'); - if (tail >= 0) { - si.mLabel = si.mLabel.substring(tail+1, si.mLabel.length()); - } - si.mDisplayLabel = si.mLabel; - } - si.mPackageInfo = si.mServiceInfo.applicationInfo; - mServices.put(service.service, si); - } - si.mCurSeq = mCurSeq; - si.mRunningService = service; - long activeSince = service.restarting == 0 ? service.activeSince : -1; - if (si.mActiveSince != activeSince) { - si.mActiveSince = activeSince; - changed = true; - } - if (service.clientPackage != null && service.clientLabel != 0) { - if (si.mShownAsStarted) { - si.mShownAsStarted = false; - changed = true; - } - try { - Resources clientr = pm.getResourcesForApplication(service.clientPackage); - String label = clientr.getString(service.clientLabel); - si.mDescription = context.getResources().getString( - R.string.service_client_name, label); - } catch (PackageManager.NameNotFoundException e) { - si.mDescription = null; - } - } else { - if (!si.mShownAsStarted) { - si.mShownAsStarted = true; - changed = true; - } - si.mDescription = context.getResources().getString( - R.string.service_started_by_app); - } - - return changed; - } - - boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) { - mSize = ((long)mem.getTotalPss()) * 1024; - if (mCurSeq == curSeq) { - String sizeStr = Formatter.formatShortFileSize( - context, mSize); - if (!sizeStr.equals(mSizeStr)){ - mSizeStr = sizeStr; - // We update this on the second tick where we update just - // the text in the current items, so no need to say we - // changed here. - return false; - } - } - return false; - } - - boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) { - final int NP = mDependentProcesses.size(); - boolean changed = false; - for (int i=0; i dest, - ArrayList destProc) { - final int NP = mDependentProcesses.size(); - for (int i=0; i 0) { - destProc.add(proc); - } - } - } - } - - static class ServiceProcessComparator implements Comparator { - public int compare(ProcessItem object1, ProcessItem object2) { - if (object1.mIsStarted != object2.mIsStarted) { - // Non-started processes go last. - return object1.mIsStarted ? -1 : 1; - } - if (object1.mIsSystem != object2.mIsSystem) { - // System processes go below non-system. - return object1.mIsSystem ? 1 : -1; - } - if (object1.mActiveSince != object2.mActiveSince) { - // Remaining ones are sorted with the longest running - // services last. - return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1; - } - return 0; - } - } - - static class State { - final SparseArray> mProcesses - = new SparseArray>(); - final SparseArray mActiveProcesses - = new SparseArray(); - final ServiceProcessComparator mServiceProcessComparator - = new ServiceProcessComparator(); - - // Temporary for finding process dependencies. - final SparseArray mRunningProcesses - = new SparseArray(); - - final ArrayList mProcessItems = new ArrayList(); - final ArrayList mAllProcessItems = new ArrayList(); - - int mSequence = 0; - - // ----- following protected by mLock ----- - - // Lock for protecting the state that will be shared between the - // background update thread and the UI thread. - final Object mLock = new Object(); - - ArrayList mItems = new ArrayList(); - - int mNumBackgroundProcesses; - long mBackgroundProcessMemory; - int mNumForegroundProcesses; - long mForegroundProcessMemory; - int mNumServiceProcesses; - long mServiceProcessMemory; - - boolean update(Context context, ActivityManager am) { - final PackageManager pm = context.getPackageManager(); - - mSequence++; - - boolean changed = false; - - List services - = am.getRunningServices(MAX_SERVICES); - final int NS = services != null ? services.size() : 0; - for (int i=0; i procs = mProcesses.get(si.uid); - if (procs == null) { - procs = new HashMap(); - mProcesses.put(si.uid, procs); - } - ProcessItem proc = procs.get(si.process); - if (proc == null) { - changed = true; - proc = new ProcessItem(context, si.uid, si.process); - procs.put(si.process, proc); - } - - if (proc.mCurSeq != mSequence) { - int pid = si.restarting == 0 ? si.pid : 0; - if (pid != proc.mPid) { - changed = true; - if (proc.mPid != pid) { - if (proc.mPid != 0) { - mActiveProcesses.remove(proc.mPid); - } - if (pid != 0) { - mActiveProcesses.put(pid, proc); - } - proc.mPid = pid; - } - } - proc.mDependentProcesses.clear(); - proc.mCurSeq = mSequence; - } - changed |= proc.updateService(context, si); - } - - // Now update the map of other processes that are running (but - // don't have services actively running inside them). - List processes - = am.getRunningAppProcesses(); - final int NP = processes != null ? processes.size() : 0; - for (int i=0; i procs = mProcesses.valueAt(i); - Iterator pit = procs.values().iterator(); - while (pit.hasNext()) { - ProcessItem pi = pit.next(); - if (pi.mCurSeq == mSequence) { - pi.ensureLabel(pm); - if (pi.mPid == 0) { - // Sanity: a non-process can't be dependent on - // anything. - pi.mDependentProcesses.clear(); - } - } else { - changed = true; - pit.remove(); - if (procs.size() == 0) { - mProcesses.remove(mProcesses.keyAt(i)); - } - if (pi.mPid != 0) { - mActiveProcesses.remove(pi.mPid); - } - continue; - } - Iterator sit = pi.mServices.values().iterator(); - while (sit.hasNext()) { - ServiceItem si = sit.next(); - if (si.mCurSeq != mSequence) { - changed = true; - sit.remove(); - } - } - } - } - - if (changed) { - // First determine an order for the services. - ArrayList sortedProcesses = new ArrayList(); - for (int i=0; i si.mRunningService.activeSince) { - pi.mActiveSince = si.mRunningService.activeSince; - } - } - } - sortedProcesses.add(pi); - } - } - - Collections.sort(sortedProcesses, mServiceProcessComparator); - - ArrayList newItems = new ArrayList(); - mProcessItems.clear(); - for (int i=0; i 0) { - mProcessItems.add(pi); - } - // And finally the services running in it. - boolean needDivider = false; - for (ServiceItem si : pi.mServices.values()) { - si.mNeedDivider = needDivider; - needDivider = true; - newItems.add(si); - } - } - synchronized (mLock) { - mItems = newItems; - } - } - - // Count number of interesting other (non-active) processes, and - // build a list of all processes we will retrieve memory for. - mAllProcessItems.clear(); - mAllProcessItems.addAll(mProcessItems); - int numBackgroundProcesses = 0; - int numForegroundProcesses = 0; - int numServiceProcesses = 0; - NRP = mRunningProcesses.size(); - for (int i=0; i= - ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { - numBackgroundProcesses++; - mAllProcessItems.add(proc); - } else if (proc.mRunningProcessInfo.importance <= - ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { - numForegroundProcesses++; - mAllProcessItems.add(proc); - } else { - Log.i(TAG, "Unknown non-service process: " - + proc.mProcessName + " #" + proc.mPid); - } - } else { - numServiceProcesses++; - } - } - - long backgroundProcessMemory = 0; - long foregroundProcessMemory = 0; - long serviceProcessMemory = 0; - try { - final int numProc = mAllProcessItems.size(); - int[] pids = new int[numProc]; - for (int i=0; i=0; i--) { - ProcessItem proc = mAllProcessItems.get(i); - changed |= proc.updateSize(context, mem[i], mSequence); - if (proc.mCurSeq == mSequence) { - serviceProcessMemory += proc.mSize; - } else if (proc.mRunningProcessInfo.importance >= - ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { - backgroundProcessMemory += proc.mSize; - } else if (proc.mRunningProcessInfo.importance <= - ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { - foregroundProcessMemory += proc.mSize; - } - } - } catch (RemoteException e) { - } - - synchronized (mLock) { - mNumBackgroundProcesses = numBackgroundProcesses; - mNumForegroundProcesses = numForegroundProcesses; - mNumServiceProcesses = numServiceProcesses; - mBackgroundProcessMemory = backgroundProcessMemory; - mForegroundProcessMemory = foregroundProcessMemory; - mServiceProcessMemory = serviceProcessMemory; - } - - return changed; - } - - ArrayList getCurrentItems() { - synchronized (mLock) { - return mItems; - } - } - } - - static class TimeTicker extends TextView { - public TimeTicker(Context context, AttributeSet attrs) { - super(context, attrs); - } - } - - static class ViewHolder { - ImageView separator; - ImageView icon; - TextView name; - TextView description; - TextView size; - } - - class ServiceListAdapter extends BaseAdapter { - final State mState; - final LayoutInflater mInflater; - ArrayList mItems; - - ServiceListAdapter(State state) { - mState = state; - mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); - refreshItems(); - } - - void refreshItems() { - ArrayList newItems = mState.getCurrentItems(); - if (mItems != newItems) { - mItems = newItems; - } - if (mItems == null) { - mItems = new ArrayList(); - } - } - - public boolean hasStableIds() { - return true; - } - - public int getCount() { - return mItems.size(); - } - - public Object getItem(int position) { - return mItems.get(position); - } - - public long getItemId(int position) { - return mItems.get(position).hashCode(); - } - - public boolean areAllItemsEnabled() { - return false; - } - - public boolean isEnabled(int position) { - return !mItems.get(position).mIsProcess; - } - - public View getView(int position, View convertView, ViewGroup parent) { - View v; - if (convertView == null) { - v = newView(parent); - } else { - v = convertView; - } - bindView(v, position); - return v; - } - - public View newView(ViewGroup parent) { - View v = mInflater.inflate(R.layout.running_services_item, parent, false); - ViewHolder h = new ViewHolder(); - h.separator = (ImageView)v.findViewById(R.id.separator); - h.icon = (ImageView)v.findViewById(R.id.icon); - h.name = (TextView)v.findViewById(R.id.name); - h.description = (TextView)v.findViewById(R.id.description); - h.size = (TextView)v.findViewById(R.id.size); - v.setTag(h); - return v; - } - - public void bindView(View view, int position) { - synchronized (mState.mLock) { - ViewHolder vh = (ViewHolder) view.getTag(); - if (position >= mItems.size()) { - // List must have changed since we last reported its - // size... ignore here, we will be doing a data changed - // to refresh the entire list. - return; - } - BaseItem item = mItems.get(position); - vh.name.setText(item.mDisplayLabel); - vh.separator.setVisibility(item.mNeedDivider - ? View.VISIBLE : View.INVISIBLE); - ActiveItem ai = new ActiveItem(); - ai.mRootView = view; - ai.mItem = item; - ai.mHolder = vh; - ai.mFirstRunTime = item.mActiveSince; - vh.description.setText(item.mDescription); - if (item.mIsProcess) { - view.setBackgroundColor(mProcessBgColor); - vh.icon.setImageDrawable(null); - vh.icon.setVisibility(View.GONE); - vh.description.setText(item.mDescription); - item.mCurSizeStr = null; - } else { - view.setBackgroundDrawable(null); - vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager())); - vh.icon.setVisibility(View.VISIBLE); - vh.description.setText(item.mDescription); - ai.mFirstRunTime = item.mActiveSince; - } - ai.updateTime(RunningServices.this); - mActiveItems.put(view, ai); - } - } - } - - public static class LinearColorBar extends LinearLayout { - private float mRedRatio; - private float mYellowRatio; - private float mGreenRatio; - - final Rect mRect = new Rect(); - final Paint mPaint = new Paint(); - - public LinearColorBar(Context context, AttributeSet attrs) { - super(context, attrs); - setWillNotDraw(false); - mPaint.setStyle(Paint.Style.FILL); - } - - public void setRatios(float red, float yellow, float green) { - mRedRatio = red; - mYellowRatio = yellow; - mGreenRatio = green; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - int width = getWidth(); - mRect.top = 0; - mRect.bottom = getHeight(); - - int left = 0; - - int right = left + (int)(width*mRedRatio); - if (left < right) { - mRect.left = left; - mRect.right = right; - mPaint.setColor(0xffff8080); - canvas.drawRect(mRect, mPaint); - width -= (right-left); - left = right; - } - - right = left + (int)(width*mYellowRatio); - if (left < right) { - mRect.left = left; - mRect.right = right; - mPaint.setColor(0xffffff00); - canvas.drawRect(mRect, mPaint); - width -= (right-left); - left = right; - } - - right = left + width; - if (left < right) { - mRect.left = left; - mRect.right = right; - mPaint.setColor(0xff80ff80); - canvas.drawRect(mRect, mPaint); - } - } - } - - HandlerThread mBackgroundThread; - final class BackgroundHandler extends Handler { - public BackgroundHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE_CONTENTS: - Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI); - cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0; - mHandler.sendMessage(cmd); - removeMessages(MSG_UPDATE_CONTENTS); - msg = obtainMessage(MSG_UPDATE_CONTENTS); - sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY); - break; - } - } - }; - BackgroundHandler mBackgroundHandler; - - final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE_TIMES: - Iterator it = mActiveItems.values().iterator(); - while (it.hasNext()) { - ActiveItem ai = it.next(); - if (ai.mRootView.getWindowToken() == null) { - // Clean out any dead views, just in case. - it.remove(); - continue; - } - ai.updateTime(RunningServices.this); - } - removeMessages(MSG_UPDATE_TIMES); - msg = obtainMessage(MSG_UPDATE_TIMES); - sendMessageDelayed(msg, TIME_UPDATE_DELAY); - break; - case MSG_REFRESH_UI: - refreshUi(msg.arg1 != 0); - break; - } - } - }; - - private boolean matchText(byte[] buffer, int index, String text) { - int N = text.length(); - if ((index+N) >= buffer.length) { - return false; - } - for (int i=0; i= '0' && buffer[index] <= '9') { - int start = index; - index++; - while (index < buffer.length && buffer[index] >= '0' - && buffer[index] <= '9') { - index++; - } - String str = new String(buffer, 0, start, index-start); - return ((long)Integer.parseInt(str)) * 1024; - } - index++; - } - return 0; - } - - private long readAvailMem() { - try { - long memFree = 0; - long memCached = 0; - FileInputStream is = new FileInputStream("/proc/meminfo"); - int len = is.read(mBuffer); - is.close(); - final int BUFLEN = mBuffer.length; - for (int i=0; i appList =new ArrayList (); - List procList = getRunningAppProcessesList(); - if ((procList == null) || (procList.size() == 0)) { - return appList; - } - // Retrieve running processes from ActivityManager - for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) { - if ((appProcInfo != null) && (appProcInfo.pkgList != null)){ - int size = appProcInfo.pkgList.length; - for (int i = 0; i < size; i++) { - ApplicationInfo appInfo = null; - try { - appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i], - PackageManager.GET_UNINSTALLED_PACKAGES); - } catch (NameNotFoundException e) { - Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]); - continue; - } - if(appInfo != null) { - appList.add(appInfo); - } - } - } - } - return appList; } else { return installedAppList; } @@ -728,31 +702,6 @@ public class ManageApplications extends TabActivity implements } } return retList; - } else if (filterOption == FILTER_APPS_RUNNING) { - List procList = getRunningAppProcessesList(); - if ((procList == null) || (procList.size() == 0)) { - return retList; - } - // Retrieve running processes from ActivityManager - HashMap runningMap = - new HashMap(); - for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) { - if ((appProcInfo != null) && (appProcInfo.pkgList != null)){ - int size = appProcInfo.pkgList.length; - for (int i = 0; i < size; i++) { - runningMap.put(appProcInfo.pkgList[i], appProcInfo); - } - } - } - // Query list to find running processes in current list - for (ApplicationInfo appInfo : pAppList) { - if (runningMap.get(appInfo.packageName) != null) { - if (matchFilter(filter, filterMap, appInfo.packageName)) { - retList.add(appInfo); - } - } - } - return retList; } else { for (ApplicationInfo appInfo : pAppList) { if (matchFilter(filter, filterMap, appInfo.packageName)) { @@ -763,11 +712,6 @@ public class ManageApplications extends TabActivity implements } } - private List getRunningAppProcessesList() { - ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); - return am.getRunningAppProcesses(); - } - // Some initialization code used when kicking off the size computation private void initAppList(List appList, int filterOption) { setProgressBarIndeterminateVisibility(true); @@ -1261,14 +1205,7 @@ public class ManageApplications extends TabActivity implements private boolean shouldBeInList(int filterOption, ApplicationInfo info) { // Match filter here - if (filterOption == FILTER_APPS_RUNNING) { - List runningList = getInstalledApps(FILTER_APPS_RUNNING); - for (ApplicationInfo running : runningList) { - if (running.packageName.equalsIgnoreCase(info.packageName)) { - return true; - } - } - } else if (filterOption == FILTER_APPS_THIRD_PARTY) { + if (filterOption == FILTER_APPS_THIRD_PARTY) { if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { return true; } else if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { @@ -1611,11 +1548,44 @@ public class ManageApplications extends TabActivity implements } } + static final int VIEW_NOTHING = 0; + static final int VIEW_LIST = 1; + static final int VIEW_RUNNING = 2; + + private void selectView(int which) { + if (mCurView == which) { + return; + } + + mCurView = which; + + if (which == VIEW_LIST) { + if (mResumedRunning) { + mRunningProcessesView.doPause(); + mResumedRunning = false; + } + mRunningProcessesView.setVisibility(View.GONE); + mListView.setVisibility(View.VISIBLE); + } else if (which == VIEW_RUNNING) { + if (!mCreatedRunning) { + mRunningProcessesView.doCreate(null, mNonConfigInstance); + mCreatedRunning = true; + } + if (mActivityResumed && !mResumedRunning) { + mRunningProcessesView.doResume(); + mResumedRunning = true; + } + mRunningProcessesView.setVisibility(View.VISIBLE); + mListView.setVisibility(View.GONE); + } + } + static final String TAB_DOWNLOADED = "Downloaded"; static final String TAB_RUNNING = "Running"; static final String TAB_ALL = "All"; static final String TAB_SDCARD = "OnSdCard"; private View mRootView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -1627,18 +1597,31 @@ public class ManageApplications extends TabActivity implements Intent intent = getIntent(); String action = intent.getAction(); String defaultTabTag = TAB_DOWNLOADED; + if (intent.getComponent().getClassName().equals( + "com.android.settings.RunningServices")) { + defaultTabTag = TAB_RUNNING; + } if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) { mSortOrder = SORT_ORDER_SIZE; mFilterApps = FILTER_APPS_ALL; defaultTabTag = TAB_ALL; mSizesFirst = true; } + + if (savedInstanceState != null) { + mSortOrder = savedInstanceState.getInt("sortOrder", mSortOrder); + mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps); + String tmp = savedInstanceState.getString("defaultTabTag"); + if (tmp != null) defaultTabTag = tmp; + mSizesFirst = savedInstanceState.getBoolean("sizesFirst", mSizesFirst); + } + + mNonConfigInstance = getLastNonConfigurationInstance(); + mPm = getPackageManager(); // initialize some window features requestWindowFeature(Window.FEATURE_RIGHT_ICON); - requestWindowFeature(Window.FEATURE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - showLoadingMsg(); mDefaultAppIcon = Resources.getSystem().getDrawable( com.android.internal.R.drawable.sym_def_app_icon); mInvalidSizeStr = getText(R.string.invalid_size_value); @@ -1658,6 +1641,8 @@ public class ManageApplications extends TabActivity implements lv.setOnItemClickListener(this); lv.setTextFilterEnabled(true); mListView = lv; + mRunningProcessesView = (RunningProcessesView)mRootView.findViewById( + R.id.running_processes); if (DEBUG_TIME) { Log.i(TAG, "Total time in Activity.create:: " + (SystemClock.elapsedRealtime() - sCreate)+ " ms"); @@ -1677,10 +1662,6 @@ public class ManageApplications extends TabActivity implements .setIndicator(getString(R.string.filter_apps_third_party), getResources().getDrawable(R.drawable.ic_tab_download)) .setContent(this)); - tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING) - .setIndicator(getString(R.string.filter_apps_running), - getResources().getDrawable(R.drawable.ic_tab_running)) - .setContent(this)); tabHost.addTab(tabHost.newTabSpec(TAB_ALL) .setIndicator(getString(R.string.filter_apps_all), getResources().getDrawable(R.drawable.ic_tab_all)) @@ -1689,8 +1670,95 @@ public class ManageApplications extends TabActivity implements .setIndicator(getString(R.string.filter_apps_onsdcard), getResources().getDrawable(R.drawable.ic_tab_sdcard)) .setContent(this)); + tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING) + .setIndicator(getString(R.string.filter_apps_running), + getResources().getDrawable(R.drawable.ic_tab_running)) + .setContent(this)); tabHost.setCurrentTabByTag(defaultTabTag); tabHost.setOnTabChangedListener(this); + + selectView(TAB_RUNNING.equals(defaultTabTag) ? VIEW_RUNNING : VIEW_LIST); + } + + @Override + public void onStart() { + super.onStart(); + // Register receiver + mReceiver.registerReceiver(); + sendMessageToHandler(INIT_PKG_INFO); + } + + @Override + protected void onResume() { + super.onResume(); + mActivityResumed = true; + if (mCurView == VIEW_RUNNING) { + mRunningProcessesView.doResume(); + mResumedRunning = true; + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("sortOrder", mSortOrder); + outState.putInt("filterApps", mFilterApps); + outState.putString("defautTabTag", getTabHost().getCurrentTabTag()); + outState.putBoolean("sizesFirst", mSizesFirst); + } + + @Override + public Object onRetainNonConfigurationInstance() { + return mRunningProcessesView.doRetainNonConfigurationInstance(); + } + + @Override + protected void onPause() { + super.onPause(); + mActivityResumed = false; + if (mResumedRunning) { + mRunningProcessesView.doPause(); + mResumedRunning = false; + } + } + + @Override + public void onStop() { + super.onStop(); + // Stop the background threads + if (mResourceThread != null) { + mResourceThread.setAbort(); + } + if (mSizeComputor != null) { + mSizeComputor.setAbort(); + } + // clear all messages related to application list + clearMessagesInHandler(); + // register receiver here + unregisterReceiver(mReceiver); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, + Intent data) { + if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { + // Refresh package attributes + try { + ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName, + PackageManager.GET_UNINSTALLED_PACKAGES); + } catch (NameNotFoundException e) { + Bundle rData = new Bundle(); + rData.putString(ATTR_PKG_NAME, mCurrentPkgName); + sendMessageToHandler(REMOVE_PKG, rData); + mCurrentPkgName = null; + } + } + } + + // Avoid the restart and pause when orientation changes + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); } @Override @@ -1700,34 +1768,6 @@ public class ManageApplications extends TabActivity implements super.onDestroy(); } - @Override - public Dialog onCreateDialog(int id, Bundle args) { - if (id == DLG_LOADING) { - ProgressDialog dlg = new ProgressDialog(this); - dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER); - dlg.setMessage(getText(R.string.loading)); - dlg.setIndeterminate(true); - dlg.setOnCancelListener(this); - return dlg; - } - return null; - } - - private void showLoadingMsg() { - if (DEBUG_TIME) { - mLoadTimeStart = SystemClock.elapsedRealtime(); - } - showDialog(DLG_LOADING); - if(localLOGV) Log.i(TAG, "Displaying Loading message"); - } - - private void dismissLoadingMsg() { - if(localLOGV) Log.i(TAG, "Dismissing Loading message"); - dismissDialog(DLG_LOADING); - if (DEBUG_TIME) Log.i(TAG, "Displayed loading message for "+ - (SystemClock.elapsedRealtime() - mLoadTimeStart) + " ms"); - } - class AppInfoCache { final static boolean FILE_CACHE = true; private static final String mFileCacheName="ManageAppsInfo.txt"; @@ -1923,36 +1963,6 @@ public class ManageApplications extends TabActivity implements } } - @Override - public void onStart() { - super.onStart(); - // Register receiver - mReceiver.registerReceiver(); - sendMessageToHandler(INIT_PKG_INFO); - } - - @Override - public void onStop() { - super.onStop(); - // Stop the background threads - if (mResourceThread != null) { - mResourceThread.setAbort(); - } - if (mSizeComputor != null) { - mSizeComputor.setAbort(); - } - // clear all messages related to application list - clearMessagesInHandler(); - // register receiver here - unregisterReceiver(mReceiver); - } - - // Avoid the restart and pause when orientation changes - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - } - /* * comparator class used to sort AppInfo objects based on size */ @@ -1994,9 +2004,8 @@ public class ManageApplications extends TabActivity implements // utility method used to start sub activity private void startApplicationDetailsActivity() { // Create intent to start new activity - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setClass(this, InstalledAppDetails.class); - intent.putExtra(APP_PKG_NAME, mCurrentPkgName); + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", mCurrentPkgName, null)); // start new activity to display extended information startActivityForResult(intent, INSTALLED_APP_DETAILS); } @@ -2049,33 +2058,19 @@ public class ManageApplications extends TabActivity implements int newOption; if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) { newOption = FILTER_APPS_THIRD_PARTY; - } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) { - newOption = FILTER_APPS_RUNNING; } else if (TAB_ALL.equalsIgnoreCase(tabId)) { newOption = FILTER_APPS_ALL; } else if (TAB_SDCARD.equalsIgnoreCase(tabId)) { newOption = FILTER_APPS_SDCARD; + } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) { + selectView(VIEW_RUNNING); + return; } else { // Invalid option. Do nothing return; } + + selectView(VIEW_LIST); sendMessageToHandler(REORDER_LIST, newOption); } - - @Override - protected void onActivityResult(int requestCode, int resultCode, - Intent data) { - if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { - // Refresh package attributes - try { - ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName, - PackageManager.GET_UNINSTALLED_PACKAGES); - } catch (NameNotFoundException e) { - Bundle rData = new Bundle(); - rData.putString(ATTR_PKG_NAME, mCurrentPkgName); - sendMessageToHandler(REMOVE_PKG, rData); - mCurrentPkgName = null; - } - } - } } diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java new file mode 100644 index 00000000000..9fbae6dc7f5 --- /dev/null +++ b/src/com/android/settings/applications/RunningProcessesView.java @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications; + +import com.android.settings.R; + +import android.app.ActivityManager; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.text.format.DateUtils; +import android.text.format.Formatter; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +public class RunningProcessesView extends FrameLayout + implements AdapterView.OnItemClickListener { + + /** Maximum number of services to retrieve */ + static final int MAX_SERVICES = 100; + + static final int MSG_UPDATE_TIMES = 1; + static final int MSG_UPDATE_CONTENTS = 2; + static final int MSG_REFRESH_UI = 3; + + static final long TIME_UPDATE_DELAY = 1000; + static final long CONTENTS_UPDATE_DELAY = 2000; + + // Memory pages are 4K. + static final long PAGE_SIZE = 4*1024; + + long SECONDARY_SERVER_MEM; + + final HashMap mActiveItems = new HashMap(); + + ActivityManager mAm; + + RunningState mState; + + StringBuilder mBuilder = new StringBuilder(128); + + RunningState.BaseItem mCurSelected; + + ListView mListView; + LinearColorBar mColorBar; + TextView mBackgroundProcessText; + TextView mForegroundProcessText; + + int mLastNumBackgroundProcesses = -1; + int mLastNumForegroundProcesses = -1; + int mLastNumServiceProcesses = -1; + long mLastBackgroundProcessMemory = -1; + long mLastForegroundProcessMemory = -1; + long mLastServiceProcessMemory = -1; + long mLastAvailMemory = -1; + + Dialog mCurDialog; + + byte[] mBuffer = new byte[1024]; + + public static class ActiveItem { + View mRootView; + RunningState.BaseItem mItem; + ActivityManager.RunningServiceInfo mService; + ViewHolder mHolder; + long mFirstRunTime; + + void updateTime(Context context, StringBuilder builder) { + TextView uptimeView = null; + + if (mItem instanceof RunningState.ServiceItem) { + // If we are displaying a service, then the service + // uptime goes at the top. + uptimeView = mHolder.size; + + } else { + String size = mItem.mSizeStr != null ? mItem.mSizeStr : ""; + if (!size.equals(mItem.mCurSizeStr)) { + mItem.mCurSizeStr = size; + mHolder.size.setText(size); + } + + if (mItem instanceof RunningState.MergedItem) { + // This item represents both services and proceses, + // so show the service uptime below. + uptimeView = mHolder.uptime; + } + } + + if (uptimeView != null) { + if (mItem.mActiveSince >= 0) { + uptimeView.setText(DateUtils.formatElapsedTime(builder, + (SystemClock.uptimeMillis()-mFirstRunTime)/1000)); + } else { + uptimeView.setText(context.getResources().getText( + R.string.service_restarting)); + } + } + } + } + + public static class ViewHolder { + public View rootView; + public ImageView icon; + public TextView name; + public TextView description; + public TextView size; + public TextView uptime; + + public ViewHolder(View v) { + rootView = v; + icon = (ImageView)v.findViewById(R.id.icon); + name = (TextView)v.findViewById(R.id.name); + description = (TextView)v.findViewById(R.id.description); + size = (TextView)v.findViewById(R.id.size); + uptime = (TextView)v.findViewById(R.id.uptime); + v.setTag(this); + } + + public ActiveItem bind(RunningState state, RunningState.BaseItem item, + StringBuilder builder) { + synchronized (state.mLock) { + name.setText(item.mDisplayLabel); + ActiveItem ai = new ActiveItem(); + ai.mRootView = rootView; + ai.mItem = item; + ai.mHolder = this; + ai.mFirstRunTime = item.mActiveSince; + description.setText(item.mDescription); + item.mCurSizeStr = null; + icon.setImageDrawable(item.mPackageInfo.loadIcon( + rootView.getContext().getPackageManager())); + icon.setVisibility(View.VISIBLE); + ai.updateTime(rootView.getContext(), builder); + return ai; + } + } + } + + static class TimeTicker extends TextView { + public TimeTicker(Context context, AttributeSet attrs) { + super(context, attrs); + } + } + + class ServiceListAdapter extends BaseAdapter { + final RunningState mState; + final LayoutInflater mInflater; + ArrayList mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList newItems = mState.getCurrentMergedItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList(); + } + } + + public boolean hasStableIds() { + return true; + } + + public int getCount() { + return mItems.size(); + } + + public Object getItem(int position) { + return mItems.get(position); + } + + public long getItemId(int position) { + return mItems.get(position).hashCode(); + } + + public boolean areAllItemsEnabled() { + return false; + } + + public boolean isEnabled(int position) { + return !mItems.get(position).mIsProcess; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View v; + if (convertView == null) { + v = newView(parent); + } else { + v = convertView; + } + bindView(v, position); + return v; + } + + public View newView(ViewGroup parent) { + View v = mInflater.inflate(R.layout.running_processes_item, parent, false); + new ViewHolder(v); + return v; + } + + public void bindView(View view, int position) { + synchronized (mState.mLock) { + if (position >= mItems.size()) { + // List must have changed since we last reported its + // size... ignore here, we will be doing a data changed + // to refresh the entire list. + return; + } + ViewHolder vh = (ViewHolder) view.getTag(); + RunningState.MergedItem item = mItems.get(position); + ActiveItem ai = vh.bind(mState, item, mBuilder); + mActiveItems.put(view, ai); + } + } + } + + public static class LinearColorBar extends LinearLayout { + private float mRedRatio; + private float mYellowRatio; + private float mGreenRatio; + + final Rect mRect = new Rect(); + final Paint mPaint = new Paint(); + + public LinearColorBar(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + mPaint.setStyle(Paint.Style.FILL); + } + + public void setRatios(float red, float yellow, float green) { + mRedRatio = red; + mYellowRatio = yellow; + mGreenRatio = green; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int width = getWidth(); + mRect.top = 0; + mRect.bottom = getHeight(); + + int left = 0; + + int right = left + (int)(width*mRedRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffff8080); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + (int)(width*mYellowRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffffff00); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + width; + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xff80ff80); + canvas.drawRect(mRect, mPaint); + } + } + } + + HandlerThread mBackgroundThread; + final class BackgroundHandler extends Handler { + public BackgroundHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_CONTENTS: + Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI); + cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0; + mHandler.sendMessage(cmd); + removeMessages(MSG_UPDATE_CONTENTS); + msg = obtainMessage(MSG_UPDATE_CONTENTS); + sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY); + break; + } + } + }; + + BackgroundHandler mBackgroundHandler; + + final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_TIMES: + Iterator it = mActiveItems.values().iterator(); + while (it.hasNext()) { + ActiveItem ai = it.next(); + if (ai.mRootView.getWindowToken() == null) { + // Clean out any dead views, just in case. + it.remove(); + continue; + } + ai.updateTime(getContext(), mBuilder); + } + removeMessages(MSG_UPDATE_TIMES); + msg = obtainMessage(MSG_UPDATE_TIMES); + sendMessageDelayed(msg, TIME_UPDATE_DELAY); + break; + case MSG_REFRESH_UI: + refreshUi(msg.arg1 != 0); + break; + } + } + }; + + private boolean matchText(byte[] buffer, int index, String text) { + int N = text.length(); + if ((index+N) >= buffer.length) { + return false; + } + for (int i=0; i= '0' && buffer[index] <= '9') { + int start = index; + index++; + while (index < buffer.length && buffer[index] >= '0' + && buffer[index] <= '9') { + index++; + } + String str = new String(buffer, 0, start, index-start); + return ((long)Integer.parseInt(str)) * 1024; + } + index++; + } + return 0; + } + + private long readAvailMem() { + try { + long memFree = 0; + long memCached = 0; + FileInputStream is = new FileInputStream("/proc/meminfo"); + int len = is.read(mBuffer); + is.close(); + final int BUFLEN = mBuffer.length; + for (int i=0; i parent, View v, int position, long id) { + ListView l = (ListView)parent; + RunningState.MergedItem mi = (RunningState.MergedItem)l.getAdapter().getItem(position); + mCurSelected = mi; + Intent intent = new Intent(); + intent.putExtra(RunningServiceDetails.KEY_UID, mi.mProcess.mUid); + intent.putExtra(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName); + intent.setClass(getContext(), RunningServiceDetails.class); + getContext().startActivity(intent); + } + + public void onMovedToScrapHeap(View view) { + mActiveItems.remove(view); + } + + public RunningProcessesView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) { + mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE); + mState = (RunningState)nonConfigurationInstace; + if (mState == null) { + mState = new RunningState(); + } + LayoutInflater inflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.running_processes_view, this); + mListView = (ListView)findViewById(android.R.id.list); + View emptyView = findViewById(com.android.internal.R.id.empty); + if (emptyView != null) { + mListView.setEmptyView(emptyView); + } + mListView.setOnItemClickListener(this); + mListView.setAdapter(new ServiceListAdapter(mState)); + mColorBar = (LinearColorBar)findViewById(R.id.color_bar); + mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText); + mForegroundProcessText = (TextView)findViewById(R.id.foregroundText); + + // Magic! Implementation detail! Don't count on this! + SECONDARY_SERVER_MEM = + Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + } + + public void doPause() { + mHandler.removeMessages(MSG_UPDATE_TIMES); + if (mBackgroundThread != null) { + mBackgroundThread.quit(); + mBackgroundThread = null; + mBackgroundHandler = null; + } + } + + public void doResume() { + refreshUi(mState.update(getContext(), mAm)); + mBackgroundThread = new HandlerThread("RunningServices"); + mBackgroundThread.start(); + mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper()); + mHandler.removeMessages(MSG_UPDATE_TIMES); + Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES); + mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY); + mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS); + msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS); + mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY); + } + + public Object doRetainNonConfigurationInstance() { + return mState; + } +} diff --git a/src/com/android/settings/applications/RunningProcessesViewOld.java b/src/com/android/settings/applications/RunningProcessesViewOld.java new file mode 100644 index 00000000000..dc7302c7a1f --- /dev/null +++ b/src/com/android/settings/applications/RunningProcessesViewOld.java @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.applications; + +import com.android.settings.R; + +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.PendingIntent; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentSender; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.text.format.DateUtils; +import android.text.format.Formatter; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +public class RunningProcessesViewOld extends FrameLayout + implements AdapterView.OnItemClickListener { + + static final String TAG = "RunningServices"; + + /** Maximum number of services to retrieve */ + static final int MAX_SERVICES = 100; + + static final int MSG_UPDATE_TIMES = 1; + static final int MSG_UPDATE_CONTENTS = 2; + static final int MSG_REFRESH_UI = 3; + + static final long TIME_UPDATE_DELAY = 1000; + static final long CONTENTS_UPDATE_DELAY = 2000; + + // Memory pages are 4K. + static final long PAGE_SIZE = 4*1024; + + long SECONDARY_SERVER_MEM; + + final HashMap mActiveItems = new HashMap(); + + ActivityManager mAm; + + RunningState mState; + + StringBuilder mBuilder = new StringBuilder(128); + + RunningState.BaseItem mCurSelected; + + int mProcessBgColor; + + ListView mListView; + LinearColorBar mColorBar; + TextView mBackgroundProcessText; + TextView mForegroundProcessText; + + int mLastNumBackgroundProcesses = -1; + int mLastNumForegroundProcesses = -1; + int mLastNumServiceProcesses = -1; + long mLastBackgroundProcessMemory = -1; + long mLastForegroundProcessMemory = -1; + long mLastServiceProcessMemory = -1; + long mLastAvailMemory = -1; + + Dialog mCurDialog; + + byte[] mBuffer = new byte[1024]; + + class ActiveItem { + View mRootView; + RunningState.BaseItem mItem; + ActivityManager.RunningServiceInfo mService; + ViewHolder mHolder; + long mFirstRunTime; + + void updateTime(Context context) { + if (mItem.mIsProcess) { + String size = mItem.mSizeStr != null ? mItem.mSizeStr : ""; + if (!size.equals(mItem.mCurSizeStr)) { + mItem.mCurSizeStr = size; + mHolder.size.setText(size); + } + } else { + if (mItem.mActiveSince >= 0) { + mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder, + (SystemClock.uptimeMillis()-mFirstRunTime)/1000)); + } else { + mHolder.size.setText(context.getResources().getText( + R.string.service_restarting)); + } + } + } + } + + static class ViewHolder { + ImageView separator; + ImageView icon; + TextView name; + TextView description; + TextView size; + } + + static class TimeTicker extends TextView { + public TimeTicker(Context context, AttributeSet attrs) { + super(context, attrs); + } + } + + class ServiceListAdapter extends BaseAdapter { + final RunningState mState; + final LayoutInflater mInflater; + ArrayList mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList newItems = mState.getCurrentItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList(); + } + } + + public boolean hasStableIds() { + return true; + } + + public int getCount() { + return mItems.size(); + } + + public Object getItem(int position) { + return mItems.get(position); + } + + public long getItemId(int position) { + return mItems.get(position).hashCode(); + } + + public boolean areAllItemsEnabled() { + return false; + } + + public boolean isEnabled(int position) { + return !mItems.get(position).mIsProcess; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View v; + if (convertView == null) { + v = newView(parent); + } else { + v = convertView; + } + bindView(v, position); + return v; + } + + public View newView(ViewGroup parent) { + View v = mInflater.inflate(R.layout.running_services_item, parent, false); + ViewHolder h = new ViewHolder(); + h.separator = (ImageView)v.findViewById(R.id.separator); + h.icon = (ImageView)v.findViewById(R.id.icon); + h.name = (TextView)v.findViewById(R.id.name); + h.description = (TextView)v.findViewById(R.id.description); + h.size = (TextView)v.findViewById(R.id.size); + v.setTag(h); + return v; + } + + public void bindView(View view, int position) { + synchronized (mState.mLock) { + ViewHolder vh = (ViewHolder) view.getTag(); + if (position >= mItems.size()) { + // List must have changed since we last reported its + // size... ignore here, we will be doing a data changed + // to refresh the entire list. + return; + } + RunningState.BaseItem item = mItems.get(position); + vh.name.setText(item.mDisplayLabel); + vh.separator.setVisibility(item.mNeedDivider + ? View.VISIBLE : View.INVISIBLE); + ActiveItem ai = new ActiveItem(); + ai.mRootView = view; + ai.mItem = item; + ai.mHolder = vh; + ai.mFirstRunTime = item.mActiveSince; + vh.description.setText(item.mDescription); + if (item.mIsProcess) { + view.setBackgroundColor(mProcessBgColor); + vh.icon.setImageDrawable(null); + vh.icon.setVisibility(View.GONE); + vh.description.setText(item.mDescription); + item.mCurSizeStr = null; + } else { + view.setBackgroundDrawable(null); + vh.icon.setImageDrawable(item.mPackageInfo.loadIcon( + getContext().getPackageManager())); + vh.icon.setVisibility(View.VISIBLE); + vh.description.setText(item.mDescription); + ai.mFirstRunTime = item.mActiveSince; + } + ai.updateTime(getContext()); + mActiveItems.put(view, ai); + } + } + } + + public static class LinearColorBar extends LinearLayout { + private float mRedRatio; + private float mYellowRatio; + private float mGreenRatio; + + final Rect mRect = new Rect(); + final Paint mPaint = new Paint(); + + public LinearColorBar(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + mPaint.setStyle(Paint.Style.FILL); + } + + public void setRatios(float red, float yellow, float green) { + mRedRatio = red; + mYellowRatio = yellow; + mGreenRatio = green; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int width = getWidth(); + mRect.top = 0; + mRect.bottom = getHeight(); + + int left = 0; + + int right = left + (int)(width*mRedRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffff8080); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + (int)(width*mYellowRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffffff00); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + width; + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xff80ff80); + canvas.drawRect(mRect, mPaint); + } + } + } + + HandlerThread mBackgroundThread; + final class BackgroundHandler extends Handler { + public BackgroundHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_CONTENTS: + Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI); + cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0; + mHandler.sendMessage(cmd); + removeMessages(MSG_UPDATE_CONTENTS); + msg = obtainMessage(MSG_UPDATE_CONTENTS); + sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY); + break; + } + } + }; + + BackgroundHandler mBackgroundHandler; + + final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_TIMES: + Iterator it = mActiveItems.values().iterator(); + while (it.hasNext()) { + ActiveItem ai = it.next(); + if (ai.mRootView.getWindowToken() == null) { + // Clean out any dead views, just in case. + it.remove(); + continue; + } + ai.updateTime(getContext()); + } + removeMessages(MSG_UPDATE_TIMES); + msg = obtainMessage(MSG_UPDATE_TIMES); + sendMessageDelayed(msg, TIME_UPDATE_DELAY); + break; + case MSG_REFRESH_UI: + refreshUi(msg.arg1 != 0); + break; + } + } + }; + + private boolean matchText(byte[] buffer, int index, String text) { + int N = text.length(); + if ((index+N) >= buffer.length) { + return false; + } + for (int i=0; i= '0' && buffer[index] <= '9') { + int start = index; + index++; + while (index < buffer.length && buffer[index] >= '0' + && buffer[index] <= '9') { + index++; + } + String str = new String(buffer, 0, start, index-start); + return ((long)Integer.parseInt(str)) * 1024; + } + index++; + } + return 0; + } + + private long readAvailMem() { + try { + long memFree = 0; + long memCached = 0; + FileInputStream is = new FileInputStream("/proc/meminfo"); + int len = is.read(mBuffer); + is.close(); + final int BUFLEN = mBuffer.length; + for (int i=0; i parent, View v, int position, long id) { + ListView l = (ListView)parent; + RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position); + mCurSelected = bi; + } + + public void onMovedToScrapHeap(View view) { + mActiveItems.remove(view); + } + + public RunningProcessesViewOld(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) { + mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE); + mState = (RunningState)nonConfigurationInstace; + if (mState == null) { + mState = new RunningState(); + } + mProcessBgColor = 0xff505050; + LayoutInflater inflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.running_processes_view, this); + mListView = (ListView)findViewById(android.R.id.list); + View emptyView = findViewById(com.android.internal.R.id.empty); + if (emptyView != null) { + mListView.setEmptyView(emptyView); + } + mListView.setOnItemClickListener(this); + mListView.setDivider(null); + mListView.setAdapter(new ServiceListAdapter(mState)); + mColorBar = (LinearColorBar)findViewById(R.id.color_bar); + mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText); + mForegroundProcessText = (TextView)findViewById(R.id.foregroundText); + + // Magic! Implementation detail! Don't count on this! + SECONDARY_SERVER_MEM = + Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + } + + public void doPause() { + mHandler.removeMessages(MSG_UPDATE_TIMES); + if (mBackgroundThread != null) { + mBackgroundThread.quit(); + mBackgroundThread = null; + mBackgroundHandler = null; + } + } + + public void doResume() { + refreshUi(mState.update(getContext(), mAm)); + mBackgroundThread = new HandlerThread("RunningServices"); + mBackgroundThread.start(); + mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper()); + mHandler.removeMessages(MSG_UPDATE_TIMES); + Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES); + mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY); + mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS); + msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS); + mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY); + } + + public Object doRetainNonConfigurationInstance() { + return mState; + } +} diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java new file mode 100644 index 00000000000..f33625e7822 --- /dev/null +++ b/src/com/android/settings/applications/RunningServiceDetails.java @@ -0,0 +1,390 @@ +package com.android.settings.applications; + +import com.android.settings.R; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.PendingIntent; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +public class RunningServiceDetails extends Activity { + static final String TAG = "RunningServicesDetails"; + + static final String KEY_UID = "uid"; + static final String KEY_PROCESS = "process"; + + static final int MSG_UPDATE_TIMES = 1; + static final int MSG_UPDATE_CONTENTS = 2; + static final int MSG_REFRESH_UI = 3; + + ActivityManager mAm; + LayoutInflater mInflater; + + RunningState mState; + + int mUid; + String mProcessName; + + RunningState.MergedItem mMergedItem; + + ViewGroup mAllDetails; + ViewGroup mSnippet; + RunningProcessesView.ActiveItem mSnippetActiveItem; + RunningProcessesView.ViewHolder mSnippetViewHolder; + + TextView mServicesHeader; + TextView mProcessesHeader; + final ArrayList mActiveDetails = new ArrayList(); + + class ActiveDetail implements View.OnClickListener { + View mRootView; + RunningProcessesView.ActiveItem mActiveItem; + RunningProcessesView.ViewHolder mViewHolder; + PendingIntent mManageIntent; + + public void onClick(View v) { + if (mManageIntent != null) { + try { + startIntentSender(mManageIntent.getIntentSender(), null, + Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, + Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0); + } catch (IntentSender.SendIntentException e) { + Log.w(TAG, e); + } catch (IllegalArgumentException e) { + Log.w(TAG, e); + } catch (ActivityNotFoundException e) { + Log.w(TAG, e); + } + } else { + RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem; + stopService(new Intent().setComponent(si.mRunningService.service)); + if (mMergedItem == null || mMergedItem.mServices.size() <= 1) { + // If there was only one service, we are finishing it, + // so no reason for the UI to stick around. + finish(); + } else { + if (mBackgroundHandler != null) { + mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); + } + } + } + } + } + + StringBuilder mBuilder = new StringBuilder(128); + + HandlerThread mBackgroundThread; + final class BackgroundHandler extends Handler { + public BackgroundHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_CONTENTS: + Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI); + cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0; + mHandler.sendMessage(cmd); + removeMessages(MSG_UPDATE_CONTENTS); + msg = obtainMessage(MSG_UPDATE_CONTENTS); + sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY); + break; + } + } + }; + + BackgroundHandler mBackgroundHandler; + + final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_TIMES: + if (mSnippetActiveItem != null) { + mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder); + } + for (int i=0; i newItems = mState.getCurrentMergedItems(); + if (newItems != null) { + for (int i=0; i=0; i--) { + mAllDetails.removeView(mActiveDetails.get(i).mRootView); + } + mActiveDetails.clear(); + + if (mServicesHeader != null) { + mAllDetails.removeView(mServicesHeader); + mServicesHeader = null; + } + + if (mProcessesHeader != null) { + mAllDetails.removeView(mProcessesHeader); + mProcessesHeader = null; + } + + if (mMergedItem != null) { + for (int i=0; i mActiveItems = new HashMap(); + + ActivityManager mAm; + + RunningState mState; + + StringBuilder mBuilder = new StringBuilder(128); + + RunningState.BaseItem mCurSelected; + + int mProcessBgColor; + + LinearColorBar mColorBar; + TextView mBackgroundProcessText; + TextView mForegroundProcessText; + + int mLastNumBackgroundProcesses = -1; + int mLastNumForegroundProcesses = -1; + int mLastNumServiceProcesses = -1; + long mLastBackgroundProcessMemory = -1; + long mLastForegroundProcessMemory = -1; + long mLastServiceProcessMemory = -1; + long mLastAvailMemory = -1; + + Dialog mCurDialog; + + byte[] mBuffer = new byte[1024]; + + class ActiveItem { + View mRootView; + RunningState.BaseItem mItem; + ActivityManager.RunningServiceInfo mService; + ViewHolder mHolder; + long mFirstRunTime; + + void updateTime(Context context) { + if (mItem.mIsProcess) { + String size = mItem.mSizeStr != null ? mItem.mSizeStr : ""; + if (!size.equals(mItem.mCurSizeStr)) { + mItem.mCurSizeStr = size; + mHolder.size.setText(size); + } + } else { + if (mItem.mActiveSince >= 0) { + mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder, + (SystemClock.uptimeMillis()-mFirstRunTime)/1000)); + } else { + mHolder.size.setText(context.getResources().getText( + R.string.service_restarting)); + } + } + } + } + + static class ViewHolder { + ImageView separator; + ImageView icon; + TextView name; + TextView description; + TextView size; + } + + static class TimeTicker extends TextView { + public TimeTicker(Context context, AttributeSet attrs) { + super(context, attrs); + } + } + + class ServiceListAdapter extends BaseAdapter { + final RunningState mState; + final LayoutInflater mInflater; + ArrayList mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList newItems = mState.getCurrentItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList(); + } + } + + public boolean hasStableIds() { + return true; + } + + public int getCount() { + return mItems.size(); + } + + public Object getItem(int position) { + return mItems.get(position); + } + + public long getItemId(int position) { + return mItems.get(position).hashCode(); + } + + public boolean areAllItemsEnabled() { + return false; + } + + public boolean isEnabled(int position) { + return !mItems.get(position).mIsProcess; + } + + public View getView(int position, View convertView, ViewGroup parent) { + View v; + if (convertView == null) { + v = newView(parent); + } else { + v = convertView; + } + bindView(v, position); + return v; + } + + public View newView(ViewGroup parent) { + View v = mInflater.inflate(R.layout.running_services_item, parent, false); + ViewHolder h = new ViewHolder(); + h.separator = (ImageView)v.findViewById(R.id.separator); + h.icon = (ImageView)v.findViewById(R.id.icon); + h.name = (TextView)v.findViewById(R.id.name); + h.description = (TextView)v.findViewById(R.id.description); + h.size = (TextView)v.findViewById(R.id.size); + v.setTag(h); + return v; + } + + public void bindView(View view, int position) { + synchronized (mState.mLock) { + ViewHolder vh = (ViewHolder) view.getTag(); + if (position >= mItems.size()) { + // List must have changed since we last reported its + // size... ignore here, we will be doing a data changed + // to refresh the entire list. + return; + } + RunningState.BaseItem item = mItems.get(position); + vh.name.setText(item.mDisplayLabel); + vh.separator.setVisibility(item.mNeedDivider + ? View.VISIBLE : View.INVISIBLE); + ActiveItem ai = new ActiveItem(); + ai.mRootView = view; + ai.mItem = item; + ai.mHolder = vh; + ai.mFirstRunTime = item.mActiveSince; + vh.description.setText(item.mDescription); + if (item.mIsProcess) { + view.setBackgroundColor(mProcessBgColor); + vh.icon.setImageDrawable(null); + vh.icon.setVisibility(View.GONE); + vh.description.setText(item.mDescription); + item.mCurSizeStr = null; + } else { + view.setBackgroundDrawable(null); + vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager())); + vh.icon.setVisibility(View.VISIBLE); + vh.description.setText(item.mDescription); + ai.mFirstRunTime = item.mActiveSince; + } + ai.updateTime(RunningServices.this); + mActiveItems.put(view, ai); + } + } + } + + public static class LinearColorBar extends LinearLayout { + private float mRedRatio; + private float mYellowRatio; + private float mGreenRatio; + + final Rect mRect = new Rect(); + final Paint mPaint = new Paint(); + + public LinearColorBar(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + mPaint.setStyle(Paint.Style.FILL); + } + + public void setRatios(float red, float yellow, float green) { + mRedRatio = red; + mYellowRatio = yellow; + mGreenRatio = green; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int width = getWidth(); + mRect.top = 0; + mRect.bottom = getHeight(); + + int left = 0; + + int right = left + (int)(width*mRedRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffff8080); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + (int)(width*mYellowRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffffff00); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + width; + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xff80ff80); + canvas.drawRect(mRect, mPaint); + } + } + } + + HandlerThread mBackgroundThread; + final class BackgroundHandler extends Handler { + public BackgroundHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_CONTENTS: + Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI); + cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0; + mHandler.sendMessage(cmd); + removeMessages(MSG_UPDATE_CONTENTS); + msg = obtainMessage(MSG_UPDATE_CONTENTS); + sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY); + break; + } + } + }; + + BackgroundHandler mBackgroundHandler; + + final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_TIMES: + Iterator it = mActiveItems.values().iterator(); + while (it.hasNext()) { + ActiveItem ai = it.next(); + if (ai.mRootView.getWindowToken() == null) { + // Clean out any dead views, just in case. + it.remove(); + continue; + } + ai.updateTime(RunningServices.this); + } + removeMessages(MSG_UPDATE_TIMES); + msg = obtainMessage(MSG_UPDATE_TIMES); + sendMessageDelayed(msg, TIME_UPDATE_DELAY); + break; + case MSG_REFRESH_UI: + refreshUi(msg.arg1 != 0); + break; + } + } + }; + + private boolean matchText(byte[] buffer, int index, String text) { + int N = text.length(); + if ((index+N) >= buffer.length) { + return false; + } + for (int i=0; i= '0' && buffer[index] <= '9') { + int start = index; + index++; + while (index < buffer.length && buffer[index] >= '0' + && buffer[index] <= '9') { + index++; + } + String str = new String(buffer, 0, start, index-start); + return ((long)Integer.parseInt(str)) * 1024; + } + index++; + } + return 0; + } + + private long readAvailMem() { + try { + long memFree = 0; + long memCached = 0; + FileInputStream is = new FileInputStream("/proc/meminfo"); + int len = is.read(mBuffer); + is.close(); + final int BUFLEN = mBuffer.length; + for (int i=0; i> mProcesses + = new SparseArray>(); + final SparseArray mActiveProcesses + = new SparseArray(); + final ServiceProcessComparator mServiceProcessComparator + = new ServiceProcessComparator(); + + // Temporary for finding process dependencies. + final SparseArray mRunningProcesses + = new SparseArray(); + + final ArrayList mProcessItems = new ArrayList(); + final ArrayList mAllProcessItems = new ArrayList(); + + int mSequence = 0; + + // ----- following protected by mLock ----- + + // Lock for protecting the state that will be shared between the + // background update thread and the UI thread. + final Object mLock = new Object(); + + ArrayList mItems = new ArrayList(); + ArrayList mMergedItems = new ArrayList(); + + int mNumBackgroundProcesses; + long mBackgroundProcessMemory; + int mNumForegroundProcesses; + long mForegroundProcessMemory; + int mNumServiceProcesses; + long mServiceProcessMemory; + + static class BaseItem { + final boolean mIsProcess; + + PackageItemInfo mPackageInfo; + CharSequence mDisplayLabel; + String mLabel; + String mDescription; + + int mCurSeq; + + long mActiveSince; + long mSize; + String mSizeStr; + String mCurSizeStr; + boolean mNeedDivider; + + public BaseItem(boolean isProcess) { + mIsProcess = isProcess; + } + } + + static class ServiceItem extends BaseItem { + ActivityManager.RunningServiceInfo mRunningService; + ServiceInfo mServiceInfo; + boolean mShownAsStarted; + + MergedItem mMergedItem; + + public ServiceItem() { + super(false); + } + } + + static class ProcessItem extends BaseItem { + final HashMap mServices + = new HashMap(); + final SparseArray mDependentProcesses + = new SparseArray(); + + final int mUid; + final String mProcessName; + int mPid; + + ProcessItem mClient; + int mLastNumDependentProcesses; + + int mRunningSeq; + ActivityManager.RunningAppProcessInfo mRunningProcessInfo; + + // Purely for sorting. + boolean mIsSystem; + boolean mIsStarted; + long mActiveSince; + + public ProcessItem(Context context, int uid, String processName) { + super(true); + mDescription = context.getResources().getString( + R.string.service_process_name, processName); + mUid = uid; + mProcessName = processName; + } + + void ensureLabel(PackageManager pm) { + if (mLabel != null) { + return; + } + + try { + ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0); + if (ai.uid == mUid) { + mDisplayLabel = ai.loadLabel(pm); + mLabel = mDisplayLabel.toString(); + mPackageInfo = ai; + return; + } + } catch (PackageManager.NameNotFoundException e) { + } + + // If we couldn't get information about the overall + // process, try to find something about the uid. + String[] pkgs = pm.getPackagesForUid(mUid); + + // If there is one package with this uid, that is what we want. + if (pkgs.length == 1) { + try { + ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); + mDisplayLabel = ai.loadLabel(pm); + mLabel = mDisplayLabel.toString(); + mPackageInfo = ai; + return; + } catch (PackageManager.NameNotFoundException e) { + } + } + + // If there are multiple, see if one gives us the official name + // for this uid. + for (String name : pkgs) { + try { + PackageInfo pi = pm.getPackageInfo(name, 0); + if (pi.sharedUserLabel != 0) { + CharSequence nm = pm.getText(name, + pi.sharedUserLabel, pi.applicationInfo); + if (nm != null) { + mDisplayLabel = nm; + mLabel = nm.toString(); + mPackageInfo = pi.applicationInfo; + return; + } + } + } catch (PackageManager.NameNotFoundException e) { + } + } + + // If still don't have anything to display, just use the + // service info. + if (mServices.size() > 0) { + mPackageInfo = mServices.values().iterator().next() + .mServiceInfo.applicationInfo; + mDisplayLabel = mPackageInfo.loadLabel(pm); + mLabel = mDisplayLabel.toString(); + return; + } + + // Finally... whatever, just pick the first package's name. + try { + ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); + mDisplayLabel = ai.loadLabel(pm); + mLabel = mDisplayLabel.toString(); + mPackageInfo = ai; + return; + } catch (PackageManager.NameNotFoundException e) { + } + } + + boolean updateService(Context context, + ActivityManager.RunningServiceInfo service) { + final PackageManager pm = context.getPackageManager(); + + boolean changed = false; + ServiceItem si = mServices.get(service.service); + if (si == null) { + changed = true; + si = new ServiceItem(); + si.mRunningService = service; + try { + si.mServiceInfo = pm.getServiceInfo(service.service, 0); + } catch (PackageManager.NameNotFoundException e) { + } + si.mDisplayLabel = makeLabel(pm, + si.mRunningService.service.getClassName(), si.mServiceInfo); + mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null; + si.mPackageInfo = si.mServiceInfo.applicationInfo; + mServices.put(service.service, si); + } + si.mCurSeq = mCurSeq; + si.mRunningService = service; + long activeSince = service.restarting == 0 ? service.activeSince : -1; + if (si.mActiveSince != activeSince) { + si.mActiveSince = activeSince; + changed = true; + } + if (service.clientPackage != null && service.clientLabel != 0) { + if (si.mShownAsStarted) { + si.mShownAsStarted = false; + changed = true; + } + try { + Resources clientr = pm.getResourcesForApplication(service.clientPackage); + String label = clientr.getString(service.clientLabel); + si.mDescription = context.getResources().getString( + R.string.service_client_name, label); + } catch (PackageManager.NameNotFoundException e) { + si.mDescription = null; + } + } else { + if (!si.mShownAsStarted) { + si.mShownAsStarted = true; + changed = true; + } + si.mDescription = context.getResources().getString( + R.string.service_started_by_app); + } + + return changed; + } + + boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) { + mSize = ((long)mem.getTotalPss()) * 1024; + if (mCurSeq == curSeq) { + String sizeStr = Formatter.formatShortFileSize( + context, mSize); + if (!sizeStr.equals(mSizeStr)){ + mSizeStr = sizeStr; + // We update this on the second tick where we update just + // the text in the current items, so no need to say we + // changed here. + return false; + } + } + return false; + } + + boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) { + final int NP = mDependentProcesses.size(); + boolean changed = false; + for (int i=0; i dest, + ArrayList destProc) { + final int NP = mDependentProcesses.size(); + for (int i=0; i 0) { + destProc.add(proc); + } + } + } + } + + static class MergedItem extends BaseItem { + ProcessItem mProcess; + final ArrayList mOtherProcesses = new ArrayList(); + final ArrayList mServices = new ArrayList(); + + MergedItem() { + super(false); + } + + boolean update(Context context) { + mPackageInfo = mProcess.mPackageInfo; + mDisplayLabel = mProcess.mDisplayLabel; + mLabel = mProcess.mLabel; + + int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(); + int numServices = mServices.size(); + int resid = R.string.running_processes_item_description_s_s; + if (numProcesses != 1) { + resid = numServices != 1 + ? R.string.running_processes_item_description_p_p + : R.string.running_processes_item_description_p_s; + } else if (numServices != 1) { + resid = R.string.running_processes_item_description_s_p; + } + mDescription = context.getResources().getString(resid, numProcesses, numServices); + + mActiveSince = -1; + for (int i=0; i= 0 && mActiveSince < si.mActiveSince) { + mActiveSince = si.mActiveSince; + } + } + + return false; + } + + boolean updateSize(Context context) { + mSize = mProcess.mSize; + for (int i=0; i { + public int compare(ProcessItem object1, ProcessItem object2) { + if (object1.mIsStarted != object2.mIsStarted) { + // Non-started processes go last. + return object1.mIsStarted ? -1 : 1; + } + if (object1.mIsSystem != object2.mIsSystem) { + // System processes go below non-system. + return object1.mIsSystem ? 1 : -1; + } + if (object1.mActiveSince != object2.mActiveSince) { + // Remaining ones are sorted with the longest running + // services last. + return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1; + } + return 0; + } + } + + static CharSequence makeLabel(PackageManager pm, + String className, PackageItemInfo item) { + if (item != null && (item.labelRes != 0 + || item.nonLocalizedLabel != null)) { + CharSequence label = item.loadLabel(pm); + if (label != null) { + return label; + } + } + + String label = className; + int tail = label.lastIndexOf('.'); + if (tail >= 0) { + label = label.substring(tail+1, label.length()); + } + return label; + } + + boolean update(Context context, ActivityManager am) { + final PackageManager pm = context.getPackageManager(); + + mSequence++; + + boolean changed = false; + + List services + = am.getRunningServices(RunningServices.MAX_SERVICES); + final int NS = services != null ? services.size() : 0; + for (int i=0; i procs = mProcesses.get(si.uid); + if (procs == null) { + procs = new HashMap(); + mProcesses.put(si.uid, procs); + } + ProcessItem proc = procs.get(si.process); + if (proc == null) { + changed = true; + proc = new ProcessItem(context, si.uid, si.process); + procs.put(si.process, proc); + } + + if (proc.mCurSeq != mSequence) { + int pid = si.restarting == 0 ? si.pid : 0; + if (pid != proc.mPid) { + changed = true; + if (proc.mPid != pid) { + if (proc.mPid != 0) { + mActiveProcesses.remove(proc.mPid); + } + if (pid != 0) { + mActiveProcesses.put(pid, proc); + } + proc.mPid = pid; + } + } + proc.mDependentProcesses.clear(); + proc.mCurSeq = mSequence; + } + changed |= proc.updateService(context, si); + } + + // Now update the map of other processes that are running (but + // don't have services actively running inside them). + List processes + = am.getRunningAppProcesses(); + final int NP = processes != null ? processes.size() : 0; + for (int i=0; i procs = mProcesses.valueAt(i); + Iterator pit = procs.values().iterator(); + while (pit.hasNext()) { + ProcessItem pi = pit.next(); + if (pi.mCurSeq == mSequence) { + pi.ensureLabel(pm); + if (pi.mPid == 0) { + // Sanity: a non-process can't be dependent on + // anything. + pi.mDependentProcesses.clear(); + } + } else { + changed = true; + pit.remove(); + if (procs.size() == 0) { + mProcesses.remove(mProcesses.keyAt(i)); + } + if (pi.mPid != 0) { + mActiveProcesses.remove(pi.mPid); + } + continue; + } + Iterator sit = pi.mServices.values().iterator(); + while (sit.hasNext()) { + ServiceItem si = sit.next(); + if (si.mCurSeq != mSequence) { + changed = true; + sit.remove(); + } + } + } + } + + if (changed) { + // First determine an order for the services. + ArrayList sortedProcesses = new ArrayList(); + for (int i=0; i si.mRunningService.activeSince) { + pi.mActiveSince = si.mRunningService.activeSince; + } + } + } + sortedProcesses.add(pi); + } + } + + Collections.sort(sortedProcesses, mServiceProcessComparator); + + ArrayList newItems = new ArrayList(); + ArrayList newMergedItems = new ArrayList(); + mProcessItems.clear(); + for (int i=0; i 0) { + mProcessItems.add(pi); + } + + // Now add the services running in it. + MergedItem mergedItem = null; + boolean haveAllMerged = false; + boolean needDivider = false; + for (ServiceItem si : pi.mServices.values()) { + si.mNeedDivider = needDivider; + needDivider = true; + newItems.add(si); + if (si.mMergedItem != null) { + if (mergedItem != null && mergedItem != si.mMergedItem) { + haveAllMerged = false; + } + mergedItem = si.mMergedItem; + } else { + haveAllMerged = false; + } + } + + if (!haveAllMerged || mergedItem == null + || mergedItem.mServices.size() != pi.mServices.size()) { + // Whoops, we need to build a new MergedItem! + mergedItem = new MergedItem(); + for (ServiceItem si : pi.mServices.values()) { + mergedItem.mServices.add(si); + si.mMergedItem = mergedItem; + } + mergedItem.mProcess = pi; + mergedItem.mOtherProcesses.clear(); + for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) { + mergedItem.mOtherProcesses.add(mProcessItems.get(mpi)); + } + } + + mergedItem.update(context); + newMergedItems.add(mergedItem); + } + synchronized (mLock) { + mItems = newItems; + mMergedItems = newMergedItems; + } + } + + // Count number of interesting other (non-active) processes, and + // build a list of all processes we will retrieve memory for. + mAllProcessItems.clear(); + mAllProcessItems.addAll(mProcessItems); + int numBackgroundProcesses = 0; + int numForegroundProcesses = 0; + int numServiceProcesses = 0; + NRP = mRunningProcesses.size(); + for (int i=0; i= + ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { + numBackgroundProcesses++; + mAllProcessItems.add(proc); + } else if (proc.mRunningProcessInfo.importance <= + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { + numForegroundProcesses++; + mAllProcessItems.add(proc); + } else { + Log.i(RunningServices.TAG, "Unknown non-service process: " + + proc.mProcessName + " #" + proc.mPid); + } + } else { + numServiceProcesses++; + } + } + + long backgroundProcessMemory = 0; + long foregroundProcessMemory = 0; + long serviceProcessMemory = 0; + try { + final int numProc = mAllProcessItems.size(); + int[] pids = new int[numProc]; + for (int i=0; i=0; i--) { + ProcessItem proc = mAllProcessItems.get(i); + changed |= proc.updateSize(context, mem[i], mSequence); + if (proc.mCurSeq == mSequence) { + serviceProcessMemory += proc.mSize; + } else if (proc.mRunningProcessInfo.importance >= + ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { + backgroundProcessMemory += proc.mSize; + } else if (proc.mRunningProcessInfo.importance <= + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { + foregroundProcessMemory += proc.mSize; + } + } + } catch (RemoteException e) { + } + + for (int i=0; i getCurrentItems() { + synchronized (mLock) { + return mItems; + } + } + + ArrayList getCurrentMergedItems() { + synchronized (mLock) { + return mMergedItems; + } + } +} diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java index 4db968a2ea7..cc112f81bfd 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java @@ -40,9 +40,9 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; -import com.android.settings.InstalledAppDetails; -import com.android.settings.ManageApplications; import com.android.settings.R; +import com.android.settings.applications.InstalledAppDetails; +import com.android.settings.applications.ManageApplications; public class PowerUsageDetail extends Activity implements Button.OnClickListener { @@ -248,9 +248,9 @@ public class PowerUsageDetail extends Activity implements Button.OnClickListener startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS)); break; case ACTION_APP_DETAILS: - Intent intent = new Intent(Intent.ACTION_VIEW); + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.fromParts("package", mPackages[0], null)); intent.setClass(this, InstalledAppDetails.class); - intent.putExtra(ManageApplications.APP_PKG_NAME, mPackages[0]); startActivity(intent); break; case ACTION_SECURITY_SETTINGS: