diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/settings/PrivacySettings.java | 4 | ||||
-rw-r--r-- | src/com/android/settings/RunningServices.java | 1172 | ||||
-rw-r--r-- | src/com/android/settings/applications/InstalledAppDetails.java (renamed from src/com/android/settings/InstalledAppDetails.java) | 8 | ||||
-rw-r--r-- | src/com/android/settings/applications/ManageApplications.java (renamed from src/com/android/settings/ManageApplications.java) | 315 | ||||
-rw-r--r-- | src/com/android/settings/applications/RunningProcessesView.java | 548 | ||||
-rw-r--r-- | src/com/android/settings/applications/RunningProcessesViewOld.java | 538 | ||||
-rw-r--r-- | src/com/android/settings/applications/RunningServiceDetails.java | 390 | ||||
-rw-r--r-- | src/com/android/settings/applications/RunningServices.java | 582 | ||||
-rw-r--r-- | src/com/android/settings/applications/RunningState.java | 736 | ||||
-rw-r--r-- | src/com/android/settings/fuelgauge/PowerUsageDetail.java | 8 | ||||
-rw-r--r-- | src/com/android/settings/wifi/WifiDialog.java | 6 |
11 files changed, 2967 insertions, 1340 deletions
diff --git a/src/com/android/settings/PrivacySettings.java b/src/com/android/settings/PrivacySettings.java index 28d823d..29deacf 100644 --- a/src/com/android/settings/PrivacySettings.java +++ b/src/com/android/settings/PrivacySettings.java @@ -44,7 +44,7 @@ public class PrivacySettings extends PreferenceActivity implements // Vendor specific private static final String GSETTINGS_PROVIDER = "com.google.settings"; private static final String BACKUP_CATEGORY = "backup_category"; - private static final String BACKUP_SETTINGS = "backup_settings"; + private static final String BACKUP_DATA = "backup_data"; private static final String AUTO_RESTORE = "auto_restore"; private CheckBoxPreference mBackup; private CheckBoxPreference mAutoRestore; @@ -59,7 +59,7 @@ public class PrivacySettings extends PreferenceActivity implements addPreferencesFromResource(R.xml.privacy_settings); final PreferenceScreen screen = getPreferenceScreen(); - mBackup = (CheckBoxPreference) screen.findPreference(BACKUP_SETTINGS); + mBackup = (CheckBoxPreference) screen.findPreference(BACKUP_DATA); mAutoRestore = (CheckBoxPreference) screen.findPreference(AUTO_RESTORE); // Vendor specific 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/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/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/wifi/WifiDialog.java b/src/com/android/settings/wifi/WifiDialog.java index f85d054..a8bf717 100644 --- a/src/com/android/settings/wifi/WifiDialog.java +++ b/src/com/android/settings/wifi/WifiDialog.java @@ -60,6 +60,7 @@ class WifiDialog extends AlertDialog implements View.OnClickListener, private Spinner mEapMethod; private Spinner mEapCaCert; + private Spinner mPhase2; private Spinner mEapUserCert; private TextView mEapIdentity; private TextView mEapAnonymous; @@ -141,6 +142,9 @@ class WifiDialog extends AlertDialog implements View.OnClickListener, config.allowedKeyManagement.set(KeyMgmt.WPA_EAP); config.allowedKeyManagement.set(KeyMgmt.IEEE8021X); config.eap.setValue((String) mEapMethod.getSelectedItem()); + + config.phase2.setValue((mPhase2.getSelectedItemPosition() == 0) ? "" : + "auth=" + mPhase2.getSelectedItem()); config.ca_cert.setValue((mEapCaCert.getSelectedItemPosition() == 0) ? "" : KEYSTORE_SPACE + Credentials.CA_CERTIFICATE + (String) mEapCaCert.getSelectedItem()); @@ -302,6 +306,7 @@ class WifiDialog extends AlertDialog implements View.OnClickListener, if (mEapMethod == null) { mEapMethod = (Spinner) mView.findViewById(R.id.method); + mPhase2 = (Spinner) mView.findViewById(R.id.phase2); mEapCaCert = (Spinner) mView.findViewById(R.id.ca_cert); mEapUserCert = (Spinner) mView.findViewById(R.id.user_cert); mEapIdentity = (TextView) mView.findViewById(R.id.identity); @@ -313,6 +318,7 @@ class WifiDialog extends AlertDialog implements View.OnClickListener, if (mAccessPoint != null && mAccessPoint.networkId != -1) { WifiConfiguration config = mAccessPoint.getConfig(); setSelection(mEapMethod, config.eap.value()); + setSelection(mPhase2, config.phase2.value()); setCertificate(mEapCaCert, Credentials.CA_CERTIFICATE, config.ca_cert.value()); setCertificate(mEapUserCert, Credentials.USER_PRIVATE_KEY, |