am 6fce81a6: am 508fedc4: Improvements to manage apps / running services:

Merge commit '6fce81a69d7c2891d31618016c8ddc7b08deaa03'

* commit '6fce81a69d7c2891d31618016c8ddc7b08deaa03':
  Improvements to manage apps / running services:
This commit is contained in:
Dianne Hackborn
2010-07-28 17:46:32 -07:00
committed by Android Git Automerger
9 changed files with 310 additions and 188 deletions

View File

@@ -380,10 +380,10 @@
</intent-filter> </intent-filter>
</activity> </activity>
<!-- <!-- Provide direct entry into manage apps showing running services. -->
<activity android:name=".applications.RunningServices" <activity-alias android:name=".RunningServices"
android:label="@string/runningservices_settings_title" android:label="@string/runningservices_settings_title"
android:clearTaskOnLaunch="true"> android:targetActivity=".applications.ManageApplications">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
@@ -391,11 +391,11 @@
<category android:name="android.intent.category.VOICE_LAUNCH" /> <category android:name="android.intent.category.VOICE_LAUNCH" />
<category android:name="com.android.settings.SHORTCUT" /> <category android:name="com.android.settings.SHORTCUT" />
</intent-filter> </intent-filter>
</activity> </activity-alias>
-->
<!-- Provide direct entry into manage apps showing running services. --> <!-- Provide direct entry into manage apps showing running services. -->
<activity-alias android:name=".RunningServices" <activity-alias android:name=".applications.StorageUse"
android:label="@string/storageuse_settings_title"
android:targetActivity=".applications.ManageApplications"> android:targetActivity=".applications.ManageApplications">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,12 +18,20 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" > android:layout_height="match_parent" >
<ListView <FrameLayout android:id="@+id/list_container"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:id="@android:id/list" android:layout_height="match_parent">
android:drawSelectorOnTop="false" <ListView android:id="@android:id/list"
android:layout_width="match_parent" android:drawSelectorOnTop="false"
android:layout_height="match_parent" /> android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_applications"
android:textAppearance="?android:attr/textAppearanceLarge" />
</FrameLayout>
<view class="com.android.settings.applications.RunningProcessesView" <view class="com.android.settings.applications.RunningProcessesView"
android:id="@+id/running_processes" android:id="@+id/running_processes"

View File

@@ -1737,7 +1737,8 @@
<string name="filter_apps_onsdcard">On SD card</string> <string name="filter_apps_onsdcard">On SD card</string>
<!-- Manage applications, text telling using an application is disabled. --> <!-- Manage applications, text telling using an application is disabled. -->
<string name="disabled">Disabled</string> <string name="disabled">Disabled</string>
<string name="loading">Loading\u2026</string> <!-- Text shown when there are no applications to display. -->
<string name="no_applications">No applications.</string>
<!-- Manage app screen, shown when the activity is busy recomputing the size of each app --> <!-- Manage app screen, shown when the activity is busy recomputing the size of each app -->
<string name="recompute_size">Recomputing size\u2026</string> <string name="recompute_size">Recomputing size\u2026</string>
<!-- Manage applications, individual application screen, confirmation dialog title. Displays when user selects to "Clear data". --> <!-- Manage applications, individual application screen, confirmation dialog title. Displays when user selects to "Clear data". -->
@@ -1800,12 +1801,19 @@ found in the list of installed applications.</string>
<!-- Manage applications. application installation location summary --> <!-- Manage applications. application installation location summary -->
<string name="app_install_location_summary">Change the preferred installation location for new applications.</string> <string name="app_install_location_summary">Change the preferred installation location for new applications.</string>
<!-- Services settings screen, setting option name for the user to go to the screen to view app storage use -->
<string name="storageuse_settings_title">Storage use</string>
<!-- Services settings screen, setting option summary for the user to go to the screen to app storage use -->
<string name="storageuse_settings_summary">View storage used by applications</string>
<!-- Services settings screen, setting option name for the user to go to the screen to view running services --> <!-- Services settings screen, setting option name for the user to go to the screen to view running services -->
<string name="runningservices_settings_title">Running services</string> <string name="runningservices_settings_title">Running services</string>
<!-- Services settings screen, setting option summary for the user to go to the screen to view running services --> <!-- Services settings screen, setting option summary for the user to go to the screen to view running services -->
<string name="runningservices_settings_summary">View and control currently running services</string> <string name="runningservices_settings_summary">View and control currently running services</string>
<!-- Label for a service item when it is restarting --> <!-- Label for a service item when it is restarting -->
<string name="service_restarting">Restarting</string> <string name="service_restarting">Restarting</string>
<!-- Text shown when there are no services running -->
<string name="no_running_services">Nothing running.</string>
<!-- Running services, description for a service in the started state --> <!-- Running services, description for a service in the started state -->
<string name="service_started_by_app">Started by application.</string> <string name="service_started_by_app">Started by application.</string>
<!-- Running services, description for a service in the started state --> <!-- Running services, description for a service in the started state -->

