summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/settings/ApnEditor.java6
-rw-r--r--src/com/android/settings/ApplicationSettings.java2
-rw-r--r--src/com/android/settings/RunningServices.java1172
-rw-r--r--src/com/android/settings/SecuritySettings.java80
-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.java548
-rw-r--r--src/com/android/settings/applications/RunningProcessesViewOld.java538
-rw-r--r--src/com/android/settings/applications/RunningServiceDetails.java390
-rw-r--r--src/com/android/settings/applications/RunningServices.java582
-rw-r--r--src/com/android/settings/applications/RunningState.java736
-rw-r--r--src/com/android/settings/bluetooth/CachedBluetoothDevice.java289
-rw-r--r--src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java7
-rw-r--r--src/com/android/settings/bluetooth/DockService.java55
-rw-r--r--src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java87
-rw-r--r--src/com/android/settings/fuelgauge/PowerUsageDetail.java8
-rw-r--r--src/com/android/settings/quicklaunch/QuickLaunchSettings.java2
-rw-r--r--src/com/android/settings/vpn/VpnSettings.java12
-rw-r--r--src/com/android/settings/wifi/AdvancedSettings.java21
19 files changed, 3246 insertions, 1612 deletions
diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java
index 62856d1..e097854 100644
--- a/src/com/android/settings/ApnEditor.java
+++ b/src/com/android/settings/ApnEditor.java
@@ -458,7 +458,11 @@ public class ApnEditor extends PreferenceActivity
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preference pref = findPreference(key);
if (pref != null) {
- pref.setSummary(checkNull(sharedPreferences.getString(key, "")));
+ if (pref.equals(mPassword)){
+ pref.setSummary(starify(sharedPreferences.getString(key, "")));
+ } else {
+ pref.setSummary(checkNull(sharedPreferences.getString(key, "")));
+ }
}
}
}
diff --git a/src/com/android/settings/ApplicationSettings.java b/src/com/android/settings/ApplicationSettings.java
index c743f1c..a919ae8 100644
--- a/src/com/android/settings/ApplicationSettings.java
+++ b/src/com/android/settings/ApplicationSettings.java
@@ -124,7 +124,7 @@ public class ApplicationSettings extends PreferenceActivity implements
}
public void onClick(DialogInterface dialog, int which) {
- if (dialog == mWarnInstallApps && which == DialogInterface.BUTTON1) {
+ if (dialog == mWarnInstallApps && which == DialogInterface.BUTTON_POSITIVE) {
setNonMarketAppsAllowed(true);
mToggleAppInstallation.setChecked(true);
}
diff --git a/src/com/android/settings/RunningServices.java b/src/com/android/settings/RunningServices.java
deleted file mode 100644
index e67adf0..0000000
--- a/src/com/android/settings/RunningServices.java
+++ /dev/null
@@ -1,1172 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings;
-
-import com.android.settings.R;
-import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ListActivity;
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.text.format.DateUtils;
-import android.text.format.Formatter;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import java.io.FileInputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-public class RunningServices extends ListActivity
- implements AbsListView.RecyclerListener,
- DialogInterface.OnClickListener {
- static final String TAG = "RunningServices";
-
- /** Maximum number of services to retrieve */
- static final int MAX_SERVICES = 100;
-
- static final int MSG_UPDATE_TIMES = 1;
- static final int MSG_UPDATE_CONTENTS = 2;
- static final int MSG_REFRESH_UI = 3;
-
- static final long TIME_UPDATE_DELAY = 1000;
- static final long CONTENTS_UPDATE_DELAY = 2000;
-
- // Memory pages are 4K.
- static final long PAGE_SIZE = 4*1024;
-
- long SECONDARY_SERVER_MEM;
-
- final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
-
- ActivityManager mAm;
-
- State mState;
-
- StringBuilder mBuilder = new StringBuilder(128);
-
- BaseItem mCurSelected;
-
- int mProcessBgColor;
-
- LinearColorBar mColorBar;
- TextView mBackgroundProcessText;
- TextView mForegroundProcessText;
-
- int mLastNumBackgroundProcesses = -1;
- int mLastNumForegroundProcesses = -1;
- int mLastNumServiceProcesses = -1;
- long mLastBackgroundProcessMemory = -1;
- long mLastForegroundProcessMemory = -1;
- long mLastServiceProcessMemory = -1;
- long mLastAvailMemory = -1;
-
- Dialog mCurDialog;
-
- byte[] mBuffer = new byte[1024];
-
- class ActiveItem {
- View mRootView;
- BaseItem mItem;
- ActivityManager.RunningServiceInfo mService;
- ViewHolder mHolder;
- long mFirstRunTime;
-
- void updateTime(Context context) {
- if (mItem.mIsProcess) {
- String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
- if (!size.equals(mItem.mCurSizeStr)) {
- mItem.mCurSizeStr = size;
- mHolder.size.setText(size);
- }
- } else {
- if (mItem.mActiveSince >= 0) {
- mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
- (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
- } else {
- mHolder.size.setText(context.getResources().getText(
- R.string.service_restarting));
- }
- }
- }
- }
-
- static class BaseItem {
- final boolean mIsProcess;
-
- PackageItemInfo mPackageInfo;
- CharSequence mDisplayLabel;
- String mLabel;
- String mDescription;
-
- int mCurSeq;
-
- long mActiveSince;
- long mSize;
- String mSizeStr;
- String mCurSizeStr;
- boolean mNeedDivider;
-
- public BaseItem(boolean isProcess) {
- mIsProcess = isProcess;
- }
- }
-
- static class ServiceItem extends BaseItem {
- ActivityManager.RunningServiceInfo mRunningService;
- ServiceInfo mServiceInfo;
- boolean mShownAsStarted;
-
- public ServiceItem() {
- super(false);
- }
- }
-
- static class ProcessItem extends BaseItem {
- final HashMap<ComponentName, ServiceItem> mServices
- = new HashMap<ComponentName, ServiceItem>();
- final SparseArray<ProcessItem> mDependentProcesses
- = new SparseArray<ProcessItem>();
-
- final int mUid;
- final String mProcessName;
- int mPid;
-
- ProcessItem mClient;
- int mLastNumDependentProcesses;
-
- int mRunningSeq;
- ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
-
- // Purely for sorting.
- boolean mIsSystem;
- boolean mIsStarted;
- long mActiveSince;
-
- public ProcessItem(Context context, int uid, String processName) {
- super(true);
- mDescription = context.getResources().getString(
- R.string.service_process_name, processName);
- mUid = uid;
- mProcessName = processName;
- }
-
- void ensureLabel(PackageManager pm) {
- if (mLabel != null) {
- return;
- }
-
- try {
- ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0);
- if (ai.uid == mUid) {
- mDisplayLabel = ai.loadLabel(pm);
- mLabel = mDisplayLabel.toString();
- mPackageInfo = ai;
- return;
- }
- } catch (PackageManager.NameNotFoundException e) {
- }
-
- // If we couldn't get information about the overall
- // process, try to find something about the uid.
- String[] pkgs = pm.getPackagesForUid(mUid);
-
- // If there is one package with this uid, that is what we want.
- if (pkgs.length == 1) {
- try {
- ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
- mDisplayLabel = ai.loadLabel(pm);
- mLabel = mDisplayLabel.toString();
- mPackageInfo = ai;
- return;
- } catch (PackageManager.NameNotFoundException e) {
- }
- }
-
- // If there are multiple, see if one gives us the official name
- // for this uid.
- for (String name : pkgs) {
- try {
- PackageInfo pi = pm.getPackageInfo(name, 0);
- if (pi.sharedUserLabel != 0) {
- CharSequence nm = pm.getText(name,
- pi.sharedUserLabel, pi.applicationInfo);
- if (nm != null) {
- mDisplayLabel = nm;
- mLabel = nm.toString();
- mPackageInfo = pi.applicationInfo;
- return;
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- }
- }
-
- // If still don't have anything to display, just use the
- // service info.
- if (mServices.size() > 0) {
- mPackageInfo = mServices.values().iterator().next()
- .mServiceInfo.applicationInfo;
- mDisplayLabel = mPackageInfo.loadLabel(pm);
- mLabel = mDisplayLabel.toString();
- return;
- }
-
- // Finally... whatever, just pick the first package's name.
- try {
- ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
- mDisplayLabel = ai.loadLabel(pm);
- mLabel = mDisplayLabel.toString();
- mPackageInfo = ai;
- return;
- } catch (PackageManager.NameNotFoundException e) {
- }
- }
-
- boolean updateService(Context context,
- ActivityManager.RunningServiceInfo service) {
- final PackageManager pm = context.getPackageManager();
-
- boolean changed = false;
- ServiceItem si = mServices.get(service.service);
- if (si == null) {
- changed = true;
- si = new ServiceItem();
- si.mRunningService = service;
- try {
- si.mServiceInfo = pm.getServiceInfo(service.service, 0);
- } catch (PackageManager.NameNotFoundException e) {
- }
- if (si.mServiceInfo != null && (si.mServiceInfo.labelRes != 0
- || si.mServiceInfo.nonLocalizedLabel != null)) {
- si.mDisplayLabel = si.mServiceInfo.loadLabel(pm);
- si.mLabel = si.mDisplayLabel.toString();
- } else {
- si.mLabel = si.mRunningService.service.getClassName();
- int tail = si.mLabel.lastIndexOf('.');
- if (tail >= 0) {
- si.mLabel = si.mLabel.substring(tail+1, si.mLabel.length());
- }
- si.mDisplayLabel = si.mLabel;
- }
- si.mPackageInfo = si.mServiceInfo.applicationInfo;
- mServices.put(service.service, si);
- }
- si.mCurSeq = mCurSeq;
- si.mRunningService = service;
- long activeSince = service.restarting == 0 ? service.activeSince : -1;
- if (si.mActiveSince != activeSince) {
- si.mActiveSince = activeSince;
- changed = true;
- }
- if (service.clientPackage != null && service.clientLabel != 0) {
- if (si.mShownAsStarted) {
- si.mShownAsStarted = false;
- changed = true;
- }
- try {
- Resources clientr = pm.getResourcesForApplication(service.clientPackage);
- String label = clientr.getString(service.clientLabel);
- si.mDescription = context.getResources().getString(
- R.string.service_client_name, label);
- } catch (PackageManager.NameNotFoundException e) {
- si.mDescription = null;
- }
- } else {
- if (!si.mShownAsStarted) {
- si.mShownAsStarted = true;
- changed = true;
- }
- si.mDescription = context.getResources().getString(
- R.string.service_started_by_app);
- }
-
- return changed;
- }
-
- boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) {
- mSize = ((long)mem.getTotalPss()) * 1024;
- if (mCurSeq == curSeq) {
- String sizeStr = Formatter.formatShortFileSize(
- context, mSize);
- if (!sizeStr.equals(mSizeStr)){
- mSizeStr = sizeStr;
- // We update this on the second tick where we update just
- // the text in the current items, so no need to say we
- // changed here.
- return false;
- }
- }
- return false;
- }
-
- boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
- final int NP = mDependentProcesses.size();
- boolean changed = false;
- for (int i=0; i<NP; i++) {
- ProcessItem proc = mDependentProcesses.valueAt(i);
- if (proc.mClient != this) {
- changed = true;
- proc.mClient = this;
- }
- proc.mCurSeq = curSeq;
- proc.ensureLabel(pm);
- changed |= proc.buildDependencyChain(context, pm, curSeq);
- }
-
- if (mLastNumDependentProcesses != mDependentProcesses.size()) {
- changed = true;
- mLastNumDependentProcesses = mDependentProcesses.size();
- }
-
- return changed;
- }
-
- void addDependentProcesses(ArrayList<BaseItem> dest,
- ArrayList<ProcessItem> destProc) {
- final int NP = mDependentProcesses.size();
- for (int i=0; i<NP; i++) {
- ProcessItem proc = mDependentProcesses.valueAt(i);
- proc.addDependentProcesses(dest, destProc);
- dest.add(proc);
- if (proc.mPid > 0) {
- destProc.add(proc);
- }
- }
- }
- }
-
- static class ServiceProcessComparator implements Comparator<ProcessItem> {
- public int compare(ProcessItem object1, ProcessItem object2) {
- if (object1.mIsStarted != object2.mIsStarted) {
- // Non-started processes go last.
- return object1.mIsStarted ? -1 : 1;
- }
- if (object1.mIsSystem != object2.mIsSystem) {
- // System processes go below non-system.
- return object1.mIsSystem ? 1 : -1;
- }
- if (object1.mActiveSince != object2.mActiveSince) {
- // Remaining ones are sorted with the longest running
- // services last.
- return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
- }
- return 0;
- }
- }
-
- static class State {
- final SparseArray<HashMap<String, ProcessItem>> mProcesses
- = new SparseArray<HashMap<String, ProcessItem>>();
- final SparseArray<ProcessItem> mActiveProcesses
- = new SparseArray<ProcessItem>();
- final ServiceProcessComparator mServiceProcessComparator
- = new ServiceProcessComparator();
-
- // Temporary for finding process dependencies.
- final SparseArray<ProcessItem> mRunningProcesses
- = new SparseArray<ProcessItem>();
-
- final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
- final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
-
- int mSequence = 0;
-
- // ----- following protected by mLock -----
-
- // Lock for protecting the state that will be shared between the
- // background update thread and the UI thread.
- final Object mLock = new Object();
-
- ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
-
- int mNumBackgroundProcesses;
- long mBackgroundProcessMemory;
- int mNumForegroundProcesses;
- long mForegroundProcessMemory;
- int mNumServiceProcesses;
- long mServiceProcessMemory;
-
- boolean update(Context context, ActivityManager am) {
- final PackageManager pm = context.getPackageManager();
-
- mSequence++;
-
- boolean changed = false;
-
- List<ActivityManager.RunningServiceInfo> services
- = am.getRunningServices(MAX_SERVICES);
- final int NS = services != null ? services.size() : 0;
- for (int i=0; i<NS; i++) {
- ActivityManager.RunningServiceInfo si = services.get(i);
- // We are not interested in services that have not been started
- // and don't have a known client, because
- // there is nothing the user can do about them.
- if (!si.started && si.clientLabel == 0) {
- continue;
- }
- // We likewise don't care about services running in a
- // persistent process like the system or phone.
- if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
- != 0) {
- continue;
- }
-
- HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
- if (procs == null) {
- procs = new HashMap<String, ProcessItem>();
- mProcesses.put(si.uid, procs);
- }
- ProcessItem proc = procs.get(si.process);
- if (proc == null) {
- changed = true;
- proc = new ProcessItem(context, si.uid, si.process);
- procs.put(si.process, proc);
- }
-
- if (proc.mCurSeq != mSequence) {
- int pid = si.restarting == 0 ? si.pid : 0;
- if (pid != proc.mPid) {
- changed = true;
- if (proc.mPid != pid) {
- if (proc.mPid != 0) {
- mActiveProcesses.remove(proc.mPid);
- }
- if (pid != 0) {
- mActiveProcesses.put(pid, proc);
- }
- proc.mPid = pid;
- }
- }
- proc.mDependentProcesses.clear();
- proc.mCurSeq = mSequence;
- }
- changed |= proc.updateService(context, si);
- }
-
- // Now update the map of other processes that are running (but
- // don't have services actively running inside them).
- List<ActivityManager.RunningAppProcessInfo> processes
- = am.getRunningAppProcesses();
- final int NP = processes != null ? processes.size() : 0;
- for (int i=0; i<NP; i++) {
- ActivityManager.RunningAppProcessInfo pi = processes.get(i);
- ProcessItem proc = mActiveProcesses.get(pi.pid);
- if (proc == null) {
- // This process is not one that is a direct container
- // of a service, so look for it in the secondary
- // running list.
- proc = mRunningProcesses.get(pi.pid);
- if (proc == null) {
- proc = new ProcessItem(context, pi.uid, pi.processName);
- proc.mPid = pi.pid;
- mRunningProcesses.put(pi.pid, proc);
- }
- proc.mDependentProcesses.clear();
- }
- proc.mRunningSeq = mSequence;
- proc.mRunningProcessInfo = pi;
- }
-
- // Build the chains from client processes to the process they are
- // dependent on; also remove any old running processes.
- int NRP = mRunningProcesses.size();
- for (int i=0; i<NRP; i++) {
- ProcessItem proc = mRunningProcesses.valueAt(i);
- if (proc.mRunningSeq == mSequence) {
- int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
- if (clientPid != 0) {
- ProcessItem client = mActiveProcesses.get(clientPid);
- if (client == null) {
- client = mRunningProcesses.get(clientPid);
- }
- if (client != null) {
- client.mDependentProcesses.put(proc.mPid, proc);
- }
- } else {
- // In this pass the process doesn't have a client.
- // Clear to make sure if it later gets the same one
- // that we will detect the change.
- proc.mClient = null;
- }
- } else {
- mRunningProcesses.remove(mRunningProcesses.keyAt(i));
- }
- }
-
- // Follow the tree from all primary service processes to all
- // processes they are dependent on, marking these processes as
- // still being active and determining if anything has changed.
- final int NAP = mActiveProcesses.size();
- for (int i=0; i<NAP; i++) {
- ProcessItem proc = mActiveProcesses.valueAt(i);
- if (proc.mCurSeq == mSequence) {
- changed |= proc.buildDependencyChain(context, pm, mSequence);
- }
- }
-
- // Look for services and their primary processes that no longer exist...
- for (int i=0; i<mProcesses.size(); i++) {
- HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
- Iterator<ProcessItem> pit = procs.values().iterator();
- while (pit.hasNext()) {
- ProcessItem pi = pit.next();
- if (pi.mCurSeq == mSequence) {
- pi.ensureLabel(pm);
- if (pi.mPid == 0) {
- // Sanity: a non-process can't be dependent on
- // anything.
- pi.mDependentProcesses.clear();
- }
- } else {
- changed = true;
- pit.remove();
- if (procs.size() == 0) {
- mProcesses.remove(mProcesses.keyAt(i));
- }
- if (pi.mPid != 0) {
- mActiveProcesses.remove(pi.mPid);
- }
- continue;
- }
- Iterator<ServiceItem> sit = pi.mServices.values().iterator();
- while (sit.hasNext()) {
- ServiceItem si = sit.next();
- if (si.mCurSeq != mSequence) {
- changed = true;
- sit.remove();
- }
- }
- }
- }
-
- if (changed) {
- // First determine an order for the services.
- ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
- for (int i=0; i<mProcesses.size(); i++) {
- for (ProcessItem pi : mProcesses.valueAt(i).values()) {
- pi.mIsSystem = false;
- pi.mIsStarted = true;
- pi.mActiveSince = Long.MAX_VALUE;
- for (ServiceItem si : pi.mServices.values()) {
- if (si.mServiceInfo != null
- && (si.mServiceInfo.applicationInfo.flags
- & ApplicationInfo.FLAG_SYSTEM) != 0) {
- pi.mIsSystem = true;
- }
- if (si.mRunningService != null
- && si.mRunningService.clientLabel != 0) {
- pi.mIsStarted = false;
- if (pi.mActiveSince > si.mRunningService.activeSince) {
- pi.mActiveSince = si.mRunningService.activeSince;
- }
- }
- }
- sortedProcesses.add(pi);
- }
- }
-
- Collections.sort(sortedProcesses, mServiceProcessComparator);
-
- ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
- mProcessItems.clear();
- for (int i=0; i<sortedProcesses.size(); i++) {
- ProcessItem pi = sortedProcesses.get(i);
- pi.mNeedDivider = false;
- // First add processes we are dependent on.
- pi.addDependentProcesses(newItems, mProcessItems);
- // And add the process itself.
- newItems.add(pi);
- if (pi.mPid > 0) {
- mProcessItems.add(pi);
- }
- // And finally the services running in it.
- boolean needDivider = false;
- for (ServiceItem si : pi.mServices.values()) {
- si.mNeedDivider = needDivider;
- needDivider = true;
- newItems.add(si);
- }
- }
- synchronized (mLock) {
- mItems = newItems;
- }
- }
-
- // Count number of interesting other (non-active) processes, and
- // build a list of all processes we will retrieve memory for.
- mAllProcessItems.clear();
- mAllProcessItems.addAll(mProcessItems);
- int numBackgroundProcesses = 0;
- int numForegroundProcesses = 0;
- int numServiceProcesses = 0;
- NRP = mRunningProcesses.size();
- for (int i=0; i<NRP; i++) {
- ProcessItem proc = mRunningProcesses.valueAt(i);
- if (proc.mCurSeq != mSequence) {
- // We didn't hit this process as a dependency on one
- // of our active ones, so add it up if needed.
- if (proc.mRunningProcessInfo.importance >=
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
- numBackgroundProcesses++;
- mAllProcessItems.add(proc);
- } else if (proc.mRunningProcessInfo.importance <=
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
- numForegroundProcesses++;
- mAllProcessItems.add(proc);
- } else {
- Log.i(TAG, "Unknown non-service process: "
- + proc.mProcessName + " #" + proc.mPid);
- }
- } else {
- numServiceProcesses++;
- }
- }
-
- long backgroundProcessMemory = 0;
- long foregroundProcessMemory = 0;
- long serviceProcessMemory = 0;
- try {
- final int numProc = mAllProcessItems.size();
- int[] pids = new int[numProc];
- for (int i=0; i<numProc; i++) {
- pids[i] = mAllProcessItems.get(i).mPid;
- }
- Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault()
- .getProcessMemoryInfo(pids);
- for (int i=pids.length-1; i>=0; i--) {
- ProcessItem proc = mAllProcessItems.get(i);
- changed |= proc.updateSize(context, mem[i], mSequence);
- if (proc.mCurSeq == mSequence) {
- serviceProcessMemory += proc.mSize;
- } else if (proc.mRunningProcessInfo.importance >=
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
- backgroundProcessMemory += proc.mSize;
- } else if (proc.mRunningProcessInfo.importance <=
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
- foregroundProcessMemory += proc.mSize;
- }
- }
- } catch (RemoteException e) {
- }
-
- synchronized (mLock) {
- mNumBackgroundProcesses = numBackgroundProcesses;
- mNumForegroundProcesses = numForegroundProcesses;
- mNumServiceProcesses = numServiceProcesses;
- mBackgroundProcessMemory = backgroundProcessMemory;
- mForegroundProcessMemory = foregroundProcessMemory;
- mServiceProcessMemory = serviceProcessMemory;
- }
-
- return changed;
- }
-
- ArrayList<BaseItem> getCurrentItems() {
- synchronized (mLock) {
- return mItems;
- }
- }
- }
-
- static class TimeTicker extends TextView {
- public TimeTicker(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- }
-
- static class ViewHolder {
- ImageView separator;
- ImageView icon;
- TextView name;
- TextView description;
- TextView size;
- }
-
- class ServiceListAdapter extends BaseAdapter {
- final State mState;
- final LayoutInflater mInflater;
- ArrayList<BaseItem> mItems;
-
- ServiceListAdapter(State state) {
- mState = state;
- mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- refreshItems();
- }
-
- void refreshItems() {
- ArrayList<BaseItem> newItems = mState.getCurrentItems();
- if (mItems != newItems) {
- mItems = newItems;
- }
- if (mItems == null) {
- mItems = new ArrayList<BaseItem>();
- }
- }
-
- public boolean hasStableIds() {
- return true;
- }
-
- public int getCount() {
- return mItems.size();
- }
-
- public Object getItem(int position) {
- return mItems.get(position);
- }
-
- public long getItemId(int position) {
- return mItems.get(position).hashCode();
- }
-
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- public boolean isEnabled(int position) {
- return !mItems.get(position).mIsProcess;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- View v;
- if (convertView == null) {
- v = newView(parent);
- } else {
- v = convertView;
- }
- bindView(v, position);
- return v;
- }
-
- public View newView(ViewGroup parent) {
- View v = mInflater.inflate(R.layout.running_services_item, parent, false);
- ViewHolder h = new ViewHolder();
- h.separator = (ImageView)v.findViewById(R.id.separator);
- h.icon = (ImageView)v.findViewById(R.id.icon);
- h.name = (TextView)v.findViewById(R.id.name);
- h.description = (TextView)v.findViewById(R.id.description);
- h.size = (TextView)v.findViewById(R.id.size);
- v.setTag(h);
- return v;
- }
-
- public void bindView(View view, int position) {
- synchronized (mState.mLock) {
- ViewHolder vh = (ViewHolder) view.getTag();
- if (position >= mItems.size()) {
- // List must have changed since we last reported its
- // size... ignore here, we will be doing a data changed
- // to refresh the entire list.
- return;
- }
- BaseItem item = mItems.get(position);
- vh.name.setText(item.mDisplayLabel);
- vh.separator.setVisibility(item.mNeedDivider
- ? View.VISIBLE : View.INVISIBLE);
- ActiveItem ai = new ActiveItem();
- ai.mRootView = view;
- ai.mItem = item;
- ai.mHolder = vh;
- ai.mFirstRunTime = item.mActiveSince;
- vh.description.setText(item.mDescription);
- if (item.mIsProcess) {
- view.setBackgroundColor(mProcessBgColor);
- vh.icon.setImageDrawable(null);
- vh.icon.setVisibility(View.GONE);
- vh.description.setText(item.mDescription);
- item.mCurSizeStr = null;
- } else {
- view.setBackgroundDrawable(null);
- vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager()));
- vh.icon.setVisibility(View.VISIBLE);
- vh.description.setText(item.mDescription);
- ai.mFirstRunTime = item.mActiveSince;
- }
- ai.updateTime(RunningServices.this);
- mActiveItems.put(view, ai);
- }
- }
- }
-
- public static class LinearColorBar extends LinearLayout {
- private float mRedRatio;
- private float mYellowRatio;
- private float mGreenRatio;
-
- final Rect mRect = new Rect();
- final Paint mPaint = new Paint();
-
- public LinearColorBar(Context context, AttributeSet attrs) {
- super(context, attrs);
- setWillNotDraw(false);
- mPaint.setStyle(Paint.Style.FILL);
- }
-
- public void setRatios(float red, float yellow, float green) {
- mRedRatio = red;
- mYellowRatio = yellow;
- mGreenRatio = green;
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- int width = getWidth();
- mRect.top = 0;
- mRect.bottom = getHeight();
-
- int left = 0;
-
- int right = left + (int)(width*mRedRatio);
- if (left < right) {
- mRect.left = left;
- mRect.right = right;
- mPaint.setColor(0xffff8080);
- canvas.drawRect(mRect, mPaint);
- width -= (right-left);
- left = right;
- }
-
- right = left + (int)(width*mYellowRatio);
- if (left < right) {
- mRect.left = left;
- mRect.right = right;
- mPaint.setColor(0xffffff00);
- canvas.drawRect(mRect, mPaint);
- width -= (right-left);
- left = right;
- }
-
- right = left + width;
- if (left < right) {
- mRect.left = left;
- mRect.right = right;
- mPaint.setColor(0xff80ff80);
- canvas.drawRect(mRect, mPaint);
- }
- }
- }
-
- HandlerThread mBackgroundThread;
- final class BackgroundHandler extends Handler {
- public BackgroundHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_CONTENTS:
- Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
- cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0;
- mHandler.sendMessage(cmd);
- removeMessages(MSG_UPDATE_CONTENTS);
- msg = obtainMessage(MSG_UPDATE_CONTENTS);
- sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
- break;
- }
- }
- };
- BackgroundHandler mBackgroundHandler;
-
- final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_TIMES:
- Iterator<ActiveItem> it = mActiveItems.values().iterator();
- while (it.hasNext()) {
- ActiveItem ai = it.next();
- if (ai.mRootView.getWindowToken() == null) {
- // Clean out any dead views, just in case.
- it.remove();
- continue;
- }
- ai.updateTime(RunningServices.this);
- }
- removeMessages(MSG_UPDATE_TIMES);
- msg = obtainMessage(MSG_UPDATE_TIMES);
- sendMessageDelayed(msg, TIME_UPDATE_DELAY);
- break;
- case MSG_REFRESH_UI:
- refreshUi(msg.arg1 != 0);
- break;
- }
- }
- };
-
- private boolean matchText(byte[] buffer, int index, String text) {
- int N = text.length();
- if ((index+N) >= buffer.length) {
- return false;
- }
- for (int i=0; i<N; i++) {
- if (buffer[index+i] != text.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- private long extractMemValue(byte[] buffer, int index) {
- while (index < buffer.length && buffer[index] != '\n') {
- if (buffer[index] >= '0' && buffer[index] <= '9') {
- int start = index;
- index++;
- while (index < buffer.length && buffer[index] >= '0'
- && buffer[index] <= '9') {
- index++;
- }
- String str = new String(buffer, 0, start, index-start);
- return ((long)Integer.parseInt(str)) * 1024;
- }
- index++;
- }
- return 0;
- }
-
- private long readAvailMem() {
- try {
- long memFree = 0;
- long memCached = 0;
- FileInputStream is = new FileInputStream("/proc/meminfo");
- int len = is.read(mBuffer);
- is.close();
- final int BUFLEN = mBuffer.length;
- for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
- if (matchText(mBuffer, i, "MemFree")) {
- i += 7;
- memFree = extractMemValue(mBuffer, i);
- } else if (matchText(mBuffer, i, "Cached")) {
- i += 6;
- memCached = extractMemValue(mBuffer, i);
- }
- while (i < BUFLEN && mBuffer[i] != '\n') {
- i++;
- }
- }
- return memFree + memCached;
- } catch (java.io.FileNotFoundException e) {
- } catch (java.io.IOException e) {
- }
- return 0;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- mState = (State)getLastNonConfigurationInstance();
- if (mState == null) {
- mState = new State();
- }
- mProcessBgColor = 0xff505050;
- setContentView(R.layout.running_services);
- getListView().setDivider(null);
- getListView().setAdapter(new ServiceListAdapter(mState));
- mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
- mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
- mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
-
- // Magic! Implementation detail! Don't count on this!
- SECONDARY_SERVER_MEM =
- Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
- }
-
- void refreshUi(boolean dataChanged) {
- if (dataChanged) {
- ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter());
- adapter.refreshItems();
- adapter.notifyDataSetChanged();
- }
-
- // This is the amount of available memory until we start killing
- // background services.
- long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
- if (availMem < 0) {
- availMem = 0;
- }
-
- synchronized (mState.mLock) {
- if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
- || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
- || mLastAvailMemory != availMem) {
- mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
- mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
- mLastAvailMemory = availMem;
- String availStr = availMem != 0
- ? Formatter.formatShortFileSize(this, availMem) : "0";
- String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory);
- mBackgroundProcessText.setText(getResources().getString(
- R.string.service_background_processes,
- mLastNumBackgroundProcesses, availStr, sizeStr));
- }
- if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
- || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
- mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
- mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
- String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory);
- mForegroundProcessText.setText(getResources().getString(
- R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr));
- }
- mLastNumServiceProcesses = mState.mNumServiceProcesses;
- mLastServiceProcessMemory = mState.mServiceProcessMemory;
-
- float totalMem = availMem + mLastBackgroundProcessMemory
- + mLastForegroundProcessMemory + mLastServiceProcessMemory;
- mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
- mLastServiceProcessMemory/totalMem,
- (availMem+mLastBackgroundProcessMemory)/totalMem);
- }
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- BaseItem bi = (BaseItem)l.getAdapter().getItem(position);
- if (!bi.mIsProcess) {
- ServiceItem si = (ServiceItem)bi;
- if (si.mRunningService.clientLabel != 0) {
- mCurSelected = null;
- PendingIntent pi = mAm.getRunningServiceControlPanel(
- si.mRunningService.service);
- if (pi != null) {
- try {
- this.startIntentSender(pi.getIntentSender(), null,
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
- Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
- } catch (IntentSender.SendIntentException e) {
- Log.w(TAG, e);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, e);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, e);
- }
- }
- } else {
- mCurSelected = bi;
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.confirm_stop_service);
- String msg = getResources().getString(
- R.string.confirm_stop_service_msg,
- si.mPackageInfo.loadLabel(getPackageManager()));
- builder.setMessage(msg);
- builder.setPositiveButton(R.string.confirm_stop_stop, this);
- builder.setNegativeButton(R.string.confirm_stop_cancel, null);
- builder.setCancelable(true);
- mCurDialog = builder.show();
- }
- } else {
- mCurSelected = null;
- }
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (mCurSelected != null) {
- stopService(new Intent().setComponent(
- ((ServiceItem)mCurSelected).mRunningService.service));
- if (mBackgroundHandler != null) {
- mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
- }
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mHandler.removeMessages(MSG_UPDATE_TIMES);
- if (mBackgroundThread != null) {
- mBackgroundThread.quit();
- mBackgroundThread = null;
- mBackgroundHandler = null;
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- refreshUi(mState.update(this, mAm));
- mBackgroundThread = new HandlerThread("RunningServices");
- mBackgroundThread.start();
- mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
- mHandler.removeMessages(MSG_UPDATE_TIMES);
- Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
- mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
- mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
- msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
- mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
- }
-
- @Override
- public Object onRetainNonConfigurationInstance() {
- return mState;
- }
-
- public void onMovedToScrapHeap(View view) {
- mActiveItems.remove(view);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mCurDialog != null) {
- mCurDialog.dismiss();
- }
- }
-}
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index 1348d48..05a655a 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -66,6 +66,10 @@ public class SecuritySettings extends PreferenceActivity {
private static final String KEY_VISIBLE_PATTERN = "visiblepattern";
private static final String KEY_TACTILE_FEEDBACK_ENABLED = "unlock_tactile_feedback";
+ // Encrypted File Systems constants
+ private static final String PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
+ private static final String PROPERTY_EFS_TRANSITION = "persist.security.efs.trans";
+
private CheckBoxPreference mVisiblePattern;
private CheckBoxPreference mTactileFeedback;
@@ -80,6 +84,9 @@ public class SecuritySettings extends PreferenceActivity {
// Credential storage
private CredentialStorage mCredentialStorage = new CredentialStorage();
+ // Encrypted file system
+ private CheckBoxPreference mEncryptedFSEnabled;
+
private CheckBoxPreference mNetwork;
private CheckBoxPreference mGps;
private CheckBoxPreference mAssistedGps;
@@ -209,6 +216,11 @@ public class SecuritySettings extends PreferenceActivity {
root.addPreference(credentialsCat);
mCredentialStorage.createPreferences(credentialsCat, CredentialStorage.TYPE_KEYSTORE);
+ // File System Encryption
+ PreferenceCategory encryptedfsCat = new PreferenceCategory(this);
+ encryptedfsCat.setTitle(R.string.encrypted_fs_category);
+ //root.addPreference(encryptedfsCat);
+ mCredentialStorage.createPreferences(encryptedfsCat, CredentialStorage.TYPE_ENCRYPTEDFS);
return root;
}
@@ -302,18 +314,21 @@ public class SecuritySettings extends PreferenceActivity {
private static final int MINIMUM_PASSWORD_LENGTH = 8;
private static final int TYPE_KEYSTORE = 0;
+ private static final int TYPE_ENCRYPTEDFS = 1;
// Dialog identifiers
private static final int DLG_BASE = 0;
private static final int DLG_UNLOCK = DLG_BASE + 1;
private static final int DLG_PASSWORD = DLG_UNLOCK + 1;
private static final int DLG_RESET = DLG_PASSWORD + 1;
+ private static final int DLG_ENABLE_EFS = DLG_RESET + 1;
private KeyStore mKeyStore = KeyStore.getInstance();
private int mState;
private boolean mSubmit = false;
private boolean mExternal = false;
+ private boolean mWillEnableEncryptedFS;
private int mShowingDialog = 0;
// Key Store controls
@@ -322,6 +337,10 @@ public class SecuritySettings extends PreferenceActivity {
private Preference mPasswordButton;
private Preference mResetButton;
+
+ // Encrypted file system controls
+ private CheckBoxPreference mEncryptedFSEnabled;
+
void resume() {
mState = mKeyStore.test();
updatePreferences(mState);
@@ -373,6 +392,10 @@ public class SecuritySettings extends PreferenceActivity {
lock();
}
return true;
+ } else if (preference == mEncryptedFSEnabled) {
+ Boolean bval = (Boolean)value;
+ mWillEnableEncryptedFS = bval.booleanValue();
+ showSwitchEncryptedFSDialog();
}
return true;
}
@@ -391,9 +414,26 @@ public class SecuritySettings extends PreferenceActivity {
}
public void onClick(DialogInterface dialog, int button) {
- mSubmit = (button == DialogInterface.BUTTON_POSITIVE);
- if (button == DialogInterface.BUTTON_NEUTRAL) {
- reset();
+ if (mShowingDialog != DLG_ENABLE_EFS) {
+ mSubmit = (button == DialogInterface.BUTTON_POSITIVE);
+ if (button == DialogInterface.BUTTON_NEUTRAL) {
+ reset();
+ }
+ } else {
+ if (button == DialogInterface.BUTTON_POSITIVE) {
+ Intent intent = new Intent("android.intent.action.MASTER_CLEAR");
+ intent.putExtra("enableEFS", mWillEnableEncryptedFS);
+ sendBroadcast(intent);
+ updatePreferences(mState);
+ } else if (button == DialogInterface.BUTTON_NEGATIVE) {
+ // Cancel action
+ Toast.makeText(SecuritySettings.this, R.string.encrypted_fs_cancel_confirm,
+ Toast.LENGTH_SHORT).show();
+ updatePreferences(mState);
+ } else {
+ // Unknown - should not happen
+ return;
+ }
}
}
@@ -507,16 +547,25 @@ public class SecuritySettings extends PreferenceActivity {
category.addPreference(mResetButton);
break;
+ case TYPE_ENCRYPTEDFS:
+ mEncryptedFSEnabled = new CheckBoxPreference(SecuritySettings.this);
+ mEncryptedFSEnabled.setTitle(R.string.encrypted_fs_enable);
+ mEncryptedFSEnabled.setSummary(R.string.encrypted_fs_enable_summary);
+ mEncryptedFSEnabled.setOnPreferenceChangeListener(this);
+ // category.addPreference(mEncryptedFSEnabled);
+ break;
}
}
private void updatePreferences(int state) {
mAccessCheckBox.setChecked(state == KeyStore.NO_ERROR);
-
- mResetButton.setEnabled(state != KeyStore.UNINITIALIZED);
- mAccessCheckBox.setEnabled(state != KeyStore.UNINITIALIZED);
+ boolean encFSEnabled = SystemProperties.getBoolean(PROPERTY_EFS_ENABLED,
+ false);
+ mResetButton.setEnabled((!encFSEnabled) && (state != KeyStore.UNINITIALIZED));
+ mAccessCheckBox.setEnabled((state != KeyStore.UNINITIALIZED) && (!encFSEnabled));
// Encrypted File system preferences
+ mEncryptedFSEnabled.setChecked(encFSEnabled);
// Show a toast message if the state is changed.
if (mState == state) {
@@ -586,5 +635,24 @@ public class SecuritySettings extends PreferenceActivity {
.setNegativeButton(getString(android.R.string.cancel), this)
.create().show();
}
+
+ private void showSwitchEncryptedFSDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(SecuritySettings.this)
+ .setCancelable(false)
+ .setTitle(R.string.encrypted_fs_alert_dialog_title);
+
+ mShowingDialog = DLG_ENABLE_EFS;
+ if (mWillEnableEncryptedFS) {
+ builder.setMessage(R.string.encrypted_fs_enable_dialog)
+ .setPositiveButton(R.string.encrypted_fs_enable_button, this)
+ .setNegativeButton(R.string.encrypted_fs_cancel_button, this)
+ .create().show();
+ } else {
+ builder.setMessage(R.string.encrypted_fs_disable_dialog)
+ .setPositiveButton(R.string.encrypted_fs_disable_button, this)
+ .setNegativeButton(R.string.encrypted_fs_cancel_button, this)
+ .create().show();
+ }
+ }
}
}
diff --git a/src/com/android/settings/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 0ca35b8..92fe9d2 100644
--- a/src/com/android/settings/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -16,10 +16,14 @@
* under the License.
*/
-package com.android.settings;
+package com.android.settings.applications;
import com.android.internal.content.PackageHelper;
import com.android.settings.R;
+import com.android.settings.R.id;
+import com.android.settings.R.layout;
+import com.android.settings.R.string;
+
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
@@ -312,7 +316,7 @@ public class InstalledAppDetails extends Activity implements View.OnClickListene
// Get application's name from intent
Intent intent = getIntent();
- final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME);
+ final String packageName = intent.getData().getSchemeSpecificPart();
if (! initAppInfo(packageName)) {
return; // could not find package, finish called
}
diff --git a/src/com/android/settings/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index dfff8c9..52ea376 100644
--- a/src/com/android/settings/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.applications;
import com.android.settings.R;
@@ -41,6 +41,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.text.format.Formatter;
import android.util.Log;
import android.view.LayoutInflater;
@@ -119,7 +120,6 @@ public class ManageApplications extends TabActivity implements
private static final boolean DEBUG_TIME = false;
// attributes used as keys when passing values to InstalledAppDetails activity
- public static final String APP_PKG_NAME = "pkg";
public static final String APP_CHG = "chg";
// attribute name used in receiver for tagging names of added/deleted packages
@@ -140,9 +140,8 @@ public class ManageApplications extends TabActivity implements
private static final int MENU_OPTIONS_BASE = 0;
// Filter options used for displayed list of applications
public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0;
- public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 1;
- public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 2;
- public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 3;
+ public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1;
+ public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2;
public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
@@ -220,6 +219,8 @@ public class ManageApplications extends TabActivity implements
private boolean mSizesFirst = false;
// ListView used to display list
private ListView mListView;
+ // Custom view used to display running processes
+ private RunningProcessesView mRunningProcessesView;
// State variables used to figure out menu options and also
// initiate the first computation and loading of resources
private boolean mJustCreated = true;
@@ -227,6 +228,14 @@ public class ManageApplications extends TabActivity implements
private long mLoadTimeStart;
private boolean mSetListViewLater = true;
+ // These are for keeping track of activity and tab switch state.
+ private int mCurView;
+ private boolean mCreatedRunning;
+
+ private boolean mResumedRunning;
+ private boolean mActivityResumed;
+ private Object mNonConfigInstance;
+
/*
* Handler class to handle messages for various operations.
* Most of the operations that effect Application related data
@@ -268,10 +277,8 @@ public class ManageApplications extends TabActivity implements
boolean status;
long size;
String formattedSize;
- ApplicationInfo info;
Bundle data;
String pkgName = null;
- AppInfo appInfo;
data = msg.getData();
if(data != null) {
pkgName = data.getString(ATTR_PKG_NAME);
@@ -359,12 +366,6 @@ public class ManageApplications extends TabActivity implements
}
break;
}
- try {
- info = mPm.getApplicationInfo(pkgName, 0);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Couldnt find application info for:"+pkgName);
- break;
- }
mObserver.invokeGetSizeInfo(pkgName);
break;
case ADD_PKG_DONE:
@@ -456,7 +457,6 @@ public class ManageApplications extends TabActivity implements
// Set the adapter here.
mJustCreated = false;
mListView.setAdapter(mAppInfoAdapter);
- dismissLoadingMsg();
}
}
@@ -645,32 +645,6 @@ public class ManageApplications extends TabActivity implements
}
}
return appList;
- } else if (filterOption == FILTER_APPS_RUNNING) {
- List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
- List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
- if ((procList == null) || (procList.size() == 0)) {
- return appList;
- }
- // Retrieve running processes from ActivityManager
- for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
- if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
- int size = appProcInfo.pkgList.length;
- for (int i = 0; i < size; i++) {
- ApplicationInfo appInfo = null;
- try {
- appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
- PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
- continue;
- }
- if(appInfo != null) {
- appList.add(appInfo);
- }
- }
- }
- }
- return appList;
} else {
return installedAppList;
}
@@ -728,31 +702,6 @@ public class ManageApplications extends TabActivity implements
}
}
return retList;
- } else if (filterOption == FILTER_APPS_RUNNING) {
- List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
- if ((procList == null) || (procList.size() == 0)) {
- return retList;
- }
- // Retrieve running processes from ActivityManager
- HashMap<String, ActivityManager.RunningAppProcessInfo> runningMap =
- new HashMap<String, ActivityManager.RunningAppProcessInfo>();
- for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
- if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
- int size = appProcInfo.pkgList.length;
- for (int i = 0; i < size; i++) {
- runningMap.put(appProcInfo.pkgList[i], appProcInfo);
- }
- }
- }
- // Query list to find running processes in current list
- for (ApplicationInfo appInfo : pAppList) {
- if (runningMap.get(appInfo.packageName) != null) {
- if (matchFilter(filter, filterMap, appInfo.packageName)) {
- retList.add(appInfo);
- }
- }
- }
- return retList;
} else {
for (ApplicationInfo appInfo : pAppList) {
if (matchFilter(filter, filterMap, appInfo.packageName)) {
@@ -763,11 +712,6 @@ public class ManageApplications extends TabActivity implements
}
}
- private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() {
- ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- return am.getRunningAppProcesses();
- }
-
// Some initialization code used when kicking off the size computation
private void initAppList(List<ApplicationInfo> appList, int filterOption) {
setProgressBarIndeterminateVisibility(true);
@@ -1261,14 +1205,7 @@ public class ManageApplications extends TabActivity implements
private boolean shouldBeInList(int filterOption, ApplicationInfo info) {
// Match filter here
- if (filterOption == FILTER_APPS_RUNNING) {
- List<ApplicationInfo> runningList = getInstalledApps(FILTER_APPS_RUNNING);
- for (ApplicationInfo running : runningList) {
- if (running.packageName.equalsIgnoreCase(info.packageName)) {
- return true;
- }
- }
- } else if (filterOption == FILTER_APPS_THIRD_PARTY) {
+ if (filterOption == FILTER_APPS_THIRD_PARTY) {
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
return true;
} else if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
@@ -1611,11 +1548,44 @@ public class ManageApplications extends TabActivity implements
}
}
+ static final int VIEW_NOTHING = 0;
+ static final int VIEW_LIST = 1;
+ static final int VIEW_RUNNING = 2;
+
+ private void selectView(int which) {
+ if (mCurView == which) {
+ return;
+ }
+
+ mCurView = which;
+
+ if (which == VIEW_LIST) {
+ if (mResumedRunning) {
+ mRunningProcessesView.doPause();
+ mResumedRunning = false;
+ }
+ mRunningProcessesView.setVisibility(View.GONE);
+ mListView.setVisibility(View.VISIBLE);
+ } else if (which == VIEW_RUNNING) {
+ if (!mCreatedRunning) {
+ mRunningProcessesView.doCreate(null, mNonConfigInstance);
+ mCreatedRunning = true;
+ }
+ if (mActivityResumed && !mResumedRunning) {
+ mRunningProcessesView.doResume();
+ mResumedRunning = true;
+ }
+ mRunningProcessesView.setVisibility(View.VISIBLE);
+ mListView.setVisibility(View.GONE);
+ }
+ }
+
static final String TAB_DOWNLOADED = "Downloaded";
static final String TAB_RUNNING = "Running";
static final String TAB_ALL = "All";
static final String TAB_SDCARD = "OnSdCard";
private View mRootView;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -1627,18 +1597,31 @@ public class ManageApplications extends TabActivity implements
Intent intent = getIntent();
String action = intent.getAction();
String defaultTabTag = TAB_DOWNLOADED;
+ if (intent.getComponent().getClassName().equals(
+ "com.android.settings.RunningServices")) {
+ defaultTabTag = TAB_RUNNING;
+ }
if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
mSortOrder = SORT_ORDER_SIZE;
mFilterApps = FILTER_APPS_ALL;
defaultTabTag = TAB_ALL;
mSizesFirst = true;
}
+
+ if (savedInstanceState != null) {
+ mSortOrder = savedInstanceState.getInt("sortOrder", mSortOrder);
+ mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps);
+ String tmp = savedInstanceState.getString("defaultTabTag");
+ if (tmp != null) defaultTabTag = tmp;
+ mSizesFirst = savedInstanceState.getBoolean("sizesFirst", mSizesFirst);
+ }
+
+ mNonConfigInstance = getLastNonConfigurationInstance();
+
mPm = getPackageManager();
// initialize some window features
requestWindowFeature(Window.FEATURE_RIGHT_ICON);
- requestWindowFeature(Window.FEATURE_PROGRESS);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- showLoadingMsg();
mDefaultAppIcon = Resources.getSystem().getDrawable(
com.android.internal.R.drawable.sym_def_app_icon);
mInvalidSizeStr = getText(R.string.invalid_size_value);
@@ -1658,6 +1641,8 @@ public class ManageApplications extends TabActivity implements
lv.setOnItemClickListener(this);
lv.setTextFilterEnabled(true);
mListView = lv;
+ mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
+ R.id.running_processes);
if (DEBUG_TIME) {
Log.i(TAG, "Total time in Activity.create:: " +
(SystemClock.elapsedRealtime() - sCreate)+ " ms");
@@ -1677,10 +1662,6 @@ public class ManageApplications extends TabActivity implements
.setIndicator(getString(R.string.filter_apps_third_party),
getResources().getDrawable(R.drawable.ic_tab_download))
.setContent(this));
- tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING)
- .setIndicator(getString(R.string.filter_apps_running),
- getResources().getDrawable(R.drawable.ic_tab_running))
- .setContent(this));
tabHost.addTab(tabHost.newTabSpec(TAB_ALL)
.setIndicator(getString(R.string.filter_apps_all),
getResources().getDrawable(R.drawable.ic_tab_all))
@@ -1689,43 +1670,102 @@ public class ManageApplications extends TabActivity implements
.setIndicator(getString(R.string.filter_apps_onsdcard),
getResources().getDrawable(R.drawable.ic_tab_sdcard))
.setContent(this));
+ tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING)
+ .setIndicator(getString(R.string.filter_apps_running),
+ getResources().getDrawable(R.drawable.ic_tab_running))
+ .setContent(this));
tabHost.setCurrentTabByTag(defaultTabTag);
tabHost.setOnTabChangedListener(this);
+
+ selectView(TAB_RUNNING.equals(defaultTabTag) ? VIEW_RUNNING : VIEW_LIST);
}
@Override
- protected void onDestroy() {
- // Persist values in cache
- mCache.updateCache();
- super.onDestroy();
+ public void onStart() {
+ super.onStart();
+ // Register receiver
+ mReceiver.registerReceiver();
+ sendMessageToHandler(INIT_PKG_INFO);
}
@Override
- public Dialog onCreateDialog(int id, Bundle args) {
- if (id == DLG_LOADING) {
- ProgressDialog dlg = new ProgressDialog(this);
- dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- dlg.setMessage(getText(R.string.loading));
- dlg.setIndeterminate(true);
- dlg.setOnCancelListener(this);
- return dlg;
- }
- return null;
+ protected void onResume() {
+ super.onResume();
+ mActivityResumed = true;
+ if (mCurView == VIEW_RUNNING) {
+ mRunningProcessesView.doResume();
+ mResumedRunning = true;
+ }
}
- private void showLoadingMsg() {
- if (DEBUG_TIME) {
- mLoadTimeStart = SystemClock.elapsedRealtime();
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("sortOrder", mSortOrder);
+ outState.putInt("filterApps", mFilterApps);
+ outState.putString("defautTabTag", getTabHost().getCurrentTabTag());
+ outState.putBoolean("sizesFirst", mSizesFirst);
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ return mRunningProcessesView.doRetainNonConfigurationInstance();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mActivityResumed = false;
+ if (mResumedRunning) {
+ mRunningProcessesView.doPause();
+ mResumedRunning = false;
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ // Stop the background threads
+ if (mResourceThread != null) {
+ mResourceThread.setAbort();
+ }
+ if (mSizeComputor != null) {
+ mSizeComputor.setAbort();
+ }
+ // clear all messages related to application list
+ clearMessagesInHandler();
+ // register receiver here
+ unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
+ // Refresh package attributes
+ try {
+ ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName,
+ PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException e) {
+ Bundle rData = new Bundle();
+ rData.putString(ATTR_PKG_NAME, mCurrentPkgName);
+ sendMessageToHandler(REMOVE_PKG, rData);
+ mCurrentPkgName = null;
+ }
}
- showDialog(DLG_LOADING);
- if(localLOGV) Log.i(TAG, "Displaying Loading message");
}
- private void dismissLoadingMsg() {
- if(localLOGV) Log.i(TAG, "Dismissing Loading message");
- dismissDialog(DLG_LOADING);
- if (DEBUG_TIME) Log.i(TAG, "Displayed loading message for "+
- (SystemClock.elapsedRealtime() - mLoadTimeStart) + " ms");
+ // Avoid the restart and pause when orientation changes
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onDestroy() {
+ // Persist values in cache
+ mCache.updateCache();
+ super.onDestroy();
}
class AppInfoCache {
@@ -1923,36 +1963,6 @@ public class ManageApplications extends TabActivity implements
}
}
- @Override
- public void onStart() {
- super.onStart();
- // Register receiver
- mReceiver.registerReceiver();
- sendMessageToHandler(INIT_PKG_INFO);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- // Stop the background threads
- if (mResourceThread != null) {
- mResourceThread.setAbort();
- }
- if (mSizeComputor != null) {
- mSizeComputor.setAbort();
- }
- // clear all messages related to application list
- clearMessagesInHandler();
- // register receiver here
- unregisterReceiver(mReceiver);
- }
-
- // Avoid the restart and pause when orientation changes
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- }
-
/*
* comparator class used to sort AppInfo objects based on size
*/
@@ -1994,9 +2004,8 @@ public class ManageApplications extends TabActivity implements
// utility method used to start sub activity
private void startApplicationDetailsActivity() {
// Create intent to start new activity
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setClass(this, InstalledAppDetails.class);
- intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.fromParts("package", mCurrentPkgName, null));
// start new activity to display extended information
startActivityForResult(intent, INSTALLED_APP_DETAILS);
}
@@ -2049,33 +2058,19 @@ public class ManageApplications extends TabActivity implements
int newOption;
if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_THIRD_PARTY;
- } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
- newOption = FILTER_APPS_RUNNING;
} else if (TAB_ALL.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_ALL;
} else if (TAB_SDCARD.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_SDCARD;
+ } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
+ selectView(VIEW_RUNNING);
+ return;
} else {
// Invalid option. Do nothing
return;
}
+
+ selectView(VIEW_LIST);
sendMessageToHandler(REORDER_LIST, newOption);
}
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode,
- Intent data) {
- if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
- // Refresh package attributes
- try {
- ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (NameNotFoundException e) {
- Bundle rData = new Bundle();
- rData.putString(ATTR_PKG_NAME, mCurrentPkgName);
- sendMessageToHandler(REMOVE_PKG, rData);
- mCurrentPkgName = null;
- }
- }
- }
}
diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java
new file mode 100644
index 0000000..9fbae6d
--- /dev/null
+++ b/src/com/android/settings/applications/RunningProcessesView.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.ActivityManager;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class RunningProcessesView extends FrameLayout
+ implements AdapterView.OnItemClickListener {
+
+ /** Maximum number of services to retrieve */
+ static final int MAX_SERVICES = 100;
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+
+ static final long TIME_UPDATE_DELAY = 1000;
+ static final long CONTENTS_UPDATE_DELAY = 2000;
+
+ // Memory pages are 4K.
+ static final long PAGE_SIZE = 4*1024;
+
+ long SECONDARY_SERVER_MEM;
+
+ final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
+
+ ActivityManager mAm;
+
+ RunningState mState;
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ RunningState.BaseItem mCurSelected;
+
+ ListView mListView;
+ LinearColorBar mColorBar;
+ TextView mBackgroundProcessText;
+ TextView mForegroundProcessText;
+
+ int mLastNumBackgroundProcesses = -1;
+ int mLastNumForegroundProcesses = -1;
+ int mLastNumServiceProcesses = -1;
+ long mLastBackgroundProcessMemory = -1;
+ long mLastForegroundProcessMemory = -1;
+ long mLastServiceProcessMemory = -1;
+ long mLastAvailMemory = -1;
+
+ Dialog mCurDialog;
+
+ byte[] mBuffer = new byte[1024];
+
+ public static class ActiveItem {
+ View mRootView;
+ RunningState.BaseItem mItem;
+ ActivityManager.RunningServiceInfo mService;
+ ViewHolder mHolder;
+ long mFirstRunTime;
+
+ void updateTime(Context context, StringBuilder builder) {
+ TextView uptimeView = null;
+
+ if (mItem instanceof RunningState.ServiceItem) {
+ // If we are displaying a service, then the service
+ // uptime goes at the top.
+ uptimeView = mHolder.size;
+
+ } else {
+ String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
+ if (!size.equals(mItem.mCurSizeStr)) {
+ mItem.mCurSizeStr = size;
+ mHolder.size.setText(size);
+ }
+
+ if (mItem instanceof RunningState.MergedItem) {
+ // This item represents both services and proceses,
+ // so show the service uptime below.
+ uptimeView = mHolder.uptime;
+ }
+ }
+
+ if (uptimeView != null) {
+ if (mItem.mActiveSince >= 0) {
+ uptimeView.setText(DateUtils.formatElapsedTime(builder,
+ (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
+ } else {
+ uptimeView.setText(context.getResources().getText(
+ R.string.service_restarting));
+ }
+ }
+ }
+ }
+
+ public static class ViewHolder {
+ public View rootView;
+ public ImageView icon;
+ public TextView name;
+ public TextView description;
+ public TextView size;
+ public TextView uptime;
+
+ public ViewHolder(View v) {
+ rootView = v;
+ icon = (ImageView)v.findViewById(R.id.icon);
+ name = (TextView)v.findViewById(R.id.name);
+ description = (TextView)v.findViewById(R.id.description);
+ size = (TextView)v.findViewById(R.id.size);
+ uptime = (TextView)v.findViewById(R.id.uptime);
+ v.setTag(this);
+ }
+
+ public ActiveItem bind(RunningState state, RunningState.BaseItem item,
+ StringBuilder builder) {
+ synchronized (state.mLock) {
+ name.setText(item.mDisplayLabel);
+ ActiveItem ai = new ActiveItem();
+ ai.mRootView = rootView;
+ ai.mItem = item;
+ ai.mHolder = this;
+ ai.mFirstRunTime = item.mActiveSince;
+ description.setText(item.mDescription);
+ item.mCurSizeStr = null;
+ icon.setImageDrawable(item.mPackageInfo.loadIcon(
+ rootView.getContext().getPackageManager()));
+ icon.setVisibility(View.VISIBLE);
+ ai.updateTime(rootView.getContext(), builder);
+ return ai;
+ }
+ }
+ }
+
+ static class TimeTicker extends TextView {
+ public TimeTicker(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ class ServiceListAdapter extends BaseAdapter {
+ final RunningState mState;
+ final LayoutInflater mInflater;
+ ArrayList<RunningState.MergedItem> mItems;
+
+ ServiceListAdapter(RunningState state) {
+ mState = state;
+ mInflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ refreshItems();
+ }
+
+ void refreshItems() {
+ ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
+ if (mItems != newItems) {
+ mItems = newItems;
+ }
+ if (mItems == null) {
+ mItems = new ArrayList<RunningState.MergedItem>();
+ }
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mItems.size();
+ }
+
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ public long getItemId(int position) {
+ return mItems.get(position).hashCode();
+ }
+
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ public boolean isEnabled(int position) {
+ return !mItems.get(position).mIsProcess;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (convertView == null) {
+ v = newView(parent);
+ } else {
+ v = convertView;
+ }
+ bindView(v, position);
+ return v;
+ }
+
+ public View newView(ViewGroup parent) {
+ View v = mInflater.inflate(R.layout.running_processes_item, parent, false);
+ new ViewHolder(v);
+ return v;
+ }
+
+ public void bindView(View view, int position) {
+ synchronized (mState.mLock) {
+ if (position >= mItems.size()) {
+ // List must have changed since we last reported its
+ // size... ignore here, we will be doing a data changed
+ // to refresh the entire list.
+ return;
+ }
+ ViewHolder vh = (ViewHolder) view.getTag();
+ RunningState.MergedItem item = mItems.get(position);
+ ActiveItem ai = vh.bind(mState, item, mBuilder);
+ mActiveItems.put(view, ai);
+ }
+ }
+ }
+
+ public static class LinearColorBar extends LinearLayout {
+ private float mRedRatio;
+ private float mYellowRatio;
+ private float mGreenRatio;
+
+ final Rect mRect = new Rect();
+ final Paint mPaint = new Paint();
+
+ public LinearColorBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+ mPaint.setStyle(Paint.Style.FILL);
+ }
+
+ public void setRatios(float red, float yellow, float green) {
+ mRedRatio = red;
+ mYellowRatio = yellow;
+ mGreenRatio = green;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int width = getWidth();
+ mRect.top = 0;
+ mRect.bottom = getHeight();
+
+ int left = 0;
+
+ int right = left + (int)(width*mRedRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffff8080);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + (int)(width*mYellowRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffffff00);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + width;
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xff80ff80);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+ }
+
+ HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ Iterator<ActiveItem> it = mActiveItems.values().iterator();
+ while (it.hasNext()) {
+ ActiveItem ai = it.next();
+ if (ai.mRootView.getWindowToken() == null) {
+ // Clean out any dead views, just in case.
+ it.remove();
+ continue;
+ }
+ ai.updateTime(getContext(), mBuilder);
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ break;
+ case MSG_REFRESH_UI:
+ refreshUi(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ private boolean matchText(byte[] buffer, int index, String text) {
+ int N = text.length();
+ if ((index+N) >= buffer.length) {
+ return false;
+ }
+ for (int i=0; i<N; i++) {
+ if (buffer[index+i] != text.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private long extractMemValue(byte[] buffer, int index) {
+ while (index < buffer.length && buffer[index] != '\n') {
+ if (buffer[index] >= '0' && buffer[index] <= '9') {
+ int start = index;
+ index++;
+ while (index < buffer.length && buffer[index] >= '0'
+ && buffer[index] <= '9') {
+ index++;
+ }
+ String str = new String(buffer, 0, start, index-start);
+ return ((long)Integer.parseInt(str)) * 1024;
+ }
+ index++;
+ }
+ return 0;
+ }
+
+ private long readAvailMem() {
+ try {
+ long memFree = 0;
+ long memCached = 0;
+ FileInputStream is = new FileInputStream("/proc/meminfo");
+ int len = is.read(mBuffer);
+ is.close();
+ final int BUFLEN = mBuffer.length;
+ for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
+ if (matchText(mBuffer, i, "MemFree")) {
+ i += 7;
+ memFree = extractMemValue(mBuffer, i);
+ } else if (matchText(mBuffer, i, "Cached")) {
+ i += 6;
+ memCached = extractMemValue(mBuffer, i);
+ }
+ while (i < BUFLEN && mBuffer[i] != '\n') {
+ i++;
+ }
+ }
+ return memFree + memCached;
+ } catch (java.io.FileNotFoundException e) {
+ } catch (java.io.IOException e) {
+ }
+ return 0;
+ }
+
+
+ void refreshUi(boolean dataChanged) {
+ if (dataChanged) {
+ ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
+ adapter.refreshItems();
+ adapter.notifyDataSetChanged();
+ }
+
+ // This is the amount of available memory until we start killing
+ // background services.
+ long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
+ if (availMem < 0) {
+ availMem = 0;
+ }
+
+ synchronized (mState.mLock) {
+ if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
+ || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
+ || mLastAvailMemory != availMem) {
+ mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
+ mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
+ mLastAvailMemory = availMem;
+ String sizeStr = Formatter.formatShortFileSize(getContext(),
+ mLastAvailMemory + mLastBackgroundProcessMemory);
+ mBackgroundProcessText.setText(getResources().getString(
+ R.string.service_background_processes, sizeStr));
+ }
+ if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
+ || mLastForegroundProcessMemory != mState.mForegroundProcessMemory
+ || mLastNumServiceProcesses != mState.mNumServiceProcesses
+ || mLastServiceProcessMemory != mState.mServiceProcessMemory) {
+ mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
+ mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
+ mLastNumServiceProcesses = mState.mNumServiceProcesses;
+ mLastServiceProcessMemory = mState.mServiceProcessMemory;
+ String sizeStr = Formatter.formatShortFileSize(getContext(),
+ mLastForegroundProcessMemory + mLastServiceProcessMemory);
+ mForegroundProcessText.setText(getResources().getString(
+ R.string.service_foreground_processes, sizeStr));
+ }
+
+ float totalMem = availMem + mLastBackgroundProcessMemory
+ + mLastForegroundProcessMemory + mLastServiceProcessMemory;
+ mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
+ mLastServiceProcessMemory/totalMem,
+ (availMem+mLastBackgroundProcessMemory)/totalMem);
+ }
+ }
+
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ ListView l = (ListView)parent;
+ RunningState.MergedItem mi = (RunningState.MergedItem)l.getAdapter().getItem(position);
+ mCurSelected = mi;
+ Intent intent = new Intent();
+ intent.putExtra(RunningServiceDetails.KEY_UID, mi.mProcess.mUid);
+ intent.putExtra(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName);
+ intent.setClass(getContext(), RunningServiceDetails.class);
+ getContext().startActivity(intent);
+ }
+
+ public void onMovedToScrapHeap(View view) {
+ mActiveItems.remove(view);
+ }
+
+ public RunningProcessesView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
+ mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ mState = (RunningState)nonConfigurationInstace;
+ if (mState == null) {
+ mState = new RunningState();
+ }
+ LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.running_processes_view, this);
+ mListView = (ListView)findViewById(android.R.id.list);
+ View emptyView = findViewById(com.android.internal.R.id.empty);
+ if (emptyView != null) {
+ mListView.setEmptyView(emptyView);
+ }
+ mListView.setOnItemClickListener(this);
+ mListView.setAdapter(new ServiceListAdapter(mState));
+ mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
+ mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
+ mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
+
+ // Magic! Implementation detail! Don't count on this!
+ SECONDARY_SERVER_MEM =
+ Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
+ }
+
+ public void doPause() {
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quit();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ }
+ }
+
+ public void doResume() {
+ refreshUi(mState.update(getContext(), mAm));
+ mBackgroundThread = new HandlerThread("RunningServices");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ }
+
+ public Object doRetainNonConfigurationInstance() {
+ return mState;
+ }
+}
diff --git a/src/com/android/settings/applications/RunningProcessesViewOld.java b/src/com/android/settings/applications/RunningProcessesViewOld.java
new file mode 100644
index 0000000..dc7302c
--- /dev/null
+++ b/src/com/android/settings/applications/RunningProcessesViewOld.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class RunningProcessesViewOld extends FrameLayout
+ implements AdapterView.OnItemClickListener {
+
+ static final String TAG = "RunningServices";
+
+ /** Maximum number of services to retrieve */
+ static final int MAX_SERVICES = 100;
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+
+ static final long TIME_UPDATE_DELAY = 1000;
+ static final long CONTENTS_UPDATE_DELAY = 2000;
+
+ // Memory pages are 4K.
+ static final long PAGE_SIZE = 4*1024;
+
+ long SECONDARY_SERVER_MEM;
+
+ final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
+
+ ActivityManager mAm;
+
+ RunningState mState;
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ RunningState.BaseItem mCurSelected;
+
+ int mProcessBgColor;
+
+ ListView mListView;
+ LinearColorBar mColorBar;
+ TextView mBackgroundProcessText;
+ TextView mForegroundProcessText;
+
+ int mLastNumBackgroundProcesses = -1;
+ int mLastNumForegroundProcesses = -1;
+ int mLastNumServiceProcesses = -1;
+ long mLastBackgroundProcessMemory = -1;
+ long mLastForegroundProcessMemory = -1;
+ long mLastServiceProcessMemory = -1;
+ long mLastAvailMemory = -1;
+
+ Dialog mCurDialog;
+
+ byte[] mBuffer = new byte[1024];
+
+ class ActiveItem {
+ View mRootView;
+ RunningState.BaseItem mItem;
+ ActivityManager.RunningServiceInfo mService;
+ ViewHolder mHolder;
+ long mFirstRunTime;
+
+ void updateTime(Context context) {
+ if (mItem.mIsProcess) {
+ String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
+ if (!size.equals(mItem.mCurSizeStr)) {
+ mItem.mCurSizeStr = size;
+ mHolder.size.setText(size);
+ }
+ } else {
+ if (mItem.mActiveSince >= 0) {
+ mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
+ (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
+ } else {
+ mHolder.size.setText(context.getResources().getText(
+ R.string.service_restarting));
+ }
+ }
+ }
+ }
+
+ static class ViewHolder {
+ ImageView separator;
+ ImageView icon;
+ TextView name;
+ TextView description;
+ TextView size;
+ }
+
+ static class TimeTicker extends TextView {
+ public TimeTicker(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ class ServiceListAdapter extends BaseAdapter {
+ final RunningState mState;
+ final LayoutInflater mInflater;
+ ArrayList<RunningState.BaseItem> mItems;
+
+ ServiceListAdapter(RunningState state) {
+ mState = state;
+ mInflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ refreshItems();
+ }
+
+ void refreshItems() {
+ ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
+ if (mItems != newItems) {
+ mItems = newItems;
+ }
+ if (mItems == null) {
+ mItems = new ArrayList<RunningState.BaseItem>();
+ }
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mItems.size();
+ }
+
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ public long getItemId(int position) {
+ return mItems.get(position).hashCode();
+ }
+
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ public boolean isEnabled(int position) {
+ return !mItems.get(position).mIsProcess;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (convertView == null) {
+ v = newView(parent);
+ } else {
+ v = convertView;
+ }
+ bindView(v, position);
+ return v;
+ }
+
+ public View newView(ViewGroup parent) {
+ View v = mInflater.inflate(R.layout.running_services_item, parent, false);
+ ViewHolder h = new ViewHolder();
+ h.separator = (ImageView)v.findViewById(R.id.separator);
+ h.icon = (ImageView)v.findViewById(R.id.icon);
+ h.name = (TextView)v.findViewById(R.id.name);
+ h.description = (TextView)v.findViewById(R.id.description);
+ h.size = (TextView)v.findViewById(R.id.size);
+ v.setTag(h);
+ return v;
+ }
+
+ public void bindView(View view, int position) {
+ synchronized (mState.mLock) {
+ ViewHolder vh = (ViewHolder) view.getTag();
+ if (position >= mItems.size()) {
+ // List must have changed since we last reported its
+ // size... ignore here, we will be doing a data changed
+ // to refresh the entire list.
+ return;
+ }
+ RunningState.BaseItem item = mItems.get(position);
+ vh.name.setText(item.mDisplayLabel);
+ vh.separator.setVisibility(item.mNeedDivider
+ ? View.VISIBLE : View.INVISIBLE);
+ ActiveItem ai = new ActiveItem();
+ ai.mRootView = view;
+ ai.mItem = item;
+ ai.mHolder = vh;
+ ai.mFirstRunTime = item.mActiveSince;
+ vh.description.setText(item.mDescription);
+ if (item.mIsProcess) {
+ view.setBackgroundColor(mProcessBgColor);
+ vh.icon.setImageDrawable(null);
+ vh.icon.setVisibility(View.GONE);
+ vh.description.setText(item.mDescription);
+ item.mCurSizeStr = null;
+ } else {
+ view.setBackgroundDrawable(null);
+ vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(
+ getContext().getPackageManager()));
+ vh.icon.setVisibility(View.VISIBLE);
+ vh.description.setText(item.mDescription);
+ ai.mFirstRunTime = item.mActiveSince;
+ }
+ ai.updateTime(getContext());
+ mActiveItems.put(view, ai);
+ }
+ }
+ }
+
+ public static class LinearColorBar extends LinearLayout {
+ private float mRedRatio;
+ private float mYellowRatio;
+ private float mGreenRatio;
+
+ final Rect mRect = new Rect();
+ final Paint mPaint = new Paint();
+
+ public LinearColorBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+ mPaint.setStyle(Paint.Style.FILL);
+ }
+
+ public void setRatios(float red, float yellow, float green) {
+ mRedRatio = red;
+ mYellowRatio = yellow;
+ mGreenRatio = green;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int width = getWidth();
+ mRect.top = 0;
+ mRect.bottom = getHeight();
+
+ int left = 0;
+
+ int right = left + (int)(width*mRedRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffff8080);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + (int)(width*mYellowRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffffff00);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + width;
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xff80ff80);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+ }
+
+ HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ Iterator<ActiveItem> it = mActiveItems.values().iterator();
+ while (it.hasNext()) {
+ ActiveItem ai = it.next();
+ if (ai.mRootView.getWindowToken() == null) {
+ // Clean out any dead views, just in case.
+ it.remove();
+ continue;
+ }
+ ai.updateTime(getContext());
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ break;
+ case MSG_REFRESH_UI:
+ refreshUi(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ private boolean matchText(byte[] buffer, int index, String text) {
+ int N = text.length();
+ if ((index+N) >= buffer.length) {
+ return false;
+ }
+ for (int i=0; i<N; i++) {
+ if (buffer[index+i] != text.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private long extractMemValue(byte[] buffer, int index) {
+ while (index < buffer.length && buffer[index] != '\n') {
+ if (buffer[index] >= '0' && buffer[index] <= '9') {
+ int start = index;
+ index++;
+ while (index < buffer.length && buffer[index] >= '0'
+ && buffer[index] <= '9') {
+ index++;
+ }
+ String str = new String(buffer, 0, start, index-start);
+ return ((long)Integer.parseInt(str)) * 1024;
+ }
+ index++;
+ }
+ return 0;
+ }
+
+ private long readAvailMem() {
+ try {
+ long memFree = 0;
+ long memCached = 0;
+ FileInputStream is = new FileInputStream("/proc/meminfo");
+ int len = is.read(mBuffer);
+ is.close();
+ final int BUFLEN = mBuffer.length;
+ for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
+ if (matchText(mBuffer, i, "MemFree")) {
+ i += 7;
+ memFree = extractMemValue(mBuffer, i);
+ } else if (matchText(mBuffer, i, "Cached")) {
+ i += 6;
+ memCached = extractMemValue(mBuffer, i);
+ }
+ while (i < BUFLEN && mBuffer[i] != '\n') {
+ i++;
+ }
+ }
+ return memFree + memCached;
+ } catch (java.io.FileNotFoundException e) {
+ } catch (java.io.IOException e) {
+ }
+ return 0;
+ }
+
+
+ void refreshUi(boolean dataChanged) {
+ if (dataChanged) {
+ ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
+ adapter.refreshItems();
+ adapter.notifyDataSetChanged();
+ }
+
+ // This is the amount of available memory until we start killing
+ // background services.
+ long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
+ if (availMem < 0) {
+ availMem = 0;
+ }
+
+ synchronized (mState.mLock) {
+ if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
+ || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
+ || mLastAvailMemory != availMem) {
+ mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
+ mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
+ mLastAvailMemory = availMem;
+ String sizeStr = Formatter.formatShortFileSize(getContext(),
+ mLastAvailMemory + mLastBackgroundProcessMemory);
+ mBackgroundProcessText.setText(getResources().getString(
+ R.string.service_background_processes, sizeStr));
+ }
+ if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
+ || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
+ mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
+ mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
+ String sizeStr = Formatter.formatShortFileSize(getContext(),
+ mLastForegroundProcessMemory);
+ mForegroundProcessText.setText(getResources().getString(
+ R.string.service_foreground_processes, sizeStr));
+ }
+ mLastNumServiceProcesses = mState.mNumServiceProcesses;
+ mLastServiceProcessMemory = mState.mServiceProcessMemory;
+
+ float totalMem = availMem + mLastBackgroundProcessMemory
+ + mLastForegroundProcessMemory + mLastServiceProcessMemory;
+ mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
+ mLastServiceProcessMemory/totalMem,
+ (availMem+mLastBackgroundProcessMemory)/totalMem);
+ }
+ }
+
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ ListView l = (ListView)parent;
+ RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
+ mCurSelected = bi;
+ }
+
+ public void onMovedToScrapHeap(View view) {
+ mActiveItems.remove(view);
+ }
+
+ public RunningProcessesViewOld(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
+ mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ mState = (RunningState)nonConfigurationInstace;
+ if (mState == null) {
+ mState = new RunningState();
+ }
+ mProcessBgColor = 0xff505050;
+ LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.running_processes_view, this);
+ mListView = (ListView)findViewById(android.R.id.list);
+ View emptyView = findViewById(com.android.internal.R.id.empty);
+ if (emptyView != null) {
+ mListView.setEmptyView(emptyView);
+ }
+ mListView.setOnItemClickListener(this);
+ mListView.setDivider(null);
+ mListView.setAdapter(new ServiceListAdapter(mState));
+ mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
+ mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
+ mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
+
+ // Magic! Implementation detail! Don't count on this!
+ SECONDARY_SERVER_MEM =
+ Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
+ }
+
+ public void doPause() {
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quit();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ }
+ }
+
+ public void doResume() {
+ refreshUi(mState.update(getContext(), mAm));
+ mBackgroundThread = new HandlerThread("RunningServices");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ }
+
+ public Object doRetainNonConfigurationInstance() {
+ return mState;
+ }
+}
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
new file mode 100644
index 0000000..f33625e
--- /dev/null
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -0,0 +1,390 @@
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RunningServiceDetails extends Activity {
+ static final String TAG = "RunningServicesDetails";
+
+ static final String KEY_UID = "uid";
+ static final String KEY_PROCESS = "process";
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+
+ ActivityManager mAm;
+ LayoutInflater mInflater;
+
+ RunningState mState;
+
+ int mUid;
+ String mProcessName;
+
+ RunningState.MergedItem mMergedItem;
+
+ ViewGroup mAllDetails;
+ ViewGroup mSnippet;
+ RunningProcessesView.ActiveItem mSnippetActiveItem;
+ RunningProcessesView.ViewHolder mSnippetViewHolder;
+
+ TextView mServicesHeader;
+ TextView mProcessesHeader;
+ final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
+
+ class ActiveDetail implements View.OnClickListener {
+ View mRootView;
+ RunningProcessesView.ActiveItem mActiveItem;
+ RunningProcessesView.ViewHolder mViewHolder;
+ PendingIntent mManageIntent;
+
+ public void onClick(View v) {
+ if (mManageIntent != null) {
+ try {
+ startIntentSender(mManageIntent.getIntentSender(), null,
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
+ Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Log.w(TAG, e);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, e);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, e);
+ }
+ } else {
+ RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
+ stopService(new Intent().setComponent(si.mRunningService.service));
+ if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
+ // If there was only one service, we are finishing it,
+ // so no reason for the UI to stick around.
+ finish();
+ } else {
+ if (mBackgroundHandler != null) {
+ mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
+ }
+ }
+ }
+ }
+ }
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ if (mSnippetActiveItem != null) {
+ mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
+ }
+ for (int i=0; i<mActiveDetails.size(); i++) {
+ mActiveDetails.get(i).mActiveItem.updateTime(
+ RunningServiceDetails.this, mBuilder);
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
+ break;
+ case MSG_REFRESH_UI:
+ refreshUi(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ boolean findMergedItem() {
+ RunningState.MergedItem item = null;
+ ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
+ if (newItems != null) {
+ for (int i=0; i<newItems.size(); i++) {
+ RunningState.MergedItem mi = newItems.get(i);
+ if (mi.mProcess.mUid == mUid
+ && mi.mProcess.mProcessName.equals(mProcessName)) {
+ item = mi;
+ break;
+ }
+ }
+ }
+ if (mMergedItem != item) {
+ mMergedItem = item;
+ return true;
+ }
+ return false;
+ }
+
+ void addDetailViews() {
+ for (int i=mActiveDetails.size()-1; i>=0; i--) {
+ mAllDetails.removeView(mActiveDetails.get(i).mRootView);
+ }
+ mActiveDetails.clear();
+
+ if (mServicesHeader != null) {
+ mAllDetails.removeView(mServicesHeader);
+ mServicesHeader = null;
+ }
+
+ if (mProcessesHeader != null) {
+ mAllDetails.removeView(mProcessesHeader);
+ mProcessesHeader = null;
+ }
+
+ if (mMergedItem != null) {
+ for (int i=0; i<mMergedItem.mServices.size(); i++) {
+ if (i == 0) {
+ mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
+ mAllDetails, false);
+ mServicesHeader.setText(R.string.runningservicedetails_services_title);
+ mAllDetails.addView(mServicesHeader);
+ }
+ RunningState.ServiceItem si = mMergedItem.mServices.get(i);
+ ActiveDetail detail = new ActiveDetail();
+ View root = mInflater.inflate(R.layout.running_service_details_service,
+ mAllDetails, false);
+ mAllDetails.addView(root);
+ detail.mRootView = root;
+ detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
+ detail.mActiveItem = detail.mViewHolder.bind(mState, si, mBuilder);
+
+ if (si.mRunningService.clientLabel != 0) {
+ detail.mManageIntent = mAm.getRunningServiceControlPanel(
+ si.mRunningService.service);
+ }
+
+ TextView description = (TextView)root.findViewById(R.id.comp_description);
+ if (si.mServiceInfo.descriptionRes != 0) {
+ description.setText(getPackageManager().getText(
+ si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
+ si.mServiceInfo.applicationInfo));
+ } else {
+ if (detail.mManageIntent != null) {
+ try {
+ Resources clientr = getPackageManager().getResourcesForApplication(
+ si.mRunningService.clientPackage);
+ String label = clientr.getString(si.mRunningService.clientLabel);
+ description.setText(getString(R.string.service_manage_description,
+ label));
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ } else {
+ description.setText(getText(R.string.service_stop_description));
+ }
+ }
+
+ View button = root.findViewById(R.id.right_button);
+ button.setOnClickListener(detail);
+ ((TextView)button).setText(getText(detail.mManageIntent != null
+ ? R.string.service_manage : R.string.service_stop));
+ root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
+
+ mActiveDetails.add(detail);
+ }
+
+ boolean didProcess = false;
+ for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
+ RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
+ : mMergedItem.mOtherProcesses.get(i);
+ if (pi.mPid <= 0) {
+ continue;
+ }
+
+ if (!didProcess) {
+ mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
+ mAllDetails, false);
+ mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
+ mAllDetails.addView(mProcessesHeader);
+ didProcess = true;
+ }
+ ActiveDetail detail = new ActiveDetail();
+ View root = mInflater.inflate(R.layout.running_service_details_process,
+ mAllDetails, false);
+ mAllDetails.addView(root);
+ detail.mRootView = root;
+ detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
+ detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
+
+ TextView description = (TextView)root.findViewById(R.id.comp_description);
+ if (i < 0) {
+ description.setText(R.string.main_running_process_description);
+ } else {
+ int textid = 0;
+ CharSequence label = null;
+ ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
+ final ComponentName comp = rpi.importanceReasonComponent;
+ //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
+ // + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
+ switch (rpi.importanceReasonCode) {
+ case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
+ textid = R.string.process_provider_in_use_description;
+ List<ProviderInfo> providers = null;
+ if (comp != null) {
+ providers = getPackageManager()
+ .queryContentProviders(comp.getPackageName(),
+ rpi.uid, 0);
+ }
+ if (providers != null) {
+ for (int j=0; j<providers.size(); j++) {
+ ProviderInfo prov = providers.get(i);
+ if (comp.getClassName().equals(prov.name)) {
+ label = RunningState.makeLabel(getPackageManager(),
+ prov.name, prov);
+ break;
+ }
+ }
+ }
+ break;
+ case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
+ textid = R.string.process_service_in_use_description;
+ if (rpi.importanceReasonComponent != null) {
+ try {
+ ServiceInfo serv = getPackageManager().getServiceInfo(
+ rpi.importanceReasonComponent, 0);
+ label = RunningState.makeLabel(getPackageManager(),
+ serv.name, serv);
+ } catch (NameNotFoundException e) {
+ }
+ }
+ break;
+ }
+ if (textid != 0 && label != null) {
+ description.setText(getString(textid, label));
+ }
+ }
+
+ mActiveDetails.add(detail);
+ }
+ }
+ }
+
+ void refreshUi(boolean dataChanged) {
+ if (findMergedItem()) {
+ dataChanged = true;
+ }
+ if (dataChanged) {
+ if (mMergedItem != null) {
+ mSnippetActiveItem = mSnippetViewHolder.bind(mState,
+ mMergedItem, mBuilder);
+ } else if (mSnippetActiveItem != null) {
+ // Clear whatever is currently being shown.
+ mSnippetActiveItem.mHolder.size.setText("");
+ mSnippetActiveItem.mHolder.uptime.setText("");
+ mSnippetActiveItem.mHolder.description.setText(R.string.no_services);
+ } else {
+ // No merged item, never had one. Nothing to do.
+ finish();
+ return;
+ }
+ addDetailViews();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUid = getIntent().getIntExtra(KEY_UID, 0);
+ mProcessName = getIntent().getStringExtra(KEY_PROCESS);
+
+ mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ mState = (RunningState)getLastNonConfigurationInstance();
+ if (mState == null) {
+ mState = new RunningState();
+ }
+
+ setContentView(R.layout.running_service_details);
+
+ mAllDetails = (ViewGroup)findViewById(R.id.all_details);
+ mSnippet = (ViewGroup)findViewById(R.id.snippet);
+ mSnippet.setBackgroundResource(com.android.internal.R.drawable.title_bar_medium);
+ mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom());
+ mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quit();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ refreshUi(mState.update(this, mAm));
+ mBackgroundThread = new HandlerThread("RunningServices");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ return mState;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ }
+}
diff --git a/src/com/android/settings/applications/RunningServices.java b/src/com/android/settings/applications/RunningServices.java
new file mode 100644
index 0000000..703e8c7
--- /dev/null
+++ b/src/com/android/settings/applications/RunningServices.java
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class RunningServices extends ListActivity
+ implements AbsListView.RecyclerListener,
+ DialogInterface.OnClickListener {
+ static final String TAG = "RunningServices";
+
+ /** Maximum number of services to retrieve */
+ static final int MAX_SERVICES = 100;
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+
+ static final long TIME_UPDATE_DELAY = 1000;
+ static final long CONTENTS_UPDATE_DELAY = 2000;
+
+ // Memory pages are 4K.
+ static final long PAGE_SIZE = 4*1024;
+
+ long SECONDARY_SERVER_MEM;
+
+ final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
+
+ ActivityManager mAm;
+
+ RunningState mState;
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ RunningState.BaseItem mCurSelected;
+
+ int mProcessBgColor;
+
+ LinearColorBar mColorBar;
+ TextView mBackgroundProcessText;
+ TextView mForegroundProcessText;
+
+ int mLastNumBackgroundProcesses = -1;
+ int mLastNumForegroundProcesses = -1;
+ int mLastNumServiceProcesses = -1;
+ long mLastBackgroundProcessMemory = -1;
+ long mLastForegroundProcessMemory = -1;
+ long mLastServiceProcessMemory = -1;
+ long mLastAvailMemory = -1;
+
+ Dialog mCurDialog;
+
+ byte[] mBuffer = new byte[1024];
+
+ class ActiveItem {
+ View mRootView;
+ RunningState.BaseItem mItem;
+ ActivityManager.RunningServiceInfo mService;
+ ViewHolder mHolder;
+ long mFirstRunTime;
+
+ void updateTime(Context context) {
+ if (mItem.mIsProcess) {
+ String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
+ if (!size.equals(mItem.mCurSizeStr)) {
+ mItem.mCurSizeStr = size;
+ mHolder.size.setText(size);
+ }
+ } else {
+ if (mItem.mActiveSince >= 0) {
+ mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
+ (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
+ } else {
+ mHolder.size.setText(context.getResources().getText(
+ R.string.service_restarting));
+ }
+ }
+ }
+ }
+
+ static class ViewHolder {
+ ImageView separator;
+ ImageView icon;
+ TextView name;
+ TextView description;
+ TextView size;
+ }
+
+ static class TimeTicker extends TextView {
+ public TimeTicker(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ class ServiceListAdapter extends BaseAdapter {
+ final RunningState mState;
+ final LayoutInflater mInflater;
+ ArrayList<RunningState.BaseItem> mItems;
+
+ ServiceListAdapter(RunningState state) {
+ mState = state;
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ refreshItems();
+ }
+
+ void refreshItems() {
+ ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
+ if (mItems != newItems) {
+ mItems = newItems;
+ }
+ if (mItems == null) {
+ mItems = new ArrayList<RunningState.BaseItem>();
+ }
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mItems.size();
+ }
+
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ public long getItemId(int position) {
+ return mItems.get(position).hashCode();
+ }
+
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ public boolean isEnabled(int position) {
+ return !mItems.get(position).mIsProcess;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (convertView == null) {
+ v = newView(parent);
+ } else {
+ v = convertView;
+ }
+ bindView(v, position);
+ return v;
+ }
+
+ public View newView(ViewGroup parent) {
+ View v = mInflater.inflate(R.layout.running_services_item, parent, false);
+ ViewHolder h = new ViewHolder();
+ h.separator = (ImageView)v.findViewById(R.id.separator);
+ h.icon = (ImageView)v.findViewById(R.id.icon);
+ h.name = (TextView)v.findViewById(R.id.name);
+ h.description = (TextView)v.findViewById(R.id.description);
+ h.size = (TextView)v.findViewById(R.id.size);
+ v.setTag(h);
+ return v;
+ }
+
+ public void bindView(View view, int position) {
+ synchronized (mState.mLock) {
+ ViewHolder vh = (ViewHolder) view.getTag();
+ if (position >= mItems.size()) {
+ // List must have changed since we last reported its
+ // size... ignore here, we will be doing a data changed
+ // to refresh the entire list.
+ return;
+ }
+ RunningState.BaseItem item = mItems.get(position);
+ vh.name.setText(item.mDisplayLabel);
+ vh.separator.setVisibility(item.mNeedDivider
+ ? View.VISIBLE : View.INVISIBLE);
+ ActiveItem ai = new ActiveItem();
+ ai.mRootView = view;
+ ai.mItem = item;
+ ai.mHolder = vh;
+ ai.mFirstRunTime = item.mActiveSince;
+ vh.description.setText(item.mDescription);
+ if (item.mIsProcess) {
+ view.setBackgroundColor(mProcessBgColor);
+ vh.icon.setImageDrawable(null);
+ vh.icon.setVisibility(View.GONE);
+ vh.description.setText(item.mDescription);
+ item.mCurSizeStr = null;
+ } else {
+ view.setBackgroundDrawable(null);
+ vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager()));
+ vh.icon.setVisibility(View.VISIBLE);
+ vh.description.setText(item.mDescription);
+ ai.mFirstRunTime = item.mActiveSince;
+ }
+ ai.updateTime(RunningServices.this);
+ mActiveItems.put(view, ai);
+ }
+ }
+ }
+
+ public static class LinearColorBar extends LinearLayout {
+ private float mRedRatio;
+ private float mYellowRatio;
+ private float mGreenRatio;
+
+ final Rect mRect = new Rect();
+ final Paint mPaint = new Paint();
+
+ public LinearColorBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+ mPaint.setStyle(Paint.Style.FILL);
+ }
+
+ public void setRatios(float red, float yellow, float green) {
+ mRedRatio = red;
+ mYellowRatio = yellow;
+ mGreenRatio = green;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int width = getWidth();
+ mRect.top = 0;
+ mRect.bottom = getHeight();
+
+ int left = 0;
+
+ int right = left + (int)(width*mRedRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffff8080);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + (int)(width*mYellowRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffffff00);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + width;
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xff80ff80);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+ }
+
+ HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ Iterator<ActiveItem> it = mActiveItems.values().iterator();
+ while (it.hasNext()) {
+ ActiveItem ai = it.next();
+ if (ai.mRootView.getWindowToken() == null) {
+ // Clean out any dead views, just in case.
+ it.remove();
+ continue;
+ }
+ ai.updateTime(RunningServices.this);
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ break;
+ case MSG_REFRESH_UI:
+ refreshUi(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ private boolean matchText(byte[] buffer, int index, String text) {
+ int N = text.length();
+ if ((index+N) >= buffer.length) {
+ return false;
+ }
+ for (int i=0; i<N; i++) {
+ if (buffer[index+i] != text.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private long extractMemValue(byte[] buffer, int index) {
+ while (index < buffer.length && buffer[index] != '\n') {
+ if (buffer[index] >= '0' && buffer[index] <= '9') {
+ int start = index;
+ index++;
+ while (index < buffer.length && buffer[index] >= '0'
+ && buffer[index] <= '9') {
+ index++;
+ }
+ String str = new String(buffer, 0, start, index-start);
+ return ((long)Integer.parseInt(str)) * 1024;
+ }
+ index++;
+ }
+ return 0;
+ }
+
+ private long readAvailMem() {
+ try {
+ long memFree = 0;
+ long memCached = 0;
+ FileInputStream is = new FileInputStream("/proc/meminfo");
+ int len = is.read(mBuffer);
+ is.close();
+ final int BUFLEN = mBuffer.length;
+ for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
+ if (matchText(mBuffer, i, "MemFree")) {
+ i += 7;
+ memFree = extractMemValue(mBuffer, i);
+ } else if (matchText(mBuffer, i, "Cached")) {
+ i += 6;
+ memCached = extractMemValue(mBuffer, i);
+ }
+ while (i < BUFLEN && mBuffer[i] != '\n') {
+ i++;
+ }
+ }
+ return memFree + memCached;
+ } catch (java.io.FileNotFoundException e) {
+ } catch (java.io.IOException e) {
+ }
+ return 0;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ mState = (RunningState)getLastNonConfigurationInstance();
+ if (mState == null) {
+ mState = new RunningState();
+ }
+ mProcessBgColor = 0xff505050;
+ setContentView(R.layout.running_services);
+ getListView().setDivider(null);
+ getListView().setAdapter(new ServiceListAdapter(mState));
+ mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
+ mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
+ mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
+
+ // Magic! Implementation detail! Don't count on this!
+ SECONDARY_SERVER_MEM =
+ Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
+ }
+
+ void refreshUi(boolean dataChanged) {
+ if (dataChanged) {
+ ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter());
+ adapter.refreshItems();
+ adapter.notifyDataSetChanged();
+ }
+
+ // This is the amount of available memory until we start killing
+ // background services.
+ long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
+ if (availMem < 0) {
+ availMem = 0;
+ }
+
+ synchronized (mState.mLock) {
+ if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
+ || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
+ || mLastAvailMemory != availMem) {
+ mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
+ mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
+ mLastAvailMemory = availMem;
+ String availStr = availMem != 0
+ ? Formatter.formatShortFileSize(this, availMem) : "0";
+ String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory);
+ mBackgroundProcessText.setText(getResources().getString(
+ R.string.service_background_processes,
+ mLastNumBackgroundProcesses, availStr, sizeStr));
+ }
+ if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
+ || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
+ mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
+ mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
+ String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory);
+ mForegroundProcessText.setText(getResources().getString(
+ R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr));
+ }
+ mLastNumServiceProcesses = mState.mNumServiceProcesses;
+ mLastServiceProcessMemory = mState.mServiceProcessMemory;
+
+ float totalMem = availMem + mLastBackgroundProcessMemory
+ + mLastForegroundProcessMemory + mLastServiceProcessMemory;
+ mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
+ mLastServiceProcessMemory/totalMem,
+ (availMem+mLastBackgroundProcessMemory)/totalMem);
+ }
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
+ if (!bi.mIsProcess) {
+ RunningState.ServiceItem si = (RunningState.ServiceItem)bi;
+ if (si.mRunningService.clientLabel != 0) {
+ mCurSelected = null;
+ PendingIntent pi = mAm.getRunningServiceControlPanel(
+ si.mRunningService.service);
+ if (pi != null) {
+ try {
+ this.startIntentSender(pi.getIntentSender(), null,
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
+ Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Log.w(TAG, e);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, e);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, e);
+ }
+ }
+ } else {
+ mCurSelected = bi;
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.confirm_stop_service);
+ String msg = getResources().getString(
+ R.string.confirm_stop_service_msg,
+ si.mPackageInfo.loadLabel(getPackageManager()));
+ builder.setMessage(msg);
+ builder.setPositiveButton(R.string.confirm_stop_stop, this);
+ builder.setNegativeButton(R.string.confirm_stop_cancel, null);
+ builder.setCancelable(true);
+ mCurDialog = builder.show();
+ }
+ } else {
+ mCurSelected = null;
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (mCurSelected != null) {
+ stopService(new Intent().setComponent(
+ ((RunningState.ServiceItem)mCurSelected).mRunningService.service));
+ if (mBackgroundHandler != null) {
+ mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
+ }
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quit();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ refreshUi(mState.update(this, mAm));
+ mBackgroundThread = new HandlerThread("RunningServices");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ return mState;
+ }
+
+ public void onMovedToScrapHeap(View view) {
+ mActiveItems.remove(view);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mCurDialog != null) {
+ mCurDialog.dismiss();
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java
new file mode 100644
index 0000000..11b40be
--- /dev/null
+++ b/src/com/android/settings/applications/RunningState.java
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.os.Debug;
+import android.os.RemoteException;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Singleton for retrieving and monitoring the state about all running
+ * applications/processes/services.
+ */
+public class RunningState {
+
+ final SparseArray<HashMap<String, ProcessItem>> mProcesses
+ = new SparseArray<HashMap<String, ProcessItem>>();
+ final SparseArray<ProcessItem> mActiveProcesses
+ = new SparseArray<ProcessItem>();
+ final ServiceProcessComparator mServiceProcessComparator
+ = new ServiceProcessComparator();
+
+ // Temporary for finding process dependencies.
+ final SparseArray<ProcessItem> mRunningProcesses
+ = new SparseArray<ProcessItem>();
+
+ final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
+ final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
+
+ int mSequence = 0;
+
+ // ----- following protected by mLock -----
+
+ // Lock for protecting the state that will be shared between the
+ // background update thread and the UI thread.
+ final Object mLock = new Object();
+
+ ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
+ ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
+
+ int mNumBackgroundProcesses;
+ long mBackgroundProcessMemory;
+ int mNumForegroundProcesses;
+ long mForegroundProcessMemory;
+ int mNumServiceProcesses;
+ long mServiceProcessMemory;
+
+ static class BaseItem {
+ final boolean mIsProcess;
+
+ PackageItemInfo mPackageInfo;
+ CharSequence mDisplayLabel;
+ String mLabel;
+ String mDescription;
+
+ int mCurSeq;
+
+ long mActiveSince;
+ long mSize;
+ String mSizeStr;
+ String mCurSizeStr;
+ boolean mNeedDivider;
+
+ public BaseItem(boolean isProcess) {
+ mIsProcess = isProcess;
+ }
+ }
+
+ static class ServiceItem extends BaseItem {
+ ActivityManager.RunningServiceInfo mRunningService;
+ ServiceInfo mServiceInfo;
+ boolean mShownAsStarted;
+
+ MergedItem mMergedItem;
+
+ public ServiceItem() {
+ super(false);
+ }
+ }
+
+ static class ProcessItem extends BaseItem {
+ final HashMap<ComponentName, ServiceItem> mServices
+ = new HashMap<ComponentName, ServiceItem>();
+ final SparseArray<ProcessItem> mDependentProcesses
+ = new SparseArray<ProcessItem>();
+
+ final int mUid;
+ final String mProcessName;
+ int mPid;
+
+ ProcessItem mClient;
+ int mLastNumDependentProcesses;
+
+ int mRunningSeq;
+ ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
+
+ // Purely for sorting.
+ boolean mIsSystem;
+ boolean mIsStarted;
+ long mActiveSince;
+
+ public ProcessItem(Context context, int uid, String processName) {
+ super(true);
+ mDescription = context.getResources().getString(
+ R.string.service_process_name, processName);
+ mUid = uid;
+ mProcessName = processName;
+ }
+
+ void ensureLabel(PackageManager pm) {
+ if (mLabel != null) {
+ return;
+ }
+
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0);
+ if (ai.uid == mUid) {
+ mDisplayLabel = ai.loadLabel(pm);
+ mLabel = mDisplayLabel.toString();
+ mPackageInfo = ai;
+ return;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+
+ // If we couldn't get information about the overall
+ // process, try to find something about the uid.
+ String[] pkgs = pm.getPackagesForUid(mUid);
+
+ // If there is one package with this uid, that is what we want.
+ if (pkgs.length == 1) {
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
+ mDisplayLabel = ai.loadLabel(pm);
+ mLabel = mDisplayLabel.toString();
+ mPackageInfo = ai;
+ return;
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ // If there are multiple, see if one gives us the official name
+ // for this uid.
+ for (String name : pkgs) {
+ try {
+ PackageInfo pi = pm.getPackageInfo(name, 0);
+ if (pi.sharedUserLabel != 0) {
+ CharSequence nm = pm.getText(name,
+ pi.sharedUserLabel, pi.applicationInfo);
+ if (nm != null) {
+ mDisplayLabel = nm;
+ mLabel = nm.toString();
+ mPackageInfo = pi.applicationInfo;
+ return;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ // If still don't have anything to display, just use the
+ // service info.
+ if (mServices.size() > 0) {
+ mPackageInfo = mServices.values().iterator().next()
+ .mServiceInfo.applicationInfo;
+ mDisplayLabel = mPackageInfo.loadLabel(pm);
+ mLabel = mDisplayLabel.toString();
+ return;
+ }
+
+ // Finally... whatever, just pick the first package's name.
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
+ mDisplayLabel = ai.loadLabel(pm);
+ mLabel = mDisplayLabel.toString();
+ mPackageInfo = ai;
+ return;
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ boolean updateService(Context context,
+ ActivityManager.RunningServiceInfo service) {
+ final PackageManager pm = context.getPackageManager();
+
+ boolean changed = false;
+ ServiceItem si = mServices.get(service.service);
+ if (si == null) {
+ changed = true;
+ si = new ServiceItem();
+ si.mRunningService = service;
+ try {
+ si.mServiceInfo = pm.getServiceInfo(service.service, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ si.mDisplayLabel = makeLabel(pm,
+ si.mRunningService.service.getClassName(), si.mServiceInfo);
+ mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
+ si.mPackageInfo = si.mServiceInfo.applicationInfo;
+ mServices.put(service.service, si);
+ }
+ si.mCurSeq = mCurSeq;
+ si.mRunningService = service;
+ long activeSince = service.restarting == 0 ? service.activeSince : -1;
+ if (si.mActiveSince != activeSince) {
+ si.mActiveSince = activeSince;
+ changed = true;
+ }
+ if (service.clientPackage != null && service.clientLabel != 0) {
+ if (si.mShownAsStarted) {
+ si.mShownAsStarted = false;
+ changed = true;
+ }
+ try {
+ Resources clientr = pm.getResourcesForApplication(service.clientPackage);
+ String label = clientr.getString(service.clientLabel);
+ si.mDescription = context.getResources().getString(
+ R.string.service_client_name, label);
+ } catch (PackageManager.NameNotFoundException e) {
+ si.mDescription = null;
+ }
+ } else {
+ if (!si.mShownAsStarted) {
+ si.mShownAsStarted = true;
+ changed = true;
+ }
+ si.mDescription = context.getResources().getString(
+ R.string.service_started_by_app);
+ }
+
+ return changed;
+ }
+
+ boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) {
+ mSize = ((long)mem.getTotalPss()) * 1024;
+ if (mCurSeq == curSeq) {
+ String sizeStr = Formatter.formatShortFileSize(
+ context, mSize);
+ if (!sizeStr.equals(mSizeStr)){
+ mSizeStr = sizeStr;
+ // We update this on the second tick where we update just
+ // the text in the current items, so no need to say we
+ // changed here.
+ return false;
+ }
+ }
+ return false;
+ }
+
+ boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
+ final int NP = mDependentProcesses.size();
+ boolean changed = false;
+ for (int i=0; i<NP; i++) {
+ ProcessItem proc = mDependentProcesses.valueAt(i);
+ if (proc.mClient != this) {
+ changed = true;
+ proc.mClient = this;
+ }
+ proc.mCurSeq = curSeq;
+ proc.ensureLabel(pm);
+ changed |= proc.buildDependencyChain(context, pm, curSeq);
+ }
+
+ if (mLastNumDependentProcesses != mDependentProcesses.size()) {
+ changed = true;
+ mLastNumDependentProcesses = mDependentProcesses.size();
+ }
+
+ return changed;
+ }
+
+ void addDependentProcesses(ArrayList<BaseItem> dest,
+ ArrayList<ProcessItem> destProc) {
+ final int NP = mDependentProcesses.size();
+ for (int i=0; i<NP; i++) {
+ ProcessItem proc = mDependentProcesses.valueAt(i);
+ proc.addDependentProcesses(dest, destProc);
+ dest.add(proc);
+ if (proc.mPid > 0) {
+ destProc.add(proc);
+ }
+ }
+ }
+ }
+
+ static class MergedItem extends BaseItem {
+ ProcessItem mProcess;
+ final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
+ final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
+
+ MergedItem() {
+ super(false);
+ }
+
+ boolean update(Context context) {
+ mPackageInfo = mProcess.mPackageInfo;
+ mDisplayLabel = mProcess.mDisplayLabel;
+ mLabel = mProcess.mLabel;
+
+ int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size();
+ int numServices = mServices.size();
+ int resid = R.string.running_processes_item_description_s_s;
+ if (numProcesses != 1) {
+ resid = numServices != 1
+ ? R.string.running_processes_item_description_p_p
+ : R.string.running_processes_item_description_p_s;
+ } else if (numServices != 1) {
+ resid = R.string.running_processes_item_description_s_p;
+ }
+ mDescription = context.getResources().getString(resid, numProcesses, numServices);
+
+ mActiveSince = -1;
+ for (int i=0; i<mServices.size(); i++) {
+ ServiceItem si = mServices.get(i);
+ if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
+ mActiveSince = si.mActiveSince;
+ }
+ }
+
+ return false;
+ }
+
+ boolean updateSize(Context context) {
+ mSize = mProcess.mSize;
+ for (int i=0; i<mOtherProcesses.size(); i++) {
+ mSize += mOtherProcesses.get(i).mSize;
+ }
+
+ String sizeStr = Formatter.formatShortFileSize(
+ context, mSize);
+ if (!sizeStr.equals(mSizeStr)){
+ mSizeStr = sizeStr;
+ // We update this on the second tick where we update just
+ // the text in the current items, so no need to say we
+ // changed here.
+ return false;
+ }
+ return false;
+ }
+ }
+
+ static class ServiceProcessComparator implements Comparator<ProcessItem> {
+ public int compare(ProcessItem object1, ProcessItem object2) {
+ if (object1.mIsStarted != object2.mIsStarted) {
+ // Non-started processes go last.
+ return object1.mIsStarted ? -1 : 1;
+ }
+ if (object1.mIsSystem != object2.mIsSystem) {
+ // System processes go below non-system.
+ return object1.mIsSystem ? 1 : -1;
+ }
+ if (object1.mActiveSince != object2.mActiveSince) {
+ // Remaining ones are sorted with the longest running
+ // services last.
+ return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
+ }
+ return 0;
+ }
+ }
+
+ static CharSequence makeLabel(PackageManager pm,
+ String className, PackageItemInfo item) {
+ if (item != null && (item.labelRes != 0
+ || item.nonLocalizedLabel != null)) {
+ CharSequence label = item.loadLabel(pm);
+ if (label != null) {
+ return label;
+ }
+ }
+
+ String label = className;
+ int tail = label.lastIndexOf('.');
+ if (tail >= 0) {
+ label = label.substring(tail+1, label.length());
+ }
+ return label;
+ }
+
+ boolean update(Context context, ActivityManager am) {
+ final PackageManager pm = context.getPackageManager();
+
+ mSequence++;
+
+ boolean changed = false;
+
+ List<ActivityManager.RunningServiceInfo> services
+ = am.getRunningServices(RunningServices.MAX_SERVICES);
+ final int NS = services != null ? services.size() : 0;
+ for (int i=0; i<NS; i++) {
+ ActivityManager.RunningServiceInfo si = services.get(i);
+ // We are not interested in services that have not been started
+ // and don't have a known client, because
+ // there is nothing the user can do about them.
+ if (!si.started && si.clientLabel == 0) {
+ continue;
+ }
+ // We likewise don't care about services running in a
+ // persistent process like the system or phone.
+ if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
+ != 0) {
+ continue;
+ }
+
+ HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
+ if (procs == null) {
+ procs = new HashMap<String, ProcessItem>();
+ mProcesses.put(si.uid, procs);
+ }
+ ProcessItem proc = procs.get(si.process);
+ if (proc == null) {
+ changed = true;
+ proc = new ProcessItem(context, si.uid, si.process);
+ procs.put(si.process, proc);
+ }
+
+ if (proc.mCurSeq != mSequence) {
+ int pid = si.restarting == 0 ? si.pid : 0;
+ if (pid != proc.mPid) {
+ changed = true;
+ if (proc.mPid != pid) {
+ if (proc.mPid != 0) {
+ mActiveProcesses.remove(proc.mPid);
+ }
+ if (pid != 0) {
+ mActiveProcesses.put(pid, proc);
+ }
+ proc.mPid = pid;
+ }
+ }
+ proc.mDependentProcesses.clear();
+ proc.mCurSeq = mSequence;
+ }
+ changed |= proc.updateService(context, si);
+ }
+
+ // Now update the map of other processes that are running (but
+ // don't have services actively running inside them).
+ List<ActivityManager.RunningAppProcessInfo> processes
+ = am.getRunningAppProcesses();
+ final int NP = processes != null ? processes.size() : 0;
+ for (int i=0; i<NP; i++) {
+ ActivityManager.RunningAppProcessInfo pi = processes.get(i);
+ ProcessItem proc = mActiveProcesses.get(pi.pid);
+ if (proc == null) {
+ // This process is not one that is a direct container
+ // of a service, so look for it in the secondary
+ // running list.
+ proc = mRunningProcesses.get(pi.pid);
+ if (proc == null) {
+ proc = new ProcessItem(context, pi.uid, pi.processName);
+ proc.mPid = pi.pid;
+ mRunningProcesses.put(pi.pid, proc);
+ }
+ proc.mDependentProcesses.clear();
+ }
+ proc.mRunningSeq = mSequence;
+ proc.mRunningProcessInfo = pi;
+ }
+
+ // Build the chains from client processes to the process they are
+ // dependent on; also remove any old running processes.
+ int NRP = mRunningProcesses.size();
+ for (int i=0; i<NRP; i++) {
+ ProcessItem proc = mRunningProcesses.valueAt(i);
+ if (proc.mRunningSeq == mSequence) {
+ int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
+ if (clientPid != 0) {
+ ProcessItem client = mActiveProcesses.get(clientPid);
+ if (client == null) {
+ client = mRunningProcesses.get(clientPid);
+ }
+ if (client != null) {
+ client.mDependentProcesses.put(proc.mPid, proc);
+ }
+ } else {
+ // In this pass the process doesn't have a client.
+ // Clear to make sure if it later gets the same one
+ // that we will detect the change.
+ proc.mClient = null;
+ }
+ } else {
+ mRunningProcesses.remove(mRunningProcesses.keyAt(i));
+ }
+ }
+
+ // Follow the tree from all primary service processes to all
+ // processes they are dependent on, marking these processes as
+ // still being active and determining if anything has changed.
+ final int NAP = mActiveProcesses.size();
+ for (int i=0; i<NAP; i++) {
+ ProcessItem proc = mActiveProcesses.valueAt(i);
+ if (proc.mCurSeq == mSequence) {
+ changed |= proc.buildDependencyChain(context, pm, mSequence);
+ }
+ }
+
+ // Look for services and their primary processes that no longer exist...
+ for (int i=0; i<mProcesses.size(); i++) {
+ HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
+ Iterator<ProcessItem> pit = procs.values().iterator();
+ while (pit.hasNext()) {
+ ProcessItem pi = pit.next();
+ if (pi.mCurSeq == mSequence) {
+ pi.ensureLabel(pm);
+ if (pi.mPid == 0) {
+ // Sanity: a non-process can't be dependent on
+ // anything.
+ pi.mDependentProcesses.clear();
+ }
+ } else {
+ changed = true;
+ pit.remove();
+ if (procs.size() == 0) {
+ mProcesses.remove(mProcesses.keyAt(i));
+ }
+ if (pi.mPid != 0) {
+ mActiveProcesses.remove(pi.mPid);
+ }
+ continue;
+ }
+ Iterator<ServiceItem> sit = pi.mServices.values().iterator();
+ while (sit.hasNext()) {
+ ServiceItem si = sit.next();
+ if (si.mCurSeq != mSequence) {
+ changed = true;
+ sit.remove();
+ }
+ }
+ }
+ }
+
+ if (changed) {
+ // First determine an order for the services.
+ ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
+ for (int i=0; i<mProcesses.size(); i++) {
+ for (ProcessItem pi : mProcesses.valueAt(i).values()) {
+ pi.mIsSystem = false;
+ pi.mIsStarted = true;
+ pi.mActiveSince = Long.MAX_VALUE;
+ for (ServiceItem si : pi.mServices.values()) {
+ if (si.mServiceInfo != null
+ && (si.mServiceInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ pi.mIsSystem = true;
+ }
+ if (si.mRunningService != null
+ && si.mRunningService.clientLabel != 0) {
+ pi.mIsStarted = false;
+ if (pi.mActiveSince > si.mRunningService.activeSince) {
+ pi.mActiveSince = si.mRunningService.activeSince;
+ }
+ }
+ }
+ sortedProcesses.add(pi);
+ }
+ }
+
+ Collections.sort(sortedProcesses, mServiceProcessComparator);
+
+ ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
+ ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
+ mProcessItems.clear();
+ for (int i=0; i<sortedProcesses.size(); i++) {
+ ProcessItem pi = sortedProcesses.get(i);
+ pi.mNeedDivider = false;
+
+ int firstProc = mProcessItems.size();
+ // First add processes we are dependent on.
+ pi.addDependentProcesses(newItems, mProcessItems);
+ // And add the process itself.
+ newItems.add(pi);
+ if (pi.mPid > 0) {
+ mProcessItems.add(pi);
+ }
+
+ // Now add the services running in it.
+ MergedItem mergedItem = null;
+ boolean haveAllMerged = false;
+ boolean needDivider = false;
+ for (ServiceItem si : pi.mServices.values()) {
+ si.mNeedDivider = needDivider;
+ needDivider = true;
+ newItems.add(si);
+ if (si.mMergedItem != null) {
+ if (mergedItem != null && mergedItem != si.mMergedItem) {
+ haveAllMerged = false;
+ }
+ mergedItem = si.mMergedItem;
+ } else {
+ haveAllMerged = false;
+ }
+ }
+
+ if (!haveAllMerged || mergedItem == null
+ || mergedItem.mServices.size() != pi.mServices.size()) {
+ // Whoops, we need to build a new MergedItem!
+ mergedItem = new MergedItem();
+ for (ServiceItem si : pi.mServices.values()) {
+ mergedItem.mServices.add(si);
+ si.mMergedItem = mergedItem;
+ }
+ mergedItem.mProcess = pi;
+ mergedItem.mOtherProcesses.clear();
+ for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
+ mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
+ }
+ }
+
+ mergedItem.update(context);
+ newMergedItems.add(mergedItem);
+ }
+ synchronized (mLock) {
+ mItems = newItems;
+ mMergedItems = newMergedItems;
+ }
+ }
+
+ // Count number of interesting other (non-active) processes, and
+ // build a list of all processes we will retrieve memory for.
+ mAllProcessItems.clear();
+ mAllProcessItems.addAll(mProcessItems);
+ int numBackgroundProcesses = 0;
+ int numForegroundProcesses = 0;
+ int numServiceProcesses = 0;
+ NRP = mRunningProcesses.size();
+ for (int i=0; i<NRP; i++) {
+ ProcessItem proc = mRunningProcesses.valueAt(i);
+ if (proc.mCurSeq != mSequence) {
+ // We didn't hit this process as a dependency on one
+ // of our active ones, so add it up if needed.
+ if (proc.mRunningProcessInfo.importance >=
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+ numBackgroundProcesses++;
+ mAllProcessItems.add(proc);
+ } else if (proc.mRunningProcessInfo.importance <=
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
+ numForegroundProcesses++;
+ mAllProcessItems.add(proc);
+ } else {
+ Log.i(RunningServices.TAG, "Unknown non-service process: "
+ + proc.mProcessName + " #" + proc.mPid);
+ }
+ } else {
+ numServiceProcesses++;
+ }
+ }
+
+ long backgroundProcessMemory = 0;
+ long foregroundProcessMemory = 0;
+ long serviceProcessMemory = 0;
+ try {
+ final int numProc = mAllProcessItems.size();
+ int[] pids = new int[numProc];
+ for (int i=0; i<numProc; i++) {
+ pids[i] = mAllProcessItems.get(i).mPid;
+ }
+ Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault()
+ .getProcessMemoryInfo(pids);
+ for (int i=pids.length-1; i>=0; i--) {
+ ProcessItem proc = mAllProcessItems.get(i);
+ changed |= proc.updateSize(context, mem[i], mSequence);
+ if (proc.mCurSeq == mSequence) {
+ serviceProcessMemory += proc.mSize;
+ } else if (proc.mRunningProcessInfo.importance >=
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+ backgroundProcessMemory += proc.mSize;
+ } else if (proc.mRunningProcessInfo.importance <=
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
+ foregroundProcessMemory += proc.mSize;
+ }
+ }
+ } catch (RemoteException e) {
+ }
+
+ for (int i=0; i<mMergedItems.size(); i++) {
+ mMergedItems.get(i).updateSize(context);
+ }
+
+ synchronized (mLock) {
+ mNumBackgroundProcesses = numBackgroundProcesses;
+ mNumForegroundProcesses = numForegroundProcesses;
+ mNumServiceProcesses = numServiceProcesses;
+ mBackgroundProcessMemory = backgroundProcessMemory;
+ mForegroundProcessMemory = foregroundProcessMemory;
+ mServiceProcessMemory = serviceProcessMemory;
+ }
+
+ return changed;
+ }
+
+ ArrayList<BaseItem> getCurrentItems() {
+ synchronized (mLock) {
+ return mItems;
+ }
+ }
+
+ ArrayList<MergedItem> getCurrentMergedItems() {
+ synchronized (mLock) {
+ return mMergedItems;
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
index aa4a958..48fd85d 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
@@ -35,11 +35,7 @@ import android.view.MenuItem;
import com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
-import java.text.DateFormat;
import java.util.ArrayList;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -90,219 +86,39 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
// See mConnectAttempted
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
- // Max time to hold the work queue if we don't get or missed a response
- // from the bt framework.
- private static final long MAX_WAIT_TIME_FOR_FRAMEWORK = 25 * 1000;
-
- private enum BluetoothCommand {
- CONNECT, DISCONNECT, REMOVE_BOND,
- }
-
- static class BluetoothJob {
- final BluetoothCommand command; // CONNECT, DISCONNECT
- final CachedBluetoothDevice cachedDevice;
- final Profile profile; // HEADSET, A2DP, etc
- // 0 means this command was not been sent to the bt framework.
- long timeSent;
-
- public BluetoothJob(BluetoothCommand command,
- CachedBluetoothDevice cachedDevice, Profile profile) {
- this.command = command;
- this.cachedDevice = cachedDevice;
- this.profile = profile;
- this.timeSent = 0;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(command.name());
- sb.append(" Address:").append(cachedDevice.mDevice);
- if (profile != null) {
- sb.append(" Profile:").append(profile.name());
- }
- sb.append(" TimeSent:");
- if (timeSent == 0) {
- sb.append("not yet");
- } else {
- sb.append(DateFormat.getTimeInstance().format(new Date(timeSent)));
- }
- return sb.toString();
- }
- }
-
+
/**
- * We want to serialize connect and disconnect calls. http://b/170538
- * This are some headsets that may have L2CAP resource limitation. We want
- * to limit the bt bandwidth usage.
- *
- * A queue to keep track of asynchronous calls to the bt framework. The
- * first item, if exist, should be in progress i.e. went to the bt framework
- * already, waiting for a notification to come back. The second item and
- * beyond have not been sent to the bt framework yet.
+ * Describes the current device and profile for logging.
+ *
+ * @param profile Profile to describe
+ * @return Description of the device and profile
*/
- private static LinkedList<BluetoothJob> workQueue = new LinkedList<BluetoothJob>();
-
- private void queueCommand(BluetoothJob job) {
- synchronized (workQueue) {
- if (D) {
- Log.d(TAG, workQueue.toString());
- }
- boolean processNow = pruneQueue(job);
-
- // Add job to queue
- if (D) {
- Log.d(TAG, "Adding: " + job.toString());
- }
- workQueue.add(job);
-
- // if there's nothing pending from before, send the command to bt
- // framework immediately.
- if (workQueue.size() == 1 || processNow) {
- // If the failed to process, just drop it from the queue.
- // There will be no callback to remove this from the queue.
- processCommands();
- }
+ private String describe(CachedBluetoothDevice cachedDevice, Profile profile) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Address:").append(cachedDevice.mDevice);
+ if (profile != null) {
+ sb.append(" Profile:").append(profile.name());
}
- }
-
- private boolean pruneQueue(BluetoothJob job) {
- boolean removedStaleItems = false;
- long now = System.currentTimeMillis();
- Iterator<BluetoothJob> it = workQueue.iterator();
- while (it.hasNext()) {
- BluetoothJob existingJob = it.next();
- // Remove any pending CONNECTS when we receive a DISCONNECT
- if (job != null && job.command == BluetoothCommand.DISCONNECT) {
- if (existingJob.timeSent == 0
- && existingJob.command == BluetoothCommand.CONNECT
- && existingJob.cachedDevice.mDevice.equals(job.cachedDevice.mDevice)
- && existingJob.profile == job.profile) {
- if (D) {
- Log.d(TAG, "Removed because of a pending disconnect. " + existingJob);
- }
- it.remove();
- continue;
- }
- }
-
- // Defensive Code: Remove any job that older than a preset time.
- // We never got a call back. It is better to have overlapping
- // calls than to get stuck.
- if (existingJob.timeSent != 0
- && (now - existingJob.timeSent) >= MAX_WAIT_TIME_FOR_FRAMEWORK) {
- Log.w(TAG, "Timeout. Removing Job:" + existingJob.toString());
- it.remove();
- removedStaleItems = true;
- continue;
- }
- }
- return removedStaleItems;
+ return sb.toString();
}
-
- private boolean processCommand(BluetoothJob job) {
- boolean successful = false;
- if (job.timeSent == 0) {
- job.timeSent = System.currentTimeMillis();
- switch (job.command) {
- case CONNECT:
- successful = connectInt(job.cachedDevice, job.profile);
- break;
- case DISCONNECT:
- successful = disconnectInt(job.cachedDevice, job.profile);
- break;
- case REMOVE_BOND:
- BluetoothDevice dev = job.cachedDevice.getDevice();
- if (dev != null) {
- successful = dev.removeBond();
- }
- break;
- }
-
- if (successful) {
- if (D) {
- Log.d(TAG, "Command sent successfully:" + job.toString());
- }
- } else if (V) {
- Log.v(TAG, "Framework rejected command immediately:" + job.toString());
- }
- } else if (D) {
- Log.d(TAG, "Job already has a sent time. Skip. " + job.toString());
- }
-
- return successful;
+
+ private String describe(Profile profile) {
+ return describe(this, profile);
}
public void onProfileStateChanged(Profile profile, int newProfileState) {
- synchronized (workQueue) {
- if (D) {
- Log.d(TAG, "onProfileStateChanged:" + workQueue.toString());
- }
-
- int newState = LocalBluetoothProfileManager.getProfileManager(mLocalManager,
- profile).convertState(newProfileState);
-
- if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) {
- if (!mProfiles.contains(profile)) {
- mProfiles.add(profile);
- }
- }
-
- /* Ignore the transient states e.g. connecting, disconnecting */
- if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED ||
- newState == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) {
- BluetoothJob job = workQueue.peek();
- if (job == null) {
- return;
- } else if (!job.cachedDevice.mDevice.equals(mDevice)) {
- // This can happen in 2 cases: 1) BT device initiated pairing and
- // 2) disconnects of one headset that's triggered by connects of
- // another.
- if (D) {
- Log.d(TAG, "mDevice:" + mDevice + " != head:" + job.toString());
- }
-
- // Check to see if we need to remove the stale items from the queue
- if (!pruneQueue(null)) {
- // nothing in the queue was modify. Just ignore the notification and return.
- return;
- }
- } else {
- // Remove the first item and process the next one
- workQueue.poll();
- }
-
- processCommands();
- }
+ if (D) {
+ Log.d(TAG, "onProfileStateChanged: profile " + profile.toString() +
+ " newProfileState " + newProfileState);
}
- }
- /*
- * This method is called in 2 places:
- * 1) queryCommand() - when someone or something want to connect or
- * disconnect
- * 2) onProfileStateChanged() - when the framework sends an intent
- * notification when it finishes processing a command
- */
- private void processCommands() {
- if (D) {
- Log.d(TAG, "processCommands:" + workQueue.toString());
- }
- Iterator<BluetoothJob> it = workQueue.iterator();
- while (it.hasNext()) {
- BluetoothJob job = it.next();
- if (processCommand(job)) {
- // Sent to bt framework. Done for now. Will remove this job
- // from queue when we get an event
- return;
- } else {
- /*
- * If the command failed immediately, there will be no event
- * callbacks. So delete the job immediately and move on to the
- * next one
- */
- it.remove();
+ int newState = LocalBluetoothProfileManager.getProfileManager(mLocalManager,
+ profile).convertState(newProfileState);
+
+ if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) {
+ if (!mProfiles.contains(profile)) {
+ mProfiles.add(profile);
}
}
}
@@ -338,7 +154,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
public void disconnect(Profile profile) {
- queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, this, profile));
+ disconnectInt(this, profile);
}
private boolean disconnectInt(CachedBluetoothDevice cachedDevice, Profile profile) {
@@ -347,9 +163,17 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
int status = profileManager.getConnectionStatus(cachedDevice.mDevice);
if (SettingsBtStatus.isConnectionStatusConnected(status)) {
if (profileManager.disconnect(cachedDevice.mDevice)) {
+ if (D) {
+ Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile));
+ }
return true;
}
+ if (V) {
+ Log.v(TAG, "Framework rejected command immediately:DISCONNECT " +
+ describe(profile));
+ }
}
+
return false;
}
@@ -419,7 +243,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
if (profileManager.isPreferred(mDevice)) {
++preferredProfiles;
disconnectConnected(profile);
- queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
+ connectInt(this, profile);
}
}
}
@@ -442,7 +266,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
.getProfileManager(mLocalManager, profile);
profileManager.setPreferred(mDevice, false);
disconnectConnected(profile);
- queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
+ connectInt(this, profile);
}
}
}
@@ -452,7 +276,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
disconnectConnected(profile);
- queueCommand(new BluetoothJob(BluetoothCommand.CONNECT, this, profile));
+ connectInt(this, profile);
}
private void disconnectConnected(Profile profile) {
@@ -464,7 +288,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
for (BluetoothDevice device : devices) {
CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(device);
if (cachedDevice != null) {
- queueCommand(new BluetoothJob(BluetoothCommand.DISCONNECT, cachedDevice, profile));
+ disconnectInt(cachedDevice, profile);
}
}
}
@@ -477,12 +301,16 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
int status = profileManager.getConnectionStatus(cachedDevice.mDevice);
if (!SettingsBtStatus.isConnectionStatusConnected(status)) {
if (profileManager.connect(cachedDevice.mDevice)) {
+ if (D) {
+ Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
+ }
return true;
}
Log.i(TAG, "Failed to connect " + profile.toString() + " to " + cachedDevice.mName);
} else {
Log.i(TAG, "Already connected");
}
+
return false;
}
@@ -527,7 +355,18 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
if (state != BluetoothDevice.BOND_NONE) {
- queueCommand(new BluetoothJob(BluetoothCommand.REMOVE_BOND, this, null));
+ final BluetoothDevice dev = getDevice();
+ if (dev != null) {
+ final boolean successful = dev.removeBond();
+ if (successful) {
+ if (D) {
+ Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));
+ }
+ } else if (V) {
+ Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +
+ describe(null));
+ }
+ }
}
}
@@ -749,30 +588,6 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
public void onBondingStateChanged(int bondState) {
if (bondState == BluetoothDevice.BOND_NONE) {
mProfiles.clear();
-
- BluetoothJob job = workQueue.peek();
- if (job != null) {
- // Remove the first item and process the next one
- if (job.command == BluetoothCommand.REMOVE_BOND
- && job.cachedDevice.mDevice.equals(mDevice)) {
- workQueue.poll(); // dequeue
- } else {
- // Unexpected job
- if (D) {
- Log.d(TAG, "job.command = " + job.command);
- Log.d(TAG, "mDevice:" + mDevice + " != head:" + job.toString());
- }
-
- // Check to see if we need to remove the stale items from the queue
- if (!pruneQueue(null)) {
- // nothing in the queue was modify. Just ignore the notification and return.
- refresh();
- return;
- }
- }
-
- processCommands();
- }
}
refresh();
diff --git a/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java b/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java
index 08534f3..2a8af5f 100644
--- a/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java
+++ b/src/com/android/settings/bluetooth/ConnectSpecificProfilesActivity.java
@@ -179,6 +179,9 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity
}
private void onProfileCheckedStateChanged(Profile profile, boolean checked) {
+ LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
+ .getProfileManager(mManager, profile);
+ profileManager.setPreferred(mCachedDevice.getDevice(), checked);
if (mOnlineMode) {
if (checked) {
mCachedDevice.connect(profile);
@@ -186,10 +189,6 @@ public class ConnectSpecificProfilesActivity extends PreferenceActivity
mCachedDevice.disconnect(profile);
}
}
-
- LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
- .getProfileManager(mManager, profile);
- profileManager.setPreferred(mCachedDevice.getDevice(), checked);
}
public void onDeviceAttributesChanged(CachedBluetoothDevice cachedDevice) {
diff --git a/src/com/android/settings/bluetooth/DockService.java b/src/com/android/settings/bluetooth/DockService.java
index f318987..d0f8099 100644
--- a/src/com/android/settings/bluetooth/DockService.java
+++ b/src/com/android/settings/bluetooth/DockService.java
@@ -18,6 +18,7 @@ package com.android.settings.bluetooth;
import com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
+import com.android.settings.bluetooth.LocalBluetoothProfileManager.ServiceListener;
import android.app.AlertDialog;
import android.app.Notification;
@@ -48,7 +49,7 @@ import java.util.Set;
public class DockService extends Service implements AlertDialog.OnMultiChoiceClickListener,
DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
- CompoundButton.OnCheckedChangeListener {
+ CompoundButton.OnCheckedChangeListener, ServiceListener {
private static final String TAG = "DockService";
@@ -101,6 +102,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
// Created in OnCreate()
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
+ private Runnable mRunnable;
private DockService mContext;
private LocalBluetoothManager mBtManager;
@@ -138,6 +140,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
@Override
public void onDestroy() {
if (DEBUG) Log.d(TAG, "onDestroy");
+ mRunnable = null;
+ LocalBluetoothProfileManager.removeServiceListener(this);
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
@@ -228,8 +232,8 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
// This method gets messages from both onStartCommand and mServiceHandler/mServiceLooper
private synchronized void processMessage(Message msg) {
int msgType = msg.what;
- int state = msg.arg1;
- int startId = msg.arg2;
+ final int state = msg.arg1;
+ final int startId = msg.arg2;
boolean deferFinishCall = false;
BluetoothDevice device = null;
if (msg.obj != null) {
@@ -271,12 +275,23 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
}
mDevice = device;
- if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
- // Setting == auto connect
- initBtSettings(mContext, device, state, false);
- applyBtSettings(mDevice, startId);
+
+ // Register first in case LocalBluetoothProfileManager
+ // becomes ready after isManagerReady is called and it
+ // would be too late to register a service listener.
+ LocalBluetoothProfileManager.addServiceListener(this);
+ if (LocalBluetoothProfileManager.isManagerReady()) {
+ handleDocked(device, state, startId);
+ // Not needed after all
+ LocalBluetoothProfileManager.removeServiceListener(this);
} else {
- createDialog(mContext, mDevice, state, startId);
+ final BluetoothDevice d = device;
+ mRunnable = new Runnable() {
+ public void run() {
+ handleDocked(d, state, startId);
+ }
+ };
+ deferFinishCall = true;
}
}
break;
@@ -721,8 +736,21 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
}
}
+ private synchronized void handleDocked(final BluetoothDevice device, final int state,
+ final int startId) {
+ if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
+ // Setting == auto connect
+ initBtSettings(mContext, device, state, false);
+ applyBtSettings(mDevice, startId);
+ } else {
+ createDialog(mContext, device, state, startId);
+ }
+ }
+
private synchronized void handleUndocked(Context context, LocalBluetoothManager localManager,
BluetoothDevice device) {
+ mRunnable = null;
+ LocalBluetoothProfileManager.removeServiceListener(this);
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
@@ -778,4 +806,15 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
editor.commit();
return;
}
+
+ public synchronized void onServiceConnected() {
+ if (mRunnable != null) {
+ mRunnable.run();
+ mRunnable = null;
+ LocalBluetoothProfileManager.removeServiceListener(this);
+ }
+ }
+
+ public void onServiceDisconnected() {
+ }
}
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
index f3aaade..01714fe 100644
--- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
+++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
@@ -28,6 +28,8 @@ import android.util.Log;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -53,6 +55,29 @@ public abstract class LocalBluetoothProfileManager {
BluetoothUuid.ObexObjectPush
};
+ /**
+ * An interface for notifying BluetoothHeadset IPC clients when they have
+ * been connected to the BluetoothHeadset service.
+ */
+ public interface ServiceListener {
+ /**
+ * Called to notify the client when this proxy object has been
+ * connected to the BluetoothHeadset service. Clients must wait for
+ * this callback before making IPC calls on the BluetoothHeadset
+ * service.
+ */
+ public void onServiceConnected();
+
+ /**
+ * Called to notify the client that this proxy object has been
+ * disconnected from the BluetoothHeadset service. Clients must not
+ * make IPC calls on the BluetoothHeadset service after this callback.
+ * This callback will currently only occur if the application hosting
+ * the BluetoothHeadset service, but may be called more often in future.
+ */
+ public void onServiceDisconnected();
+ }
+
// TODO: close profiles when we're shutting down
private static Map<Profile, LocalBluetoothProfileManager> sProfileMap =
new HashMap<Profile, LocalBluetoothProfileManager>();
@@ -76,6 +101,26 @@ public abstract class LocalBluetoothProfileManager {
}
}
+ private static LinkedList<ServiceListener> mServiceListeners = new LinkedList<ServiceListener>();
+
+ public static void addServiceListener(ServiceListener l) {
+ mServiceListeners.add(l);
+ }
+
+ public static void removeServiceListener(ServiceListener l) {
+ mServiceListeners.remove(l);
+ }
+
+ public static boolean isManagerReady() {
+ // Getting just the headset profile is fine for now. Will need to deal with A2DP
+ // and others if they aren't always in a ready state.
+ LocalBluetoothProfileManager profileManager = sProfileMap.get(Profile.HEADSET);
+ if (profileManager == null) {
+ return sProfileMap.size() > 0;
+ }
+ return profileManager.isProfileReady();
+ }
+
public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
Profile profile) {
// Note: This code assumes that "localManager" is same as the
@@ -144,6 +189,8 @@ public abstract class LocalBluetoothProfileManager {
return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device));
}
+ public abstract boolean isProfileReady();
+
// TODO: int instead of enum
public enum Profile {
HEADSET(R.string.bluetooth_profile_headset),
@@ -247,6 +294,11 @@ public abstract class LocalBluetoothProfileManager {
return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
}
}
+
+ @Override
+ public boolean isProfileReady() {
+ return true;
+ }
}
/**
@@ -256,6 +308,7 @@ public abstract class LocalBluetoothProfileManager {
implements BluetoothHeadset.ServiceListener {
private BluetoothHeadset mService;
private Handler mUiHandler = new Handler();
+ private boolean profileReady = false;
public HeadsetProfileManager(LocalBluetoothManager localManager) {
super(localManager);
@@ -263,6 +316,7 @@ public abstract class LocalBluetoothProfileManager {
}
public void onServiceConnected() {
+ profileReady = true;
// This could be called on a non-UI thread, funnel to UI thread.
mUiHandler.post(new Runnable() {
public void run() {
@@ -277,9 +331,28 @@ public abstract class LocalBluetoothProfileManager {
BluetoothHeadset.STATE_CONNECTED);
}
});
+
+ if (mServiceListeners.size() > 0) {
+ Iterator<ServiceListener> it = mServiceListeners.iterator();
+ while(it.hasNext()) {
+ it.next().onServiceConnected();
+ }
+ }
}
public void onServiceDisconnected() {
+ profileReady = false;
+ if (mServiceListeners.size() > 0) {
+ Iterator<ServiceListener> it = mServiceListeners.iterator();
+ while(it.hasNext()) {
+ it.next().onServiceDisconnected();
+ }
+ }
+ }
+
+ @Override
+ public boolean isProfileReady() {
+ return profileReady;
}
@Override
@@ -297,7 +370,10 @@ public abstract class LocalBluetoothProfileManager {
public boolean connect(BluetoothDevice device) {
// Since connectHeadset fails if already connected to a headset, we
// disconnect from any headset first
- mService.disconnectHeadset();
+ BluetoothDevice currDevice = mService.getCurrentHeadset();
+ if (currDevice != null) {
+ mService.disconnectHeadset(currDevice);
+ }
return mService.connectHeadset(device);
}
@@ -308,7 +384,7 @@ public abstract class LocalBluetoothProfileManager {
if (mService.getPriority(device) > BluetoothHeadset.PRIORITY_ON) {
mService.setPriority(device, BluetoothHeadset.PRIORITY_ON);
}
- return mService.disconnectHeadset();
+ return mService.disconnectHeadset(device);
} else {
return false;
}
@@ -318,7 +394,7 @@ public abstract class LocalBluetoothProfileManager {
public int getConnectionStatus(BluetoothDevice device) {
BluetoothDevice currentDevice = mService.getCurrentHeadset();
return currentDevice != null && currentDevice.equals(device)
- ? convertState(mService.getState())
+ ? convertState(mService.getState(device))
: SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
}
@@ -424,6 +500,11 @@ public abstract class LocalBluetoothProfileManager {
}
@Override
+ public boolean isProfileReady() {
+ return true;
+ }
+
+ @Override
public int convertState(int oppState) {
switch (oppState) {
case 0:
diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java
index 4db968a..cc112f8 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java
@@ -40,9 +40,9 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.settings.InstalledAppDetails;
-import com.android.settings.ManageApplications;
import com.android.settings.R;
+import com.android.settings.applications.InstalledAppDetails;
+import com.android.settings.applications.ManageApplications;
public class PowerUsageDetail extends Activity implements Button.OnClickListener {
@@ -248,9 +248,9 @@ public class PowerUsageDetail extends Activity implements Button.OnClickListener
startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
break;
case ACTION_APP_DETAILS:
- Intent intent = new Intent(Intent.ACTION_VIEW);
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.fromParts("package", mPackages[0], null));
intent.setClass(this, InstalledAppDetails.class);
- intent.putExtra(ManageApplications.APP_PKG_NAME, mPackages[0]);
startActivity(intent);
break;
case ACTION_SECURITY_SETTINGS:
diff --git a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
index fb9fbcd..ca5d3ab 100644
--- a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
+++ b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
@@ -178,7 +178,7 @@ public class QuickLaunchSettings extends PreferenceActivity implements
}
public void onClick(DialogInterface dialog, int which) {
- if (mClearDialogShortcut > 0 && which == AlertDialog.BUTTON1) {
+ if (mClearDialogShortcut > 0 && which == AlertDialog.BUTTON_POSITIVE) {
// Clear the shortcut
clearShortcut(mClearDialogShortcut);
}
diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java
index 8880780..7b8d433 100644
--- a/src/com/android/settings/vpn/VpnSettings.java
+++ b/src/com/android/settings/vpn/VpnSettings.java
@@ -17,7 +17,6 @@
package com.android.settings.vpn;
import com.android.settings.R;
-import com.android.settings.SecuritySettings;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -38,13 +37,10 @@ import android.net.vpn.VpnType;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.IBinder;
-import android.os.Parcel;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
-import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceClickListener;
import android.security.Credentials;
@@ -53,7 +49,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
-import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.AdapterContextMenuInfo;
@@ -67,12 +62,9 @@ import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* The preference activity for configuring VPN settings.
@@ -101,8 +93,8 @@ public class VpnSettings extends PreferenceActivity implements
private static final int CONTEXT_MENU_EDIT_ID = ContextMenu.FIRST + 2;
private static final int CONTEXT_MENU_DELETE_ID = ContextMenu.FIRST + 3;
- private static final int CONNECT_BUTTON = DialogInterface.BUTTON1;
- private static final int OK_BUTTON = DialogInterface.BUTTON1;
+ private static final int CONNECT_BUTTON = DialogInterface.BUTTON_POSITIVE;
+ private static final int OK_BUTTON = DialogInterface.BUTTON_POSITIVE;
private static final int DIALOG_CONNECT = VpnManager.VPN_ERROR_LARGEST + 1;
private static final int DIALOG_SECRET_NOT_SET = DIALOG_CONNECT + 1;
diff --git a/src/com/android/settings/wifi/AdvancedSettings.java b/src/com/android/settings/wifi/AdvancedSettings.java
index 7d62f8e..636e1df 100644
--- a/src/com/android/settings/wifi/AdvancedSettings.java
+++ b/src/com/android/settings/wifi/AdvancedSettings.java
@@ -40,6 +40,7 @@ public class AdvancedSettings extends PreferenceActivity
implements Preference.OnPreferenceChangeListener {
private static final String KEY_MAC_ADDRESS = "mac_address";
+ private static final String KEY_CURRENT_IP_ADDRESS = "current_ip_address";
private static final String KEY_USE_STATIC_IP = "use_static_ip";
private static final String KEY_NUM_CHANNELS = "num_channels";
private static final String KEY_SLEEP_POLICY = "sleep_policy";
@@ -109,7 +110,7 @@ public class AdvancedSettings extends PreferenceActivity
initNumChannelsPreference();
}
initSleepPolicyPreference();
- refreshMacAddress();
+ refreshWifiInfo();
}
private void initNumChannelsPreference() {
@@ -307,7 +308,7 @@ public class AdvancedSettings extends PreferenceActivity
}
}
- private void refreshMacAddress() {
+ private void refreshWifiInfo() {
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
@@ -315,6 +316,20 @@ public class AdvancedSettings extends PreferenceActivity
String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
wifiMacAddressPref.setSummary(!TextUtils.isEmpty(macAddress) ? macAddress
: getString(R.string.status_unavailable));
+
+ Preference wifiIpAddressPref = findPreference(KEY_CURRENT_IP_ADDRESS);
+ String ipAddress = null;
+ if (wifiInfo != null) {
+ long addr = wifiInfo.getIpAddress();
+ if (addr != 0) {
+ // handle negative values whe first octet > 127
+ if (addr < 0) addr += 0x100000000L;
+ ipAddress = String.format("%d.%d.%d.%d",
+ addr & 0xFF, (addr >> 8) & 0xFF, (addr >> 16) & 0xFF, (addr >> 24) & 0xFF);
+ }
+ }
+ wifiIpAddressPref.setSummary(ipAddress == null ?
+ getString(R.string.status_unavailable) : ipAddress);
}
-
+
}