diff options
Diffstat (limited to 'src')
19 files changed, 3246 insertions, 1612 deletions
diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java index 62856d1..e097854 100644 --- a/src/com/android/settings/ApnEditor.java +++ b/src/com/android/settings/ApnEditor.java @@ -458,7 +458,11 @@ public class ApnEditor extends PreferenceActivity public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { Preference pref = findPreference(key); if (pref != null) { - pref.setSummary(checkNull(sharedPreferences.getString(key, ""))); + if (pref.equals(mPassword)){ + pref.setSummary(starify(sharedPreferences.getString(key, ""))); + } else { + pref.setSummary(checkNull(sharedPreferences.getString(key, ""))); + } } } } diff --git a/src/com/android/settings/ApplicationSettings.java b/src/com/android/settings/ApplicationSettings.java index c743f1c..a919ae8 100644 --- a/src/com/android/settings/ApplicationSettings.java +++ b/src/com/android/settings/ApplicationSettings.java @@ -124,7 +124,7 @@ public class ApplicationSettings extends PreferenceActivity implements } public void onClick(DialogInterface dialog, int which) { - if (dialog == mWarnInstallApps && which == DialogInterface.BUTTON1) { + if (dialog == mWarnInstallApps && which == DialogInterface.BUTTON_POSITIVE) { setNonMarketAppsAllowed(true); mToggleAppInstallation.setChecked(true); } diff --git a/src/com/android/settings/RunningServices.java b/src/com/android/settings/RunningServices.java deleted file mode 100644 index e67adf0..0000000 --- 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<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>(); - - 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<ComponentName, ServiceItem> mServices - = new HashMap<ComponentName, ServiceItem>(); - final SparseArray<ProcessItem> mDependentProcesses - = new SparseArray<ProcessItem>(); - - 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<NP; i++) { - ProcessItem proc = mDependentProcesses.valueAt(i); - if (proc.mClient != this) { - changed = true; - proc.mClient = this; - } - proc.mCurSeq = curSeq; - proc.ensureLabel(pm); - changed |= proc.buildDependencyChain(context, pm, curSeq); - } - - if (mLastNumDependentProcesses != mDependentProcesses.size()) { - changed = true; - mLastNumDependentProcesses = mDependentProcesses.size(); - } - - return changed; - } - - void addDependentProcesses(ArrayList<BaseItem> dest, - ArrayList<ProcessItem> destProc) { - final int NP = mDependentProcesses.size(); - for (int i=0; i<NP; i++) { - ProcessItem proc = mDependentProcesses.valueAt(i); - proc.addDependentProcesses(dest, destProc); - dest.add(proc); - if (proc.mPid > 0) { - destProc.add(proc); - } - } - } - } - - static class ServiceProcessComparator implements Comparator<ProcessItem> { - 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<HashMap<String, ProcessItem>> mProcesses - = new SparseArray<HashMap<String, ProcessItem>>(); - final SparseArray<ProcessItem> mActiveProcesses - = new SparseArray<ProcessItem>(); - final ServiceProcessComparator mServiceProcessComparator - = new ServiceProcessComparator(); - - // Temporary for finding process dependencies. - final SparseArray<ProcessItem> mRunningProcesses - = new SparseArray<ProcessItem>(); - - final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>(); - final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>(); - - 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<BaseItem> mItems = new ArrayList<BaseItem>(); - - 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<ActivityManager.RunningServiceInfo> services - = am.getRunningServices(MAX_SERVICES); - final int NS = services != null ? services.size() : 0; - for (int i=0; i<NS; i++) { - ActivityManager.RunningServiceInfo si = services.get(i); - // We are not interested in services that have not been started - // and don't have a known client, because - // there is nothing the user can do about them. - if (!si.started && si.clientLabel == 0) { - continue; - } - // We likewise don't care about services running in a - // persistent process like the system or phone. - if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS) - != 0) { - continue; - } - - HashMap<String, ProcessItem> procs = mProcesses.get(si.uid); - if (procs == null) { - procs = new HashMap<String, ProcessItem>(); - 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<ActivityManager.RunningAppProcessInfo> processes - = am.getRunningAppProcesses(); - final int NP = processes != null ? processes.size() : 0; - for (int i=0; i<NP; i++) { - ActivityManager.RunningAppProcessInfo pi = processes.get(i); - ProcessItem proc = mActiveProcesses.get(pi.pid); - if (proc == null) { - // This process is not one that is a direct container - // of a service, so look for it in the secondary - // running list. - proc = mRunningProcesses.get(pi.pid); - if (proc == null) { - proc = new ProcessItem(context, pi.uid, pi.processName); - proc.mPid = pi.pid; - mRunningProcesses.put(pi.pid, proc); - } - proc.mDependentProcesses.clear(); - } - proc.mRunningSeq = mSequence; - proc.mRunningProcessInfo = pi; - } - - // Build the chains from client processes to the process they are - // dependent on; also remove any old running processes. - int NRP = mRunningProcesses.size(); - for (int i=0; i<NRP; i++) { - ProcessItem proc = mRunningProcesses.valueAt(i); - if (proc.mRunningSeq == mSequence) { - int clientPid = proc.mRunningProcessInfo.importanceReasonPid; - if (clientPid != 0) { - ProcessItem client = mActiveProcesses.get(clientPid); - if (client == null) { - client = mRunningProcesses.get(clientPid); - } - if (client != null) { - client.mDependentProcesses.put(proc.mPid, proc); - } - } else { - // In this pass the process doesn't have a client. - // Clear to make sure if it later gets the same one - // that we will detect the change. - proc.mClient = null; - } - } else { - mRunningProcesses.remove(mRunningProcesses.keyAt(i)); - } - } - - // Follow the tree from all primary service processes to all - // processes they are dependent on, marking these processes as - // still being active and determining if anything has changed. - final int NAP = mActiveProcesses.size(); - for (int i=0; i<NAP; i++) { - ProcessItem proc = mActiveProcesses.valueAt(i); - if (proc.mCurSeq == mSequence) { - changed |= proc.buildDependencyChain(context, pm, mSequence); - } - } - - // Look for services and their primary processes that no longer exist... - for (int i=0; i<mProcesses.size(); i++) { - HashMap<String, ProcessItem> procs = mProcesses.valueAt(i); - Iterator<ProcessItem> 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<ServiceItem> 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<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>(); - for (int i=0; i<mProcesses.size(); i++) { - for (ProcessItem pi : mProcesses.valueAt(i).values()) { - pi.mIsSystem = false; - pi.mIsStarted = true; - pi.mActiveSince = Long.MAX_VALUE; - for (ServiceItem si : pi.mServices.values()) { - if (si.mServiceInfo != null - && (si.mServiceInfo.applicationInfo.flags - & ApplicationInfo.FLAG_SYSTEM) != 0) { - pi.mIsSystem = true; - } - if (si.mRunningService != null - && si.mRunningService.clientLabel != 0) { - pi.mIsStarted = false; - if (pi.mActiveSince > si.mRunningService.activeSince) { - pi.mActiveSince = si.mRunningService.activeSince; - } - } - } - sortedProcesses.add(pi); - } - } - - Collections.sort(sortedProcesses, mServiceProcessComparator); - - ArrayList<BaseItem> newItems = new ArrayList<BaseItem>(); - mProcessItems.clear(); - for (int i=0; i<sortedProcesses.size(); i++) { - ProcessItem pi = sortedProcesses.get(i); - pi.mNeedDivider = false; - // First add processes we are dependent on. - pi.addDependentProcesses(newItems, mProcessItems); - // And add the process itself. - newItems.add(pi); - if (pi.mPid > 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<NRP; i++) { - ProcessItem proc = mRunningProcesses.valueAt(i); - if (proc.mCurSeq != mSequence) { - // We didn't hit this process as a dependency on one - // of our active ones, so add it up if needed. - if (proc.mRunningProcessInfo.importance >= - 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<numProc; i++) { - pids[i] = mAllProcessItems.get(i).mPid; - } - Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault() - .getProcessMemoryInfo(pids); - for (int i=pids.length-1; 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<BaseItem> 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<BaseItem> mItems; - - ServiceListAdapter(State state) { - mState = state; - mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); - refreshItems(); - } - - void refreshItems() { - ArrayList<BaseItem> newItems = mState.getCurrentItems(); - if (mItems != newItems) { - mItems = newItems; - } - if (mItems == null) { - mItems = new ArrayList<BaseItem>(); - } - } - - 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<ActiveItem> 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<N; i++) { - if (buffer[index+i] != text.charAt(i)) { - return false; - } - } - return true; - } - - private long extractMemValue(byte[] buffer, int index) { - while (index < buffer.length && buffer[index] != '\n') { - if (buffer[index] >= '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<len && (memFree == 0 || memCached == 0); i++) { - if (matchText(mBuffer, i, "MemFree")) { - i += 7; - memFree = extractMemValue(mBuffer, i); - } else if (matchText(mBuffer, i, "Cached")) { - i += 6; - memCached = extractMemValue(mBuffer, i); - } - while (i < BUFLEN && mBuffer[i] != '\n') { - i++; - } - } - return memFree + memCached; - } catch (java.io.FileNotFoundException e) { - } catch (java.io.IOException e) { - } - return 0; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); - mState = (State)getLastNonConfigurationInstance(); - if (mState == null) { - mState = new State(); - } - mProcessBgColor = 0xff505050; - setContentView(R.layout.running_services); - getListView().setDivider(null); - getListView().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; - } - - void refreshUi(boolean dataChanged) { - if (dataChanged) { - ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter()); - adapter.refreshItems(); - adapter.notifyDataSetChanged(); - } - - // This is the amount of available memory until we start killing - // background services. - long availMem = readAvailMem() - SECONDARY_SERVER_MEM; - if (availMem < 0) { - availMem = 0; - } - - synchronized (mState.mLock) { - if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses - || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory - || mLastAvailMemory != availMem) { - mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses; - mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory; - mLastAvailMemory = availMem; - String availStr = availMem != 0 - ? Formatter.formatShortFileSize(this, availMem) : "0"; - String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory); - mBackgroundProcessText.setText(getResources().getString( - R.string.service_background_processes, - mLastNumBackgroundProcesses, availStr, sizeStr)); - } - if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses - || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) { - mLastNumForegroundProcesses = mState.mNumForegroundProcesses; - mLastForegroundProcessMemory = mState.mForegroundProcessMemory; - String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory); - mForegroundProcessText.setText(getResources().getString( - R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr)); - } - mLastNumServiceProcesses = mState.mNumServiceProcesses; - mLastServiceProcessMemory = mState.mServiceProcessMemory; - - float totalMem = availMem + mLastBackgroundProcessMemory - + mLastForegroundProcessMemory + mLastServiceProcessMemory; - mColorBar.setRatios(mLastForegroundProcessMemory/totalMem, - mLastServiceProcessMemory/totalMem, - (availMem+mLastBackgroundProcessMemory)/totalMem); - } - } - - @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - BaseItem bi = (BaseItem)l.getAdapter().getItem(position); - if (!bi.mIsProcess) { - ServiceItem si = (ServiceItem)bi; - if (si.mRunningService.clientLabel != 0) { - mCurSelected = null; - PendingIntent pi = mAm.getRunningServiceControlPanel( - si.mRunningService.service); - if (pi != null) { - try { - this.startIntentSender(pi.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 { - mCurSelected = bi; - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.confirm_stop_service); - String msg = getResources().getString( - R.string.confirm_stop_service_msg, - si.mPackageInfo.loadLabel(getPackageManager())); - builder.setMessage(msg); - builder.setPositiveButton(R.string.confirm_stop_stop, this); - builder.setNegativeButton(R.string.confirm_stop_cancel, null); - builder.setCancelable(true); - mCurDialog = builder.show(); - } - } else { - mCurSelected = null; - } - } - - public void onClick(DialogInterface dialog, int which) { - if (mCurSelected != null) { - stopService(new Intent().setComponent( - ((ServiceItem)mCurSelected).mRunningService.service)); - if (mBackgroundHandler != null) { - mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); - } - } - } - - @Override - protected void onPause() { - super.onPause(); - mHandler.removeMessages(MSG_UPDATE_TIMES); - if (mBackgroundThread != null) { - mBackgroundThread.quit(); - mBackgroundThread = null; - mBackgroundHandler = null; - } - } - - @Override - protected void onResume() { - super.onResume(); - refreshUi(mState.update(this, 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); - } - - @Override - public Object onRetainNonConfigurationInstance() { - return mState; - } - - public void onMovedToScrapHeap(View view) { - mActiveItems.remove(view); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (mCurDialog != null) { - mCurDialog.dismiss(); - } - } -} diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java index 1348d48..05a655a 100644 --- a/src/com/android/settings/SecuritySettings.java +++ b/src/com/android/settings/SecuritySettings.java @@ -66,6 +66,10 @@ public class SecuritySettings extends PreferenceActivity { private static final String KEY_VISIBLE_PATTERN = "visiblepattern"; private static final String KEY_TACTILE_FEEDBACK_ENABLED = "unlock_tactile_feedback"; + // Encrypted File Systems constants + private static final String PROPERTY_EFS_ENABLED = "persist.security.efs.enabled"; + private static final String PROPERTY_EFS_TRANSITION = "persist.security.efs.trans"; + private CheckBoxPreference mVisiblePattern; private CheckBoxPreference mTactileFeedback; @@ -80,6 +84,9 @@ public class SecuritySettings extends PreferenceActivity { // Credential storage private CredentialStorage mCredentialStorage = new CredentialStorage(); + // Encrypted file system + private CheckBoxPreference mEncryptedFSEnabled; + private CheckBoxPreference mNetwork; private CheckBoxPreference mGps; private CheckBoxPreference mAssistedGps; @@ -209,6 +216,11 @@ public class SecuritySettings extends PreferenceActivity { root.addPreference(credentialsCat); mCredentialStorage.createPreferences(credentialsCat, CredentialStorage.TYPE_KEYSTORE); + // File System Encryption + PreferenceCategory encryptedfsCat = new PreferenceCategory(this); + encryptedfsCat.setTitle(R.string.encrypted_fs_category); + //root.addPreference(encryptedfsCat); + mCredentialStorage.createPreferences(encryptedfsCat, CredentialStorage.TYPE_ENCRYPTEDFS); return root; } @@ -302,18 +314,21 @@ public class SecuritySettings extends PreferenceActivity { private static final int MINIMUM_PASSWORD_LENGTH = 8; private static final int TYPE_KEYSTORE = 0; + private static final int TYPE_ENCRYPTEDFS = 1; // Dialog identifiers private static final int DLG_BASE = 0; private static final int DLG_UNLOCK = DLG_BASE + 1; private static final int DLG_PASSWORD = DLG_UNLOCK + 1; private static final int DLG_RESET = DLG_PASSWORD + 1; + private static final int DLG_ENABLE_EFS = DLG_RESET + 1; private KeyStore mKeyStore = KeyStore.getInstance(); private int mState; private boolean mSubmit = false; private boolean mExternal = false; + private boolean mWillEnableEncryptedFS; private int mShowingDialog = 0; // Key Store controls @@ -322,6 +337,10 @@ public class SecuritySettings extends PreferenceActivity { private Preference mPasswordButton; private Preference mResetButton; + + // Encrypted file system controls + private CheckBoxPreference mEncryptedFSEnabled; + void resume() { mState = mKeyStore.test(); updatePreferences(mState); @@ -373,6 +392,10 @@ public class SecuritySettings extends PreferenceActivity { lock(); } return true; + } else if (preference == mEncryptedFSEnabled) { + Boolean bval = (Boolean)value; + mWillEnableEncryptedFS = bval.booleanValue(); + showSwitchEncryptedFSDialog(); } return true; } @@ -391,9 +414,26 @@ public class SecuritySettings extends PreferenceActivity { } public void onClick(DialogInterface dialog, int button) { - mSubmit = (button == DialogInterface.BUTTON_POSITIVE); - if (button == DialogInterface.BUTTON_NEUTRAL) { - reset(); + if (mShowingDialog != DLG_ENABLE_EFS) { + mSubmit = (button == DialogInterface.BUTTON_POSITIVE); + if (button == DialogInterface.BUTTON_NEUTRAL) { + reset(); + } + } else { + if (button == DialogInterface.BUTTON_POSITIVE) { + Intent intent = new Intent("android.intent.action.MASTER_CLEAR"); + intent.putExtra("enableEFS", mWillEnableEncryptedFS); + sendBroadcast(intent); + updatePreferences(mState); + } else if (button == DialogInterface.BUTTON_NEGATIVE) { + // Cancel action + Toast.makeText(SecuritySettings.this, R.string.encrypted_fs_cancel_confirm, + Toast.LENGTH_SHORT).show(); + updatePreferences(mState); + } else { + // Unknown - should not happen + return; + } } } @@ -507,16 +547,25 @@ public class SecuritySettings extends PreferenceActivity { category.addPreference(mResetButton); break; + case TYPE_ENCRYPTEDFS: + mEncryptedFSEnabled = new CheckBoxPreference(SecuritySettings.this); + mEncryptedFSEnabled.setTitle(R.string.encrypted_fs_enable); + mEncryptedFSEnabled.setSummary(R.string.encrypted_fs_enable_summary); + mEncryptedFSEnabled.setOnPreferenceChangeListener(this); + // category.addPreference(mEncryptedFSEnabled); + break; } } private void updatePreferences(int state) { mAccessCheckBox.setChecked(state == KeyStore.NO_ERROR); - - mResetButton.setEnabled(state != KeyStore.UNINITIALIZED); - mAccessCheckBox.setEnabled(state != KeyStore.UNINITIALIZED); + boolean encFSEnabled = SystemProperties.getBoolean(PROPERTY_EFS_ENABLED, + false); + mResetButton.setEnabled((!encFSEnabled) && (state != KeyStore.UNINITIALIZED)); + mAccessCheckBox.setEnabled((state != KeyStore.UNINITIALIZED) && (!encFSEnabled)); // Encrypted File system preferences + mEncryptedFSEnabled.setChecked(encFSEnabled); // Show a toast message if the state is changed. if (mState == state) { @@ -586,5 +635,24 @@ public class SecuritySettings extends PreferenceActivity { .setNegativeButton(getString(android.R.string.cancel), this) .create().show(); } + + private void showSwitchEncryptedFSDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(SecuritySettings.this) + .setCancelable(false) + .setTitle(R.string.encrypted_fs_alert_dialog_title); + + mShowingDialog = DLG_ENABLE_EFS; + if (mWillEnableEncryptedFS) { + builder.setMessage(R.string.encrypted_fs_enable_dialog) + .setPositiveButton(R.string.encrypted_fs_enable_button, this) + .setNegativeButton(R.string.encrypted_fs_cancel_button, this) + .create().show(); + } else { + builder.setMessage(R.string.encrypted_fs_disable_dialog) + .setPositiveButton(R.string.encrypted_fs_disable_button, this) + .setNegativeButton(R.string.encrypted_fs_cancel_button, this) + .create().show(); + } + } } } diff --git a/src/com/android/settings/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 0ca35b8..92fe9d2 100644 --- a/src/com/android/settings/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -16,10 +16,14 @@ * under the License. */ -package com.android.settings; +package com.android.settings.applications; import com.android.internal.content.PackageHelper; import com.android.settings.R; +import com.android.settings.R.id; +import com.android.settings.R.layout; +import com.android.settings.R.string; + import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; @@ -312,7 +316,7 @@ public class InstalledAppDetails extends Activity implements View.OnClickListene // Get application's name from intent Intent intent = getIntent(); - final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME); + final String packageName = intent.getData().getSchemeSpecificPart(); if (! initAppInfo(packageName)) { return; // could not find package, finish called } diff --git a/src/com/android/settings/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index dfff8c9..52ea376 100644 --- a/src/com/android/settings/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.settings; +package com.android.settings.applications; import com.android.settings.R; @@ -41,6 +41,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.SystemClock; +import android.provider.Settings; import android.text.format.Formatter; import android.util.Log; import android.view.LayoutInflater; @@ -119,7 +120,6 @@ public class ManageApplications extends TabActivity implements private static final boolean DEBUG_TIME = false; // attributes used as keys when passing values to InstalledAppDetails activity - public static final String APP_PKG_NAME = "pkg"; public static final String APP_CHG = "chg"; // attribute name used in receiver for tagging names of added/deleted packages @@ -140,9 +140,8 @@ public class ManageApplications extends TabActivity implements private static final int MENU_OPTIONS_BASE = 0; // Filter options used for displayed list of applications public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0; - public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 1; - public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 2; - public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 3; + public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1; + public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2; public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4; public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5; @@ -220,6 +219,8 @@ public class ManageApplications extends TabActivity implements private boolean mSizesFirst = false; // ListView used to display list private ListView mListView; + // Custom view used to display running processes + private RunningProcessesView mRunningProcessesView; // State variables used to figure out menu options and also // initiate the first computation and loading of resources private boolean mJustCreated = true; @@ -227,6 +228,14 @@ public class ManageApplications extends TabActivity implements private long mLoadTimeStart; private boolean mSetListViewLater = true; + // These are for keeping track of activity and tab switch state. + private int mCurView; + private boolean mCreatedRunning; + + private boolean mResumedRunning; + private boolean mActivityResumed; + private Object mNonConfigInstance; + /* * Handler class to handle messages for various operations. * Most of the operations that effect Application related data @@ -268,10 +277,8 @@ public class ManageApplications extends TabActivity implements boolean status; long size; String formattedSize; - ApplicationInfo info; Bundle data; String pkgName = null; - AppInfo appInfo; data = msg.getData(); if(data != null) { pkgName = data.getString(ATTR_PKG_NAME); @@ -359,12 +366,6 @@ public class ManageApplications extends TabActivity implements } break; } - try { - info = mPm.getApplicationInfo(pkgName, 0); - } catch (NameNotFoundException e) { - Log.w(TAG, "Couldnt find application info for:"+pkgName); - break; - } mObserver.invokeGetSizeInfo(pkgName); break; case ADD_PKG_DONE: @@ -456,7 +457,6 @@ public class ManageApplications extends TabActivity implements // Set the adapter here. mJustCreated = false; mListView.setAdapter(mAppInfoAdapter); - dismissLoadingMsg(); } } @@ -645,32 +645,6 @@ public class ManageApplications extends TabActivity implements } } return appList; - } else if (filterOption == FILTER_APPS_RUNNING) { - List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> (); - List<ActivityManager.RunningAppProcessInfo> 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<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList(); - if ((procList == null) || (procList.size() == 0)) { - return retList; - } - // Retrieve running processes from ActivityManager - HashMap<String, ActivityManager.RunningAppProcessInfo> runningMap = - new HashMap<String, ActivityManager.RunningAppProcessInfo>(); - 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<ActivityManager.RunningAppProcessInfo> 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<ApplicationInfo> 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<ApplicationInfo> 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,43 +1670,102 @@ 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 - protected void onDestroy() { - // Persist values in cache - mCache.updateCache(); - super.onDestroy(); + public void onStart() { + super.onStart(); + // Register receiver + mReceiver.registerReceiver(); + sendMessageToHandler(INIT_PKG_INFO); } @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; + protected void onResume() { + super.onResume(); + mActivityResumed = true; + if (mCurView == VIEW_RUNNING) { + mRunningProcessesView.doResume(); + mResumedRunning = true; + } } - private void showLoadingMsg() { - if (DEBUG_TIME) { - mLoadTimeStart = SystemClock.elapsedRealtime(); + @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; + } } - 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"); + // Avoid the restart and pause when orientation changes + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + + @Override + protected void onDestroy() { + // Persist values in cache + mCache.updateCache(); + super.onDestroy(); } class AppInfoCache { @@ -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 0000000..9fbae6d --- /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<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>(); + + 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<RunningState.MergedItem> mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList<RunningState.MergedItem>(); + } + } + + 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<ActiveItem> 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<N; i++) { + if (buffer[index+i] != text.charAt(i)) { + return false; + } + } + return true; + } + + private long extractMemValue(byte[] buffer, int index) { + while (index < buffer.length && buffer[index] != '\n') { + if (buffer[index] >= '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<len && (memFree == 0 || memCached == 0); i++) { + if (matchText(mBuffer, i, "MemFree")) { + i += 7; + memFree = extractMemValue(mBuffer, i); + } else if (matchText(mBuffer, i, "Cached")) { + i += 6; + memCached = extractMemValue(mBuffer, i); + } + while (i < BUFLEN && mBuffer[i] != '\n') { + i++; + } + } + return memFree + memCached; + } catch (java.io.FileNotFoundException e) { + } catch (java.io.IOException e) { + } + return 0; + } + + + void refreshUi(boolean dataChanged) { + if (dataChanged) { + ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter()); + adapter.refreshItems(); + adapter.notifyDataSetChanged(); + } + + // This is the amount of available memory until we start killing + // background services. + long availMem = readAvailMem() - SECONDARY_SERVER_MEM; + if (availMem < 0) { + availMem = 0; + } + + synchronized (mState.mLock) { + if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses + || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory + || mLastAvailMemory != availMem) { + mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses; + mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory; + mLastAvailMemory = availMem; + String sizeStr = Formatter.formatShortFileSize(getContext(), + mLastAvailMemory + mLastBackgroundProcessMemory); + mBackgroundProcessText.setText(getResources().getString( + R.string.service_background_processes, sizeStr)); + } + if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses + || mLastForegroundProcessMemory != mState.mForegroundProcessMemory + || mLastNumServiceProcesses != mState.mNumServiceProcesses + || mLastServiceProcessMemory != mState.mServiceProcessMemory) { + mLastNumForegroundProcesses = mState.mNumForegroundProcesses; + mLastForegroundProcessMemory = mState.mForegroundProcessMemory; + mLastNumServiceProcesses = mState.mNumServiceProcesses; + mLastServiceProcessMemory = mState.mServiceProcessMemory; + String sizeStr = Formatter.formatShortFileSize(getContext(), + mLastForegroundProcessMemory + mLastServiceProcessMemory); + mForegroundProcessText.setText(getResources().getString( + R.string.service_foreground_processes, sizeStr)); + } + + float totalMem = availMem + mLastBackgroundProcessMemory + + mLastForegroundProcessMemory + mLastServiceProcessMemory; + mColorBar.setRatios(mLastForegroundProcessMemory/totalMem, + mLastServiceProcessMemory/totalMem, + (availMem+mLastBackgroundProcessMemory)/totalMem); + } + } + + public void onItemClick(AdapterView<?> 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 0000000..dc7302c --- /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<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>(); + + 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<RunningState.BaseItem> mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList<RunningState.BaseItem>(); + } + } + + 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<ActiveItem> 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<N; i++) { + if (buffer[index+i] != text.charAt(i)) { + return false; + } + } + return true; + } + + private long extractMemValue(byte[] buffer, int index) { + while (index < buffer.length && buffer[index] != '\n') { + if (buffer[index] >= '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<len && (memFree == 0 || memCached == 0); i++) { + if (matchText(mBuffer, i, "MemFree")) { + i += 7; + memFree = extractMemValue(mBuffer, i); + } else if (matchText(mBuffer, i, "Cached")) { + i += 6; + memCached = extractMemValue(mBuffer, i); + } + while (i < BUFLEN && mBuffer[i] != '\n') { + i++; + } + } + return memFree + memCached; + } catch (java.io.FileNotFoundException e) { + } catch (java.io.IOException e) { + } + return 0; + } + + + void refreshUi(boolean dataChanged) { + if (dataChanged) { + ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter()); + adapter.refreshItems(); + adapter.notifyDataSetChanged(); + } + + // This is the amount of available memory until we start killing + // background services. + long availMem = readAvailMem() - SECONDARY_SERVER_MEM; + if (availMem < 0) { + availMem = 0; + } + + synchronized (mState.mLock) { + if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses + || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory + || mLastAvailMemory != availMem) { + mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses; + mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory; + mLastAvailMemory = availMem; + String sizeStr = Formatter.formatShortFileSize(getContext(), + mLastAvailMemory + mLastBackgroundProcessMemory); + mBackgroundProcessText.setText(getResources().getString( + R.string.service_background_processes, sizeStr)); + } + if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses + || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) { + mLastNumForegroundProcesses = mState.mNumForegroundProcesses; + mLastForegroundProcessMemory = mState.mForegroundProcessMemory; + String sizeStr = Formatter.formatShortFileSize(getContext(), + mLastForegroundProcessMemory); + mForegroundProcessText.setText(getResources().getString( + R.string.service_foreground_processes, sizeStr)); + } + mLastNumServiceProcesses = mState.mNumServiceProcesses; + mLastServiceProcessMemory = mState.mServiceProcessMemory; + + float totalMem = availMem + mLastBackgroundProcessMemory + + mLastForegroundProcessMemory + mLastServiceProcessMemory; + mColorBar.setRatios(mLastForegroundProcessMemory/totalMem, + mLastServiceProcessMemory/totalMem, + (availMem+mLastBackgroundProcessMemory)/totalMem); + } + } + + public void onItemClick(AdapterView<?> 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 0000000..f33625e --- /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<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>(); + + 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<mActiveDetails.size(); i++) { + mActiveDetails.get(i).mActiveItem.updateTime( + RunningServiceDetails.this, mBuilder); + } + removeMessages(MSG_UPDATE_TIMES); + msg = obtainMessage(MSG_UPDATE_TIMES); + sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY); + break; + case MSG_REFRESH_UI: + refreshUi(msg.arg1 != 0); + break; + } + } + }; + + boolean findMergedItem() { + RunningState.MergedItem item = null; + ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems(); + if (newItems != null) { + for (int i=0; i<newItems.size(); i++) { + RunningState.MergedItem mi = newItems.get(i); + if (mi.mProcess.mUid == mUid + && mi.mProcess.mProcessName.equals(mProcessName)) { + item = mi; + break; + } + } + } + if (mMergedItem != item) { + mMergedItem = item; + return true; + } + return false; + } + + void addDetailViews() { + for (int i=mActiveDetails.size()-1; 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<mMergedItem.mServices.size(); i++) { + if (i == 0) { + mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label, + mAllDetails, false); + mServicesHeader.setText(R.string.runningservicedetails_services_title); + mAllDetails.addView(mServicesHeader); + } + RunningState.ServiceItem si = mMergedItem.mServices.get(i); + ActiveDetail detail = new ActiveDetail(); + View root = mInflater.inflate(R.layout.running_service_details_service, + mAllDetails, false); + mAllDetails.addView(root); + detail.mRootView = root; + detail.mViewHolder = new RunningProcessesView.ViewHolder(root); + detail.mActiveItem = detail.mViewHolder.bind(mState, si, mBuilder); + + if (si.mRunningService.clientLabel != 0) { + detail.mManageIntent = mAm.getRunningServiceControlPanel( + si.mRunningService.service); + } + + TextView description = (TextView)root.findViewById(R.id.comp_description); + if (si.mServiceInfo.descriptionRes != 0) { + description.setText(getPackageManager().getText( + si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes, + si.mServiceInfo.applicationInfo)); + } else { + if (detail.mManageIntent != null) { + try { + Resources clientr = getPackageManager().getResourcesForApplication( + si.mRunningService.clientPackage); + String label = clientr.getString(si.mRunningService.clientLabel); + description.setText(getString(R.string.service_manage_description, + label)); + } catch (PackageManager.NameNotFoundException e) { + } + } else { + description.setText(getText(R.string.service_stop_description)); + } + } + + View button = root.findViewById(R.id.right_button); + button.setOnClickListener(detail); + ((TextView)button).setText(getText(detail.mManageIntent != null + ? R.string.service_manage : R.string.service_stop)); + root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE); + + mActiveDetails.add(detail); + } + + boolean didProcess = false; + for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) { + RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess + : mMergedItem.mOtherProcesses.get(i); + if (pi.mPid <= 0) { + continue; + } + + if (!didProcess) { + mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label, + mAllDetails, false); + mProcessesHeader.setText(R.string.runningservicedetails_processes_title); + mAllDetails.addView(mProcessesHeader); + didProcess = true; + } + ActiveDetail detail = new ActiveDetail(); + View root = mInflater.inflate(R.layout.running_service_details_process, + mAllDetails, false); + mAllDetails.addView(root); + detail.mRootView = root; + detail.mViewHolder = new RunningProcessesView.ViewHolder(root); + detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder); + + TextView description = (TextView)root.findViewById(R.id.comp_description); + if (i < 0) { + description.setText(R.string.main_running_process_description); + } else { + int textid = 0; + CharSequence label = null; + ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo; + final ComponentName comp = rpi.importanceReasonComponent; + //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode + // + " pid=" + rpi.importanceReasonPid + " comp=" + comp); + switch (rpi.importanceReasonCode) { + case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE: + textid = R.string.process_provider_in_use_description; + List<ProviderInfo> providers = null; + if (comp != null) { + providers = getPackageManager() + .queryContentProviders(comp.getPackageName(), + rpi.uid, 0); + } + if (providers != null) { + for (int j=0; j<providers.size(); j++) { + ProviderInfo prov = providers.get(i); + if (comp.getClassName().equals(prov.name)) { + label = RunningState.makeLabel(getPackageManager(), + prov.name, prov); + break; + } + } + } + break; + case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE: + textid = R.string.process_service_in_use_description; + if (rpi.importanceReasonComponent != null) { + try { + ServiceInfo serv = getPackageManager().getServiceInfo( + rpi.importanceReasonComponent, 0); + label = RunningState.makeLabel(getPackageManager(), + serv.name, serv); + } catch (NameNotFoundException e) { + } + } + break; + } + if (textid != 0 && label != null) { + description.setText(getString(textid, label)); + } + } + + mActiveDetails.add(detail); + } + } + } + + void refreshUi(boolean dataChanged) { + if (findMergedItem()) { + dataChanged = true; + } + if (dataChanged) { + if (mMergedItem != null) { + mSnippetActiveItem = mSnippetViewHolder.bind(mState, + mMergedItem, mBuilder); + } else if (mSnippetActiveItem != null) { + // Clear whatever is currently being shown. + mSnippetActiveItem.mHolder.size.setText(""); + mSnippetActiveItem.mHolder.uptime.setText(""); + mSnippetActiveItem.mHolder.description.setText(R.string.no_services); + } else { + // No merged item, never had one. Nothing to do. + finish(); + return; + } + addDetailViews(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mUid = getIntent().getIntExtra(KEY_UID, 0); + mProcessName = getIntent().getStringExtra(KEY_PROCESS); + + mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); + mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + mState = (RunningState)getLastNonConfigurationInstance(); + if (mState == null) { + mState = new RunningState(); + } + + setContentView(R.layout.running_service_details); + + mAllDetails = (ViewGroup)findViewById(R.id.all_details); + mSnippet = (ViewGroup)findViewById(R.id.snippet); + mSnippet.setBackgroundResource(com.android.internal.R.drawable.title_bar_medium); + mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom()); + mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet); + } + + @Override + protected void onPause() { + super.onPause(); + mHandler.removeMessages(MSG_UPDATE_TIMES); + if (mBackgroundThread != null) { + mBackgroundThread.quit(); + mBackgroundThread = null; + mBackgroundHandler = null; + } + } + + @Override + protected void onResume() { + super.onResume(); + refreshUi(mState.update(this, 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, RunningProcessesView.TIME_UPDATE_DELAY); + mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS); + msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS); + mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY); + } + + @Override + public Object onRetainNonConfigurationInstance() { + return mState; + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + } +} diff --git a/src/com/android/settings/applications/RunningServices.java b/src/com/android/settings/applications/RunningServices.java new file mode 100644 index 0000000..703e8c7 --- /dev/null +++ b/src/com/android/settings/applications/RunningServices.java @@ -0,0 +1,582 @@ +/* + * 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.applications; + +import com.android.settings.R; + +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ListActivity; +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.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.HashMap; +import java.util.Iterator; + +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<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>(); + + 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<RunningState.BaseItem> mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList<RunningState.BaseItem>(); + } + } + + 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<ActiveItem> 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<N; i++) { + if (buffer[index+i] != text.charAt(i)) { + return false; + } + } + return true; + } + + private long extractMemValue(byte[] buffer, int index) { + while (index < buffer.length && buffer[index] != '\n') { + if (buffer[index] >= '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<len && (memFree == 0 || memCached == 0); i++) { + if (matchText(mBuffer, i, "MemFree")) { + i += 7; + memFree = extractMemValue(mBuffer, i); + } else if (matchText(mBuffer, i, "Cached")) { + i += 6; + memCached = extractMemValue(mBuffer, i); + } + while (i < BUFLEN && mBuffer[i] != '\n') { + i++; + } + } + return memFree + memCached; + } catch (java.io.FileNotFoundException e) { + } catch (java.io.IOException e) { + } + return 0; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); + mState = (RunningState)getLastNonConfigurationInstance(); + if (mState == null) { + mState = new RunningState(); + } + mProcessBgColor = 0xff505050; + setContentView(R.layout.running_services); + getListView().setDivider(null); + getListView().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; + } + + void refreshUi(boolean dataChanged) { + if (dataChanged) { + ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter()); + adapter.refreshItems(); + adapter.notifyDataSetChanged(); + } + + // This is the amount of available memory until we start killing + // background services. + long availMem = readAvailMem() - SECONDARY_SERVER_MEM; + if (availMem < 0) { + availMem = 0; + } + + synchronized (mState.mLock) { + if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses + || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory + || mLastAvailMemory != availMem) { + mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses; + mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory; + mLastAvailMemory = availMem; + String availStr = availMem != 0 + ? Formatter.formatShortFileSize(this, availMem) : "0"; + String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory); + mBackgroundProcessText.setText(getResources().getString( + R.string.service_background_processes, + mLastNumBackgroundProcesses, availStr, sizeStr)); + } + if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses + || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) { + mLastNumForegroundProcesses = mState.mNumForegroundProcesses; + mLastForegroundProcessMemory = mState.mForegroundProcessMemory; + String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory); + mForegroundProcessText.setText(getResources().getString( + R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr)); + } + mLastNumServiceProcesses = mState.mNumServiceProcesses; + mLastServiceProcessMemory = mState.mServiceProcessMemory; + + float totalMem = availMem + mLastBackgroundProcessMemory + + mLastForegroundProcessMemory + mLastServiceProcessMemory; + mColorBar.setRatios(mLastForegroundProcessMemory/totalMem, + mLastServiceProcessMemory/totalMem, + (availMem+mLastBackgroundProcessMemory)/totalMem); + } + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position); + if (!bi.mIsProcess) { + RunningState.ServiceItem si = (RunningState.ServiceItem)bi; + if (si.mRunningService.clientLabel != 0) { + mCurSelected = null; + PendingIntent pi = mAm.getRunningServiceControlPanel( + si.mRunningService.service); + if (pi != null) { + try { + this.startIntentSender(pi.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 { + mCurSelected = bi; + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.confirm_stop_service); + String msg = getResources().getString( + R.string.confirm_stop_service_msg, + si.mPackageInfo.loadLabel(getPackageManager())); + builder.setMessage(msg); + builder.setPositiveButton(R.string.confirm_stop_stop, this); + builder.setNegativeButton(R.string.confirm_stop_cancel, null); + builder.setCancelable(true); + mCurDialog = builder.show(); + } + } else { + mCurSelected = null; + } + } + + public void onClick(DialogInterface dialog, int which) { + if (mCurSelected != null) { + stopService(new Intent().setComponent( + ((RunningState.ServiceItem)mCurSelected).mRunningService.service)); + if (mBackgroundHandler != null) { + mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); + } + } + } + + @Override + protected void onPause() { + super.onPause(); + mHandler.removeMessages(MSG_UPDATE_TIMES); + if (mBackgroundThread != null) { + mBackgroundThread.quit(); + mBackgroundThread = null; + mBackgroundHandler = null; + } + } + + @Override + protected void onResume() { + super.onResume(); + refreshUi(mState.update(this, 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); + } + + @Override + public Object onRetainNonConfigurationInstance() { + return mState; + } + + public void onMovedToScrapHeap(View view) { + mActiveItems.remove(view); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mCurDialog != null) { + mCurDialog.dismiss(); + } + } +} diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java new file mode 100644 index 0000000..11b40be --- /dev/null +++ b/src/com/android/settings/applications/RunningState.java @@ -0,0 +1,736 @@ +/* + * 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.ActivityManagerNative; +import android.content.ComponentName; +import android.content.Context; +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.os.Debug; +import android.os.RemoteException; +import android.text.format.Formatter; +import android.util.Log; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +/** + * Singleton for retrieving and monitoring the state about all running + * applications/processes/services. + */ +public class RunningState { + + final SparseArray<HashMap<String, ProcessItem>> mProcesses + = new SparseArray<HashMap<String, ProcessItem>>(); + final SparseArray<ProcessItem> mActiveProcesses + = new SparseArray<ProcessItem>(); + final ServiceProcessComparator mServiceProcessComparator + = new ServiceProcessComparator(); + + // Temporary for finding process dependencies. + final SparseArray<ProcessItem> mRunningProcesses + = new SparseArray<ProcessItem>(); + + final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>(); + final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>(); + + 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<BaseItem> mItems = new ArrayList<BaseItem>(); + ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>(); + + 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<ComponentName, ServiceItem> mServices + = new HashMap<ComponentName, ServiceItem>(); + final SparseArray<ProcessItem> mDependentProcesses + = new SparseArray<ProcessItem>(); + + 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<NP; i++) { + ProcessItem proc = mDependentProcesses.valueAt(i); + if (proc.mClient != this) { + changed = true; + proc.mClient = this; + } + proc.mCurSeq = curSeq; + proc.ensureLabel(pm); + changed |= proc.buildDependencyChain(context, pm, curSeq); + } + + if (mLastNumDependentProcesses != mDependentProcesses.size()) { + changed = true; + mLastNumDependentProcesses = mDependentProcesses.size(); + } + + return changed; + } + + void addDependentProcesses(ArrayList<BaseItem> dest, + ArrayList<ProcessItem> destProc) { + final int NP = mDependentProcesses.size(); + for (int i=0; i<NP; i++) { + ProcessItem proc = mDependentProcesses.valueAt(i); + proc.addDependentProcesses(dest, destProc); + dest.add(proc); + if (proc.mPid > 0) { + destProc.add(proc); + } + } + } + } + + static class MergedItem extends BaseItem { + ProcessItem mProcess; + final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>(); + final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>(); + + 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<mServices.size(); i++) { + ServiceItem si = mServices.get(i); + if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) { + mActiveSince = si.mActiveSince; + } + } + + return false; + } + + boolean updateSize(Context context) { + mSize = mProcess.mSize; + for (int i=0; i<mOtherProcesses.size(); i++) { + mSize += mOtherProcesses.get(i).mSize; + } + + 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; + } + } + + static class ServiceProcessComparator implements Comparator<ProcessItem> { + 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<ActivityManager.RunningServiceInfo> services + = am.getRunningServices(RunningServices.MAX_SERVICES); + final int NS = services != null ? services.size() : 0; + for (int i=0; i<NS; i++) { + ActivityManager.RunningServiceInfo si = services.get(i); + // We are not interested in services that have not been started + // and don't have a known client, because + // there is nothing the user can do about them. + if (!si.started && si.clientLabel == 0) { + continue; + } + // We likewise don't care about services running in a + // persistent process like the system or phone. + if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS) + != 0) { + continue; + } + + HashMap<String, ProcessItem> procs = mProcesses.get(si.uid); + if (procs == null) { + procs = new HashMap<String, ProcessItem>(); + 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<ActivityManager.RunningAppProcessInfo> processes + = am.getRunningAppProcesses(); + final int NP = processes != null ? processes.size() : 0; + for (int i=0; i<NP; i++) { + ActivityManager.RunningAppProcessInfo pi = processes.get(i); + ProcessItem proc = mActiveProcesses.get(pi.pid); + if (proc == null) { + // This process is not one that is a direct container + // of a service, so look for it in the secondary + // running list. + proc = mRunningProcesses.get(pi.pid); + if (proc == null) { + proc = new ProcessItem(context, pi.uid, pi.processName); + proc.mPid = pi.pid; + mRunningProcesses.put(pi.pid, proc); + } + proc.mDependentProcesses.clear(); + } + proc.mRunningSeq = mSequence; + proc.mRunningProcessInfo = pi; + } + + // Build the chains from client processes to the process they are + // dependent on; also remove any old running processes. + int NRP = mRunningProcesses.size(); + for (int i=0; i<NRP; i++) { + ProcessItem proc = mRunningProcesses.valueAt(i); + if (proc.mRunningSeq == mSequence) { + int clientPid = proc.mRunningProcessInfo.importanceReasonPid; + if (clientPid != 0) { + ProcessItem client = mActiveProcesses.get(clientPid); + if (client == null) { + client = mRunningProcesses.get(clientPid); + } + if (client != null) { + client.mDependentProcesses.put(proc.mPid, proc); + } + } else { + // In this pass the process doesn't have a client. + // Clear to make sure if it later gets the same one + // that we will detect the change. + proc.mClient = null; + } + } else { + mRunningProcesses.remove(mRunningProcesses.keyAt(i)); + } + } + + // Follow the tree from all primary service processes to all + // processes they are dependent on, marking these processes as + // still being active and determining if anything has changed. + final int NAP = mActiveProcesses.size(); + for (int i=0; i<NAP; i++) { + ProcessItem proc = mActiveProcesses.valueAt(i); + if (proc.mCurSeq == mSequence) { + changed |= proc.buildDependencyChain(context, pm, mSequence); + } + } + + // Look for services and their primary processes that no longer exist... + for (int i=0; i<mProcesses.size(); i++) { + HashMap<String, ProcessItem> procs = mProcesses.valueAt(i); + Iterator<ProcessItem> 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<ServiceItem> 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<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>(); + for (int i=0; i<mProcesses.size(); i++) { + for (ProcessItem pi : mProcesses.valueAt(i).values()) { + pi.mIsSystem = false; + pi.mIsStarted = true; + pi.mActiveSince = Long.MAX_VALUE; + for (ServiceItem si : pi.mServices.values()) { + if (si.mServiceInfo != null + && (si.mServiceInfo.applicationInfo.flags + & ApplicationInfo.FLAG_SYSTEM) != 0) { + pi.mIsSystem = true; + } + if (si.mRunningService != null + && si.mRunningService.clientLabel != 0) { + pi.mIsStarted = false; + if (pi.mActiveSince > si.mRunningService.activeSince) { + pi.mActiveSince = si.mRunningService.activeSince; + } + } + } + sortedProcesses.add(pi); + } + } + + Collections.sort(sortedProcesses, mServiceProcessComparator); + + ArrayList<BaseItem> newItems = new ArrayList<BaseItem>(); + ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>(); + mProcessItems.clear(); + for (int i=0; i<sortedProcesses.size(); i++) { + ProcessItem pi = sortedProcesses.get(i); + pi.mNeedDivider = false; + + int firstProc = mProcessItems.size(); + // First add processes we are dependent on. + pi.addDependentProcesses(newItems, mProcessItems); + // And add the process itself. + newItems.add(pi); + if (pi.mPid > 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<NRP; i++) { + ProcessItem proc = mRunningProcesses.valueAt(i); + if (proc.mCurSeq != mSequence) { + // We didn't hit this process as a dependency on one + // of our active ones, so add it up if needed. + if (proc.mRunningProcessInfo.importance >= + 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<numProc; i++) { + pids[i] = mAllProcessItems.get(i).mPid; + } + Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault() + .getProcessMemoryInfo(pids); + for (int i=pids.length-1; 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<mMergedItems.size(); i++) { + mMergedItems.get(i).updateSize(context); + } + + synchronized (mLock) { + mNumBackgroundProcesses = numBackgroundProcesses; + mNumForegroundProcesses = numForegroundProcesses; + mNumServiceProcesses = numServiceProcesses; + mBackgroundProcessMemory = backgroundProcessMemory; + mForegroundProcessMemory = foregroundProcessMemory; + mServiceProcessMemory = serviceProcessMemory; + } + + return changed; + } + + ArrayList<BaseItem> getCurrentItems() { + synchronized (mLock) { + return mItems; + } + } + + ArrayList<MergedItem> getCurrentMergedItems() { + synchronized (mLock) { + return mMergedItems; + } + } +} diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java index aa4a958..48fd85d 100644 --- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java +++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java @@ -35,11 +35,7 @@ import android.view.MenuItem; import com.android.settings.R; import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile; -import java.text.DateFormat; import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -90,219 +86,39 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // See mConnectAttempted private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000; - // Max time to hold the work queue if we don't get or missed a response - // from the bt framework. - private static final long MAX_WAIT_TIME_FOR_FRAMEWORK = 25 * 1000; - - private enum BluetoothCommand { - CONNECT, DISCONNECT, REMOVE_BOND, - } - - static class BluetoothJob { - final BluetoothCommand command; // CONNECT, DISCONNECT - final CachedBluetoothDevice cachedDevice; - final Profile profile; // HEADSET, A2DP, etc - // 0 means this command was not been sent to the bt framework. - long timeSent; - - public BluetoothJob(BluetoothCommand command, - CachedBluetoothDevice cachedDevice, Profile profile) { - this.command = command; - this.cachedDevice = cachedDevice; - this.profile = profile; - this.timeSent = 0; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(command.name()); - sb.append(" Address:").append(cachedDevice.mDevice); - if (profile != null) { - sb.append(" Profile:").append(profile.name()); - } - sb.append(" TimeSent:"); - if (timeSent == 0) { - sb.append("not yet"); - } else { - sb.append(DateFormat.getTimeInstance().format(new Date(timeSent))); - } - return sb.toString(); - } - } - + /** - * We want to serialize connect and disconnect calls. http://b/170538 - * This are some headsets that may have L2CAP resource limitation. We want - * to limit the bt bandwidth usage. - * - * A queue to keep track of asynchronous calls to the bt framework. The - * first item, if exist, should be in progress i.e. went to the bt framework - * already, waiting for a notification to come back. The second item and - * beyond have not been sent to the bt framework yet. + * Describes the current device and profile for logging. + * + * @param profile Profile to describe + * @return Description of the device and profile */ - private static LinkedList<BluetoothJob> workQueue = new LinkedList<BluetoothJob>(); - - private void queueCommand(BluetoothJob job) { - synchronized (workQueue) { - if (D) { - Log.d(TAG, workQueue.toString()); - } - boolean processNow = pruneQueue(job); - - // Add job to queue - if (D) { - Log.d(TAG, "Adding: " + job.toString()); - } - workQueue.add(job); - - // if there's nothing pending from before, send the command to bt - // framework immediately. - if (workQueue.size() == 1 || processNow) { - // If the failed to process, just drop it from the queue. - // There will be no callback to remove this from the queue. - processCommands(); - } + private String describe(CachedBluetoothDevice cachedDevice, Profile profile) { + StringBuilder sb = new StringBuilder(); + sb.append("Address:").append(cachedDevice.mDevice); + if (profile != null) { + sb.append(" Profile:").append(profile.name()); } - } - - private boolean pruneQueue(BluetoothJob job) { - boolean removedStaleItems = false; - long now = System.currentTimeMillis(); - Iterator<BluetoothJob> it = workQueue.iterator(); - while (it.hasNext()) { - BluetoothJob existingJob = it.next(); - // Remove any pending CONNECTS when we receive a DISCONNECT - if (job != null && job.command == BluetoothCommand.DISCONNECT) { - if (existingJob.timeSent == 0 - && existingJob.command == BluetoothCommand.CONNECT - && existingJob.cachedDevice.mDevice.equals(job.cachedDevice.mDevice) - && existingJob.profile == job.profile) { - if (D) { - Log.d(TAG, "Removed because of a pending disconnect. " + existingJob); - } - it.remove(); - continue; - } - } - - // Defensive Code: Remove any job that older than a preset time. - // We never got a call back. It is better to have overlapping - // calls than to get stuck. - if (existingJob.timeSent != 0 - && (now - existingJob.timeSent) >= MAX_WAIT_TIME_FOR_FRAMEWORK) { - Log.w(TAG, "Timeout. Removing Job:" + existingJob.toString()); - it.remove(); - removedStaleItems = true; - continue; - } - } - return removedStaleItems; + return sb.toString(); } - - private boolean processCommand(BluetoothJob job) { - boolean successful = false; - if (job.timeSent == 0) { - job.timeSent = System.currentTimeMillis(); - switch (job.command) { - case CONNECT: - successful = connectInt(job.cachedDevice, job.profile); - break; - case DISCONNECT: - successful = disconnectInt(job.cachedDevice, job.profile); - break; - case REMOVE_BOND: - BluetoothDevice dev = job.cachedDevice.getDevice(); - if (dev != null) { - successful = dev.removeBond(); - } - break; - } - - if (successful) { - if (D) { - Log.d(TAG, "Command sent successfully:" + job.toString()); - } - } else if (V) { - Log.v(TAG, "Framework rejected command immediately:" + job.toString()); - } - } else if (D) { - Log.d(TAG, "Job already has a sent time. Skip. " + job.toString()); - } - - return successful; + + private String describe(Profile profile) { + return describe(this, profile); } public void onProfileStateChanged(Profile profile, int newProfileState) { - synchronized (workQueue) { - if (D) { - Log.d(TAG, "onProfileStateChanged:" + workQueue.toString()); - } - - int newState = LocalBluetoothProfileManager.getProfileManager(mLocalManager, - profile).convertState(newProfileState); - - if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) { - if (!mProfiles.contains(profile)) { - mProfiles.add(profile); - } - } - - /* Ignore the transient states e.g. connecting, disconnecting */ - if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED || - newState == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) { - BluetoothJob job = workQueue.peek(); - if (job == null) { - return; - } else if (!job.cachedDevice.mDevice.equals(mDevice)) { - // This can happen in 2 cases: 1) BT device initiated pairing and - // 2) disconnects of one headset that's triggered by connects of - // another. - if (D) { - Log.d(TAG, "mDevice:" + mDevice + " != head:" + job.toString()); - } - - // Check to see if we need to remove the stale items from the queue - if (!pruneQueue(null)) { - // nothing in the queue was modify. Just ignore the notification and return. - return; - } - } else { - // Remove the first item and process the next one - workQueue.poll(); - } - - processCommands(); - } + if (D) { + Log.d(TAG, "onProfileStateChanged: profile " + profile.toString() + + " newProfileState " + newProfileState); } - } - /* - * This method is called in 2 places: - * 1) queryCommand() - when someone or something want to connect or - * disconnect - * 2) onProfileStateChanged() - when the framework sends an intent - * notification when it finishes processing a command - */ - private void processCommands() { - if (D) { - Log.d(TAG, "processCommands:" + workQueue.toString()); - } - Iterator<BluetoothJob> it = workQueue.iterator(); - while (it.hasNext()) { - BluetoothJob job = it.next(); - if (processCommand(job)) { - // Sent to bt framework. Done for now. Will remove this job - // from queue when we get an event - return; - } else { - /* - * If the command failed immediately, there will be no event - * callbacks. So delete the job immediately and move on to the - * next one - */ - it.remove(); + int newState = LocalBluetoothProfileManager.getProfileManager(mLocalManager, + profile).convertState(newProfileState); + + if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) { + if (!mProfiles.contains(profile)) { + mProfiles.add(profile); } } } @@ -338,7 +154,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } public void disconnect(Profile profile) { - queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, this, profile)); + disconnectInt(this, profile); } private boolean disconnectInt(CachedBluetoothDevice cachedDevice, Profile profile) { @@ -347,9 +163,17 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> int status = profileManager.getConnectionStatus(cachedDevice.mDevice); if (SettingsBtStatus.isConnectionStatusConnected(status)) { if (profileManager.disconnect(cachedDevice.mDevice)) { + if (D) { + Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile)); + } return true; } + if (V) { + Log.v(TAG, "Framework rejected command immediately:DISCONNECT " + + describe(profile)); + } } + return false; } @@ -419,7 +243,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (profileManager.isPreferred(mDevice)) { ++preferredProfiles; disconnectConnected(profile); - queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); + connectInt(this, profile); } } } @@ -442,7 +266,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> .getProfileManager(mLocalManager, profile); profileManager.setPreferred(mDevice, false); disconnectConnected(profile); - queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); + connectInt(this, profile); } } } @@ -452,7 +276,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // Reset the only-show-one-error-dialog tracking variable mIsConnectingErrorPossible = true; disconnectConnected(profile); - queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile)); + connectInt(this, profile); } private void disconnectConnected(Profile profile) { @@ -464,7 +288,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> for (BluetoothDevice device : devices) { CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(device); if (cachedDevice != null) { - queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, cachedDevice, profile)); + disconnectInt(cachedDevice, profile); } } } @@ -477,12 +301,16 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> int status = profileManager.getConnectionStatus(cachedDevice.mDevice); if (!SettingsBtStatus.isConnectionStatusConnected(status)) { if (profileManager.connect(cachedDevice.mDevice)) { + if (D) { + Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile)); + } return true; } Log.i(TAG, "Failed to connect " + profile.toString() + " to " + cachedDevice.mName); } else { Log.i(TAG, "Already connected"); } + return false; } @@ -527,7 +355,18 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } if (state != BluetoothDevice.BOND_NONE) { - queueCommand(new BluetoothJob(BluetoothCommand.REMOVE_BOND, this, null)); + final BluetoothDevice dev = getDevice(); + if (dev != null) { + final boolean successful = dev.removeBond(); + if (successful) { + if (D) { + Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null)); + } + } else if (V) { + Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " + + describe(null)); + } + } } } @@ -749,30 +588,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> public void onBondingStateChanged(int bondState) { if (bondState == BluetoothDevice.BOND_NONE) { mProfiles.clear(); - - BluetoothJob job = workQueue.peek(); - if (job != null) { - // Remove the first item and process the next one - if (job.command == BluetoothCommand.REMOVE_BOND - && job.cachedDevice.mDevice.equals(mDevice)) { - workQueue.poll(); // dequeue - } else { - // Unexpected job - if (D) { - Log.d(TAG, "job.command = " + job.command); - Log.d(TAG, "mDevice:" + mDevice + " != head:" + job.toString()); - } - - // Check to see if we need to remove the stale items from the queue - if (!pruneQueue(null)) { - // nothing in the queue was modify. Just ignore the notification and return. - refresh(); - return; - } - } - - processCommands(); - } } refresh(); diff --git a/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java b/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java index 08534f3..2a8af5f 100644 --- a/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java +++ b/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java @@ -179,6 +179,9 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity } private void onProfileCheckedStateChanged(Profile profile, boolean checked) { + LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager + .getProfileManager(mManager, profile); + profileManager.setPreferred(mCachedDevice.getDevice(), checked); if (mOnlineMode) { if (checked) { mCachedDevice.connect(profile); @@ -186,10 +189,6 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity mCachedDevice.disconnect(profile); } } - - LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager - .getProfileManager(mManager, profile); - profileManager.setPreferred(mCachedDevice.getDevice(), checked); } public void onDeviceAttributesChanged(CachedBluetoothDevice cachedDevice) { diff --git a/src/com/android/settings/bluetooth/DockService.java b/src/com/android/settings/bluetooth/DockService.java index f318987..d0f8099 100644 --- a/src/com/android/settings/bluetooth/DockService.java +++ b/src/com/android/settings/bluetooth/DockService.java @@ -18,6 +18,7 @@ package com.android.settings.bluetooth; import com.android.settings.R; import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile; +import com.android.settings.bluetooth.LocalBluetoothProfileManager.ServiceListener; import android.app.AlertDialog; import android.app.Notification; @@ -48,7 +49,7 @@ import java.util.Set; public class DockService extends Service implements AlertDialog.OnMultiChoiceClickListener, DialogInterface.OnClickListener, DialogInterface.OnDismissListener, - CompoundButton.OnCheckedChangeListener { + CompoundButton.OnCheckedChangeListener, ServiceListener { private static final String TAG = "DockService"; @@ -101,6 +102,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli // Created in OnCreate() private volatile Looper mServiceLooper; private volatile ServiceHandler mServiceHandler; + private Runnable mRunnable; private DockService mContext; private LocalBluetoothManager mBtManager; @@ -138,6 +140,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli @Override public void onDestroy() { if (DEBUG) Log.d(TAG, "onDestroy"); + mRunnable = null; + LocalBluetoothProfileManager.removeServiceListener(this); if (mDialog != null) { mDialog.dismiss(); mDialog = null; @@ -228,8 +232,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli // This method gets messages from both onStartCommand and mServiceHandler/mServiceLooper private synchronized void processMessage(Message msg) { int msgType = msg.what; - int state = msg.arg1; - int startId = msg.arg2; + final int state = msg.arg1; + final int startId = msg.arg2; boolean deferFinishCall = false; BluetoothDevice device = null; if (msg.obj != null) { @@ -271,12 +275,23 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli } mDevice = device; - if (mBtManager.getDockAutoConnectSetting(device.getAddress())) { - // Setting == auto connect - initBtSettings(mContext, device, state, false); - applyBtSettings(mDevice, startId); + + // Register first in case LocalBluetoothProfileManager + // becomes ready after isManagerReady is called and it + // would be too late to register a service listener. + LocalBluetoothProfileManager.addServiceListener(this); + if (LocalBluetoothProfileManager.isManagerReady()) { + handleDocked(device, state, startId); + // Not needed after all + LocalBluetoothProfileManager.removeServiceListener(this); } else { - createDialog(mContext, mDevice, state, startId); + final BluetoothDevice d = device; + mRunnable = new Runnable() { + public void run() { + handleDocked(d, state, startId); + } + }; + deferFinishCall = true; } } break; @@ -721,8 +736,21 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli } } + private synchronized void handleDocked(final BluetoothDevice device, final int state, + final int startId) { + if (mBtManager.getDockAutoConnectSetting(device.getAddress())) { + // Setting == auto connect + initBtSettings(mContext, device, state, false); + applyBtSettings(mDevice, startId); + } else { + createDialog(mContext, device, state, startId); + } + } + private synchronized void handleUndocked(Context context, LocalBluetoothManager localManager, BluetoothDevice device) { + mRunnable = null; + LocalBluetoothProfileManager.removeServiceListener(this); if (mDialog != null) { mDialog.dismiss(); mDialog = null; @@ -778,4 +806,15 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli editor.commit(); return; } + + public synchronized void onServiceConnected() { + if (mRunnable != null) { + mRunnable.run(); + mRunnable = null; + LocalBluetoothProfileManager.removeServiceListener(this); + } + } + + public void onServiceDisconnected() { + } } diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java index f3aaade..01714fe 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java @@ -28,6 +28,8 @@ import android.util.Log; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -53,6 +55,29 @@ public abstract class LocalBluetoothProfileManager { BluetoothUuid.ObexObjectPush }; + /** + * An interface for notifying BluetoothHeadset IPC clients when they have + * been connected to the BluetoothHeadset service. + */ + public interface ServiceListener { + /** + * Called to notify the client when this proxy object has been + * connected to the BluetoothHeadset service. Clients must wait for + * this callback before making IPC calls on the BluetoothHeadset + * service. + */ + public void onServiceConnected(); + + /** + * Called to notify the client that this proxy object has been + * disconnected from the BluetoothHeadset service. Clients must not + * make IPC calls on the BluetoothHeadset service after this callback. + * This callback will currently only occur if the application hosting + * the BluetoothHeadset service, but may be called more often in future. + */ + public void onServiceDisconnected(); + } + // TODO: close profiles when we're shutting down private static Map<Profile, LocalBluetoothProfileManager> sProfileMap = new HashMap<Profile, LocalBluetoothProfileManager>(); @@ -76,6 +101,26 @@ public abstract class LocalBluetoothProfileManager { } } + private static LinkedList<ServiceListener> mServiceListeners = new LinkedList<ServiceListener>(); + + public static void addServiceListener(ServiceListener l) { + mServiceListeners.add(l); + } + + public static void removeServiceListener(ServiceListener l) { + mServiceListeners.remove(l); + } + + public static boolean isManagerReady() { + // Getting just the headset profile is fine for now. Will need to deal with A2DP + // and others if they aren't always in a ready state. + LocalBluetoothProfileManager profileManager = sProfileMap.get(Profile.HEADSET); + if (profileManager == null) { + return sProfileMap.size() > 0; + } + return profileManager.isProfileReady(); + } + public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager, Profile profile) { // Note: This code assumes that "localManager" is same as the @@ -144,6 +189,8 @@ public abstract class LocalBluetoothProfileManager { return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device)); } + public abstract boolean isProfileReady(); + // TODO: int instead of enum public enum Profile { HEADSET(R.string.bluetooth_profile_headset), @@ -247,6 +294,11 @@ public abstract class LocalBluetoothProfileManager { return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN; } } + + @Override + public boolean isProfileReady() { + return true; + } } /** @@ -256,6 +308,7 @@ public abstract class LocalBluetoothProfileManager { implements BluetoothHeadset.ServiceListener { private BluetoothHeadset mService; private Handler mUiHandler = new Handler(); + private boolean profileReady = false; public HeadsetProfileManager(LocalBluetoothManager localManager) { super(localManager); @@ -263,6 +316,7 @@ public abstract class LocalBluetoothProfileManager { } public void onServiceConnected() { + profileReady = true; // This could be called on a non-UI thread, funnel to UI thread. mUiHandler.post(new Runnable() { public void run() { @@ -277,9 +331,28 @@ public abstract class LocalBluetoothProfileManager { BluetoothHeadset.STATE_CONNECTED); } }); + + if (mServiceListeners.size() > 0) { + Iterator<ServiceListener> it = mServiceListeners.iterator(); + while(it.hasNext()) { + it.next().onServiceConnected(); + } + } } public void onServiceDisconnected() { + profileReady = false; + if (mServiceListeners.size() > 0) { + Iterator<ServiceListener> it = mServiceListeners.iterator(); + while(it.hasNext()) { + it.next().onServiceDisconnected(); + } + } + } + + @Override + public boolean isProfileReady() { + return profileReady; } @Override @@ -297,7 +370,10 @@ public abstract class LocalBluetoothProfileManager { public boolean connect(BluetoothDevice device) { // Since connectHeadset fails if already connected to a headset, we // disconnect from any headset first - mService.disconnectHeadset(); + BluetoothDevice currDevice = mService.getCurrentHeadset(); + if (currDevice != null) { + mService.disconnectHeadset(currDevice); + } return mService.connectHeadset(device); } @@ -308,7 +384,7 @@ public abstract class LocalBluetoothProfileManager { if (mService.getPriority(device) > BluetoothHeadset.PRIORITY_ON) { mService.setPriority(device, BluetoothHeadset.PRIORITY_ON); } - return mService.disconnectHeadset(); + return mService.disconnectHeadset(device); } else { return false; } @@ -318,7 +394,7 @@ public abstract class LocalBluetoothProfileManager { public int getConnectionStatus(BluetoothDevice device) { BluetoothDevice currentDevice = mService.getCurrentHeadset(); return currentDevice != null && currentDevice.equals(device) - ? convertState(mService.getState()) + ? convertState(mService.getState(device)) : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED; } @@ -424,6 +500,11 @@ public abstract class LocalBluetoothProfileManager { } @Override + public boolean isProfileReady() { + return true; + } + + @Override public int convertState(int oppState) { switch (oppState) { case 0: diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java index 4db968a..cc112f8 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: diff --git a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java index fb9fbcd..ca5d3ab 100644 --- a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java +++ b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java @@ -178,7 +178,7 @@ public class QuickLaunchSettings extends PreferenceActivity implements } public void onClick(DialogInterface dialog, int which) { - if (mClearDialogShortcut > 0 && which == AlertDialog.BUTTON1) { + if (mClearDialogShortcut > 0 && which == AlertDialog.BUTTON_POSITIVE) { // Clear the shortcut clearShortcut(mClearDialogShortcut); } diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java index 8880780..7b8d433 100644 --- a/src/com/android/settings/vpn/VpnSettings.java +++ b/src/com/android/settings/vpn/VpnSettings.java @@ -17,7 +17,6 @@ package com.android.settings.vpn; import com.android.settings.R; -import com.android.settings.SecuritySettings; import android.app.AlertDialog; import android.app.Dialog; @@ -38,13 +37,10 @@ import android.net.vpn.VpnType; import android.os.Bundle; import android.os.ConditionVariable; import android.os.IBinder; -import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; -import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.preference.Preference.OnPreferenceClickListener; import android.security.Credentials; @@ -53,7 +49,6 @@ import android.text.TextUtils; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; -import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView.AdapterContextMenuInfo; @@ -67,12 +62,9 @@ import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; /** * The preference activity for configuring VPN settings. @@ -101,8 +93,8 @@ public class VpnSettings extends PreferenceActivity implements private static final int CONTEXT_MENU_EDIT_ID = ContextMenu.FIRST + 2; private static final int CONTEXT_MENU_DELETE_ID = ContextMenu.FIRST + 3; - private static final int CONNECT_BUTTON = DialogInterface.BUTTON1; - private static final int OK_BUTTON = DialogInterface.BUTTON1; + private static final int CONNECT_BUTTON = DialogInterface.BUTTON_POSITIVE; + private static final int OK_BUTTON = DialogInterface.BUTTON_POSITIVE; private static final int DIALOG_CONNECT = VpnManager.VPN_ERROR_LARGEST + 1; private static final int DIALOG_SECRET_NOT_SET = DIALOG_CONNECT + 1; diff --git a/src/com/android/settings/wifi/AdvancedSettings.java b/src/com/android/settings/wifi/AdvancedSettings.java index 7d62f8e..636e1df 100644 --- a/src/com/android/settings/wifi/AdvancedSettings.java +++ b/src/com/android/settings/wifi/AdvancedSettings.java @@ -40,6 +40,7 @@ public class AdvancedSettings extends PreferenceActivity implements Preference.OnPreferenceChangeListener { private static final String KEY_MAC_ADDRESS = "mac_address"; + private static final String KEY_CURRENT_IP_ADDRESS = "current_ip_address"; private static final String KEY_USE_STATIC_IP = "use_static_ip"; private static final String KEY_NUM_CHANNELS = "num_channels"; private static final String KEY_SLEEP_POLICY = "sleep_policy"; @@ -109,7 +110,7 @@ public class AdvancedSettings extends PreferenceActivity initNumChannelsPreference(); } initSleepPolicyPreference(); - refreshMacAddress(); + refreshWifiInfo(); } private void initNumChannelsPreference() { @@ -307,7 +308,7 @@ public class AdvancedSettings extends PreferenceActivity } } - private void refreshMacAddress() { + private void refreshWifiInfo() { WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); WifiInfo wifiInfo = wifiManager.getConnectionInfo(); @@ -315,6 +316,20 @@ public class AdvancedSettings extends PreferenceActivity String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress(); wifiMacAddressPref.setSummary(!TextUtils.isEmpty(macAddress) ? macAddress : getString(R.string.status_unavailable)); + + Preference wifiIpAddressPref = findPreference(KEY_CURRENT_IP_ADDRESS); + String ipAddress = null; + if (wifiInfo != null) { + long addr = wifiInfo.getIpAddress(); + if (addr != 0) { + // handle negative values whe first octet > 127 + if (addr < 0) addr += 0x100000000L; + ipAddress = String.format("%d.%d.%d.%d", + addr & 0xFF, (addr >> 8) & 0xFF, (addr >> 16) & 0xFF, (addr >> 24) & 0xFF); + } + } + wifiIpAddressPref.setSummary(ipAddress == null ? + getString(R.string.status_unavailable) : ipAddress); } - + } |