View File

@@ -59,6 +59,14 @@
android:targetClass="com.android.settings.RunningServices" /> android:targetClass="com.android.settings.RunningServices" />
</PreferenceScreen> </PreferenceScreen>
<PreferenceScreen
android:title="@string/storageuse_settings_title"
android:summary="@string/storageuse_settings_summary">
<intent android:action="android.intent.action.MAIN"
android:targetPackage="com.android.settings"
android:targetClass="com.android.settings.applications.StorageUse" />
</PreferenceScreen>
<PreferenceScreen <PreferenceScreen
android:key="power_usage" android:key="power_usage"
android:title="@string/power_usage_summary_title" android:title="@string/power_usage_summary_title"

View File

@@ -320,7 +320,7 @@ public class ApplicationsState {
if (entry.icon != null) { if (entry.icon != null) {
return; return;
} }
synchronized (mEntriesMap) { synchronized (entry) {
if (entry.icon == null) { if (entry.icon == null) {
entry.icon = entry.info.loadIcon(mPm); entry.icon = entry.info.loadIcon(mPm);
} }

View File

@@ -92,6 +92,8 @@ public class ManageApplications extends TabActivity implements
private String mCurrentPkgName; private String mCurrentPkgName;
private View mListContainer;
// ListView used to display list // ListView used to display list
private ListView mListView; private ListView mListView;
// Custom view used to display running processes // Custom view used to display running processes
@@ -378,8 +380,9 @@ public class ManageApplications extends TabActivity implements
if (intent.getComponent().getClassName().equals( if (intent.getComponent().getClassName().equals(
"com.android.settings.RunningServices")) { "com.android.settings.RunningServices")) {
defaultTabTag = TAB_RUNNING; defaultTabTag = TAB_RUNNING;
} } else if (intent.getComponent().getClassName().equals(
if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) { "com.android.settings.applications.StorageUse")
|| action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
mSortOrder = SORT_ORDER_SIZE; mSortOrder = SORT_ORDER_SIZE;
mFilterApps = FILTER_APPS_ALL; mFilterApps = FILTER_APPS_ALL;
defaultTabTag = TAB_ALL; defaultTabTag = TAB_ALL;
@@ -401,9 +404,14 @@ public class ManageApplications extends TabActivity implements
mComputingSizeStr = getText(R.string.computing_size); mComputingSizeStr = getText(R.string.computing_size);
// initialize the inflater // initialize the inflater
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mRootView = mInflater.inflate(R.layout.compute_sizes, null); mRootView = mInflater.inflate(R.layout.manage_applications, null);
mListContainer = mRootView.findViewById(R.id.list_container);
// Create adapter and list view here // Create adapter and list view here
ListView lv = (ListView) mRootView.findViewById(android.R.id.list); ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
if (emptyView != null) {
lv.setEmptyView(emptyView);
}
lv.setOnItemClickListener(this); lv.setOnItemClickListener(this);
lv.setSaveEnabled(true); lv.setSaveEnabled(true);
lv.setItemsCanFocus(true); lv.setItemsCanFocus(true);
@@ -543,7 +551,7 @@ public class ManageApplications extends TabActivity implements
} }
if (mCurView != which) { if (mCurView != which) {
mRunningProcessesView.setVisibility(View.GONE); mRunningProcessesView.setVisibility(View.GONE);
mListView.setVisibility(View.VISIBLE); mListContainer.setVisibility(View.VISIBLE);
} }
if (mActivityResumed) { if (mActivityResumed) {
mApplicationsAdapter.resume(mFilterApps, mSortOrder); mApplicationsAdapter.resume(mFilterApps, mSortOrder);
@@ -560,7 +568,7 @@ public class ManageApplications extends TabActivity implements
mApplicationsAdapter.pause(); mApplicationsAdapter.pause();
if (mCurView != which) { if (mCurView != which) {
mRunningProcessesView.setVisibility(View.VISIBLE); mRunningProcessesView.setVisibility(View.VISIBLE);
mListView.setVisibility(View.GONE); mListContainer.setVisibility(View.GONE);
} }
} }
mCurView = which; mCurView = which;

View File

@@ -26,15 +26,12 @@ import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle; 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.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -53,17 +50,8 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
public class RunningProcessesView extends FrameLayout public class RunningProcessesView extends FrameLayout
implements AdapterView.OnItemClickListener, RecyclerListener { implements AdapterView.OnItemClickListener, RecyclerListener,
RunningState.OnRefreshUiListener {
/** 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. // Memory pages are 4K.
static final long PAGE_SIZE = 4*1024; static final long PAGE_SIZE = 4*1024;
@@ -128,6 +116,8 @@ public class RunningProcessesView extends FrameLayout
if (uptimeView != null) { if (uptimeView != null) {
if (mItem.mActiveSince >= 0) { if (mItem.mActiveSince >= 0) {
//Log.i("foo", "Time for " + mItem.mDisplayLabel
// + ": " + (SystemClock.uptimeMillis()-mFirstRunTime));
uptimeView.setText(DateUtils.formatElapsedTime(builder, uptimeView.setText(DateUtils.formatElapsedTime(builder,
(SystemClock.uptimeMillis()-mFirstRunTime)/1000)); (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
} else { } else {
@@ -220,6 +210,11 @@ public class RunningProcessesView extends FrameLayout
return mItems.size(); return mItems.size();
} }
@Override
public boolean isEmpty() {
return mState.hasData() && mItems.size() == 0;
}
public Object getItem(int position) { public Object getItem(int position) {
return mItems.get(position); return mItems.get(position);
} }
@@ -330,55 +325,6 @@ public class RunningProcessesView extends FrameLayout
} }
} }
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) { private boolean matchText(byte[] buffer, int index, String text) {
int N = text.length(); int N = text.length();
if ((index+N) >= buffer.length) { if ((index+N) >= buffer.length) {
@@ -506,10 +452,7 @@ public class RunningProcessesView extends FrameLayout
public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) { public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE); mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
mState = (RunningState)nonConfigurationInstace; mState = RunningState.getInstance(getContext());
if (mState == null) {
mState = new RunningState();
}
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService( LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE); Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.running_processes_view, this); inflater.inflate(R.layout.running_processes_view, this);
@@ -531,28 +474,49 @@ public class RunningProcessesView extends FrameLayout
} }
public void doPause() { public void doPause() {
mHandler.removeMessages(MSG_UPDATE_TIMES); mState.pause();
if (mBackgroundThread != null) {
mBackgroundThread.quit();
mBackgroundThread = null;
mBackgroundHandler = null;
}
} }
public void doResume() { public void doResume() {
refreshUi(mState.update(getContext(), mAm)); mState.resume(this);
mBackgroundThread = new HandlerThread("RunningServices"); if (mState.hasData()) {
mBackgroundThread.start(); // If the state already has its data, then let's populate our
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper()); // list right now to avoid flicker.
mHandler.removeMessages(MSG_UPDATE_TIMES); refreshUi(true);
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() { public Object doRetainNonConfigurationInstance() {
return mState; return null;
}
void updateTimes() {
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);
}
}
@Override
public void onRefreshUi(int what) {
switch (what) {
case REFRESH_TIME:
updateTimes();
break;
case REFRESH_DATA:
refreshUi(false);
updateTimes();
break;
case REFRESH_STRUCTURE:
refreshUi(true);
updateTimes();
break;
}
} }
} }

View File

@@ -42,16 +42,13 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class RunningServiceDetails extends Activity { public class RunningServiceDetails extends Activity
implements RunningState.OnRefreshUiListener {
static final String TAG = "RunningServicesDetails"; static final String TAG = "RunningServicesDetails";
static final String KEY_UID = "uid"; static final String KEY_UID = "uid";
static final String KEY_PROCESS = "process"; 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; ActivityManager mAm;
LayoutInflater mInflater; LayoutInflater mInflater;
@@ -156,9 +153,7 @@ public class RunningServiceDetails extends Activity {
// so no reason for the UI to stick around. // so no reason for the UI to stick around.
finish(); finish();
} else { } else {
if (mBackgroundHandler != null) { mState.updateNow();
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
} }
} else { } else {
// Heavy-weight process. We'll do a force-stop on it. // Heavy-weight process. We'll do a force-stop on it.
@@ -170,52 +165,6 @@ public class RunningServiceDetails extends Activity {
StringBuilder mBuilder = new StringBuilder(128); 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() { boolean findMergedItem() {
RunningState.MergedItem item = null; RunningState.MergedItem item = null;
ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems(); ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
@@ -447,10 +396,7 @@ public class RunningServiceDetails extends Activity {
mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mState = (RunningState)getLastNonConfigurationInstance(); mState = RunningState.getInstance(this);
if (mState == null) {
mState = new RunningState();
}
setContentView(R.layout.running_service_details); setContentView(R.layout.running_service_details);
@@ -464,36 +410,53 @@ public class RunningServiceDetails extends Activity {
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
mHandler.removeMessages(MSG_UPDATE_TIMES); mState.pause();
if (mBackgroundThread != null) {
mBackgroundThread.quit();
mBackgroundThread = null;
mBackgroundHandler = null;
}
} }
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
refreshUi(mState.update(this, mAm)); mState.resume(this);
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 // We want to go away if the service being shown no longer exists,
public Object onRetainNonConfigurationInstance() { // so we need to ensure we have done the initial data retrieval before
return mState; // showing our ui.
mState.waitForData();
// And since we know we have the data, let's show the UI right away
// to avoid flicker.
refreshUi(true);
} }
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
} }
void updateTimes() {
if (mSnippetActiveItem != null) {
mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
}
for (int i=0; i<mActiveDetails.size(); i++) {
mActiveDetails.get(i).mActiveItem.updateTime(
RunningServiceDetails.this, mBuilder);
}
}
@Override
public void onRefreshUi(int what) {
switch (what) {
case REFRESH_TIME:
updateTimes();
break;
case REFRESH_DATA:
refreshUi(false);
updateTimes();
break;
case REFRESH_STRUCTURE:
refreshUi(true);
updateTimes();
break;
}
}
} }

View File

@@ -29,7 +29,12 @@ import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Debug; 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.RemoteException;
import android.os.SystemClock;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
@@ -46,6 +51,23 @@ import java.util.List;
* applications/processes/services. * applications/processes/services.
*/ */
public class RunningState { public class RunningState {
static Object sGlobalLock = new Object();
static RunningState sInstance;
static final int MSG_UPDATE_CONTENTS = 1;
static final int MSG_REFRESH_UI = 2;
static final int MSG_UPDATE_TIME = 3;
static final long TIME_UPDATE_DELAY = 1000;
static final long CONTENTS_UPDATE_DELAY = 2000;
static final int MAX_SERVICES = 100;
final Context mApplicationContext;
final ActivityManager mAm;
final PackageManager mPm;
OnRefreshUiListener mRefreshUiListener;
// Processes that are hosting a service we are interested in, organized // Processes that are hosting a service we are interested in, organized
// by uid and name. Note that this mapping does not change even across // by uid and name. Note that this mapping does not change even across
@@ -85,6 +107,9 @@ public class RunningState {
// background update thread and the UI thread. // background update thread and the UI thread.
final Object mLock = new Object(); final Object mLock = new Object();
boolean mResumed;
boolean mHaveData;
ArrayList<BaseItem> mItems = new ArrayList<BaseItem>(); ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>(); ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
@@ -95,6 +120,78 @@ public class RunningState {
int mNumServiceProcesses; int mNumServiceProcesses;
long mServiceProcessMemory; long mServiceProcessMemory;
// ----- BACKGROUND MONITORING THREAD -----
final 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:
synchronized (mLock) {
if (!mResumed) {
return;
}
}
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0;
mHandler.sendMessage(cmd);
removeMessages(MSG_UPDATE_CONTENTS);
msg = obtainMessage(MSG_UPDATE_CONTENTS);
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
break;
}
}
};
final BackgroundHandler mBackgroundHandler;
final Handler mHandler = new Handler() {
int mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REFRESH_UI:
mNextUpdate = msg.arg1 != 0
? OnRefreshUiListener.REFRESH_STRUCTURE
: OnRefreshUiListener.REFRESH_DATA;
break;
case MSG_UPDATE_TIME:
synchronized (mLock) {
if (!mResumed) {
return;
}
}
removeMessages(MSG_UPDATE_TIME);
Message m = obtainMessage(MSG_UPDATE_TIME);
sendMessageDelayed(m, TIME_UPDATE_DELAY);
if (mRefreshUiListener != null) {
//Log.i("foo", "Refresh UI: " + mNextUpdate
// + " @ " + SystemClock.uptimeMillis());
mRefreshUiListener.onRefreshUi(mNextUpdate);
mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
}
break;
}
}
};
// ----- DATA STRUCTURES -----
static interface OnRefreshUiListener {
public static final int REFRESH_TIME = 0;
public static final int REFRESH_DATA = 1;
public static final int REFRESH_STRUCTURE = 2;
public void onRefreshUi(int what);
}
static class BaseItem { static class BaseItem {
final boolean mIsProcess; final boolean mIsProcess;
@@ -428,7 +525,69 @@ public class RunningState {
return label; return label;
} }
boolean update(Context context, ActivityManager am) { static RunningState getInstance(Context context) {
synchronized (sGlobalLock) {
if (sInstance == null) {
sInstance = new RunningState(context);
}
return sInstance;
}
}
private RunningState(Context context) {
mApplicationContext = context.getApplicationContext();
mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE);
mPm = mApplicationContext.getPackageManager();
mResumed = false;
mBackgroundThread = new HandlerThread("RunningState:Background");
mBackgroundThread.start();
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
}
void resume(OnRefreshUiListener listener) {
synchronized (mLock) {
mResumed = true;
mRefreshUiListener = listener;
if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
mHandler.sendEmptyMessage(MSG_UPDATE_TIME);
}
}
void updateNow() {
synchronized (mLock) {
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
}
boolean hasData() {
synchronized (mLock) {
return mHaveData;
}
}
void waitForData() {
synchronized (mLock) {
while (!mHaveData) {
try {
mLock.wait(0);
} catch (InterruptedException e) {
}
}
}
}
void pause() {
synchronized (mLock) {
mResumed = false;
mRefreshUiListener = null;
mHandler.removeMessages(MSG_UPDATE_TIME);
}
}
private boolean update(Context context, ActivityManager am) {
final PackageManager pm = context.getPackageManager(); final PackageManager pm = context.getPackageManager();
mSequence++; mSequence++;
@@ -436,7 +595,7 @@ public class RunningState {
boolean changed = false; boolean changed = false;
List<ActivityManager.RunningServiceInfo> services List<ActivityManager.RunningServiceInfo> services
= am.getRunningServices(RunningProcessesView.MAX_SERVICES); = am.getRunningServices(MAX_SERVICES);
final int NS = services != null ? services.size() : 0; final int NS = services != null ? services.size() : 0;
for (int i=0; i<NS; i++) { for (int i=0; i<NS; i++) {
ActivityManager.RunningServiceInfo si = services.get(i); ActivityManager.RunningServiceInfo si = services.get(i);
@@ -776,6 +935,10 @@ public class RunningState {
mBackgroundProcessMemory = backgroundProcessMemory; mBackgroundProcessMemory = backgroundProcessMemory;
mForegroundProcessMemory = foregroundProcessMemory; mForegroundProcessMemory = foregroundProcessMemory;
mServiceProcessMemory = serviceProcessMemory; mServiceProcessMemory = serviceProcessMemory;
if (!mHaveData) {
mHaveData = true;
mLock.notifyAll();
}
} }
return changed; return changed;