New Running Services UI.

This introduces a simplified (thanks, dsandler!) UI for Running Services,
collapsing the groups of apps and processes into single lines.  Tapping
on a line moves to a new activity showing details on that group, where the
stop functionality is now available.

This UI is now also integrated into Manage Applications, as the Running
tab.  You no longer get a really confusing, misleading, scary list of
every package that appears to be laying around for some reason.

The code was also re-organized, to put everything related to Manage
Applications and Running Services under its own package.

There is still some clean-up -- some performance improvements (such as
not re-computing the world when we switch to the details view), and if
this looks good then eradicating the old running services UI.

Change-Id: I3fc059c18060600742cab5b455d11ff74bf45ae3
This commit is contained in:
Dianne Hackborn
2010-06-06 22:51:42 -07:00
parent 2486572bd6
commit 728ac35373
21 changed files with 3359 additions and 1359 deletions

View File

@@ -350,7 +350,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="ManageApplications" <activity android:name=".applications.ManageApplications"
android:label="@string/manageapplications_settings_title" android:label="@string/manageapplications_settings_title"
android:clearTaskOnLaunch="true" android:clearTaskOnLaunch="true"
android:configChanges="orientation|keyboardHidden"> android:configChanges="orientation|keyboardHidden">
@@ -365,15 +365,22 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="InstalledAppDetails" android:label="@string/application_info_label"> <!-- Keep compatibility with old shortcuts. -->
<activity-alias android:name=".ManageApplications"
android:targetActivity=".applications.ManageApplications"
android:exported="true" />
<activity android:name=".applications.InstalledAppDetails"
android:label="@string/application_info_label">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.settings.APPLICATION_DETAILS_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.VOICE_LAUNCH" /> <data android:scheme="package" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="RunningServices" <!--
<activity android:name=".applications.RunningServices"
android:label="@string/runningservices_settings_title" android:label="@string/runningservices_settings_title"
android:clearTaskOnLaunch="true"> android:clearTaskOnLaunch="true">
<intent-filter> <intent-filter>
@@ -384,6 +391,24 @@
<category android:name="com.android.settings.SHORTCUT" /> <category android:name="com.android.settings.SHORTCUT" />
</intent-filter> </intent-filter>
</activity> </activity>
-->
<!-- Provide direct entry into manage apps showing running services. -->
<activity-alias android:name=".RunningServices"
android:targetActivity=".applications.ManageApplications">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY" />
<category android:name="android.intent.category.VOICE_LAUNCH" />
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
</activity-alias>
<activity android:name=".applications.RunningServiceDetails"
android:theme="@android:style/Theme.NoTitleBar"
android:label="@string/runningservicedetails_settings_title">
</activity>
<activity android:name="SecuritySettings" <activity android:name="SecuritySettings"
android:label="@string/location_security_settings_title" android:label="@string/location_security_settings_title"

View File

@@ -14,9 +14,20 @@
limitations under the License. limitations under the License.
--> -->
<ListView <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:drawSelectorOnTop="false"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" >
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:drawSelectorOnTop="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<view class="com.android.settings.applications.RunningProcessesView"
android:id="@+id/running_processes"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@@ -31,7 +31,6 @@
<ImageView android:id="@+id/app_icon" <ImageView android:id="@+id/app_icon"
android:layout_width="@android:dimen/app_icon_size" android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size" android:layout_height="@android:dimen/app_icon_size"
android:layout_marginLeft="5dip"
android:layout_marginRight="11dip" android:layout_marginRight="11dip"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:scaleType="fitCenter"/> android:scaleType="fitCenter"/>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="vertical"
android:gravity="fill" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingRight="6dip"
android:paddingLeft="6dip"
android:gravity="center_vertical" >
<ImageView android:id="@+id/icon"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginRight="11dip"
android:layout_gravity="center_vertical"
android:scaleType="fitCenter"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:orientation="horizontal"
android:baselineAlignedChildIndex="0"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/name"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingRight="4dip"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="marquee"
android:layout_marginBottom="2dip" />
<TextView android:id="@+id/size"
android:layout_gravity="center_vertical|right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:baselineAlignedChildIndex="0"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/description"
android:layout_gravity="center_vertical|left"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingRight="4dip"
android:singleLine="true"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView android:id="@+id/uptime"
android:layout_gravity="center_vertical|right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2010 Google Inc.
*
* 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="false"
android:fastScrollEnabled="true" />
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_running_services"
android:textAppearance="?android:attr/textAppearanceLarge" />
</FrameLayout>
<view class="com.android.settings.applications.RunningProcessesView$LinearColorBar"
android:id="@+id/color_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dp">
<TextView android:id="@+id/foregroundText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:color="?android:attr/textColorPrimaryInverse"
android:singleLine="true" />
<TextView android:id="@+id/backgroundText"
android:layout_gravity="center_vertical|right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:color="?android:attr/textColorPrimaryInverse"
android:singleLine="true" />
</view>
</LinearLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/all_details"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Summary information as per previous screen -->
<include
layout="@layout/running_processes_item"
android:id="@+id/snippet"/>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Icon and label of the service. -->
<include
layout="@layout/running_processes_item"
android:id="@+id/service"/>
<TextView android:id="@+id/comp_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="14dip"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="?android:attr/listDivider"/>
-->
<!-- Icon and label of the service. -->
<include
layout="@layout/running_processes_item"
android:id="@+id/service"/>
<TextView android:id="@+id/comp_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="14dip"
android:textAppearance="?android:attr/textAppearanceMedium" />
<include
layout="@layout/two_buttons_panel"
android:id="@+id/control_buttons_panel"/>
</LinearLayout>

View File

@@ -35,7 +35,7 @@
android:text="@string/no_running_services" android:text="@string/no_running_services"
android:textAppearance="?android:attr/textAppearanceLarge" /> android:textAppearance="?android:attr/textAppearanceLarge" />
</FrameLayout> </FrameLayout>
<view class="com.android.settings.RunningServices$LinearColorBar" <view class="com.android.settings.applications.RunningServices$LinearColorBar"
android:id="@+id/color_bar" android:id="@+id/color_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2008, 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.
*/
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/listSeparatorTextViewStyle" />

View File

@@ -1766,15 +1766,54 @@ found in the list of installed applications.</string>
<!-- Running services, button to cancel stopping of a service --> <!-- Running services, button to cancel stopping of a service -->
<string name="confirm_stop_cancel">Cancel</string> <string name="confirm_stop_cancel">Cancel</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: touch to stop</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 -->
<string name="service_client_name"><xliff:g id="client_name">%1$s</xliff:g>: touch to manage</string> <string name="service_client_name"><xliff:g id="client_name">%1$s</xliff:g></string>
<!-- Running services, summary of background processes --> <!-- Running services, summary of background processes -->
<string name="service_background_processes">Avail: <xliff:g id="free">%2$s</xliff:g>+<xliff:g id="memory">%3$s</xliff:g> in <xliff:g id="count">%1$d</xliff:g></string> <string name="service_background_processes"><xliff:g id="memory">%1$s</xliff:g> available</string>
<!-- Running services, summary of foreground processes --> <!-- Running services, summary of foreground processes -->
<string name="service_foreground_processes">Other: <xliff:g id="memory">%2$s</xliff:g> in <xliff:g id="count">%1$d</xliff:g></string> <string name="service_foreground_processes"><xliff:g id="memory">%1$s</xliff:g> in use</string>
<!-- Text to label a process entry with the process name. --> <!-- Text to label a process entry with the process name. -->
<string name="service_process_name">Process: <xliff:g id="process">%1$s</xliff:g></string> <string name="service_process_name"><xliff:g id="process">%1$s</xliff:g></string>
<!-- Descriptive text of a running process: singular process, singular service. -->
<string name="running_processes_item_description_s_s"><xliff:g id="numprocess">%1$d</xliff:g>
process and <xliff:g id="numservices">%2$d</xliff:g> service</string>
<!-- Descriptive text of a running process: singular process, plural service. -->
<string name="running_processes_item_description_s_p"><xliff:g id="numprocess">%1$d</xliff:g>
process and <xliff:g id="numservices">%2$d</xliff:g> services</string>
<!-- Descriptive text of a running process: plural process, singular service. -->
<string name="running_processes_item_description_p_s"><xliff:g id="numprocess">%1$d</xliff:g>
processes and <xliff:g id="numservices">%2$d</xliff:g> service</string>
<!-- Descriptive text of a running process: plural process, plural service. -->
<string name="running_processes_item_description_p_p"><xliff:g id="numprocess">%1$d</xliff:g>
processes and <xliff:g id="numservices">%2$d</xliff:g> services</string>
<!-- Details about an application's running services. -->
<string name="runningservicedetails_settings_title">Running application</string>
<!-- Message displayed when there are no active services in a process. -->
<string name="no_services">Not active</string>
<!-- Title for list of services. -->
<string name="runningservicedetails_services_title">Services</string>
<!-- Title for list of services. -->
<string name="runningservicedetails_processes_title">Processes</string>
<!-- Running service details, stop a service that has started itself. -->
<string name="service_stop">Stop</string>
<!-- Running service details, manage a service that is running for some other reason. -->
<string name="service_manage">Manage</string>
<!-- Running service details, default description for services that are started. -->
<string name="service_stop_description">This service was started by its
application. Stopping it may cause the application to misbehave.</string>
<!-- Running service details, default description for services that are managed. -->
<string name="service_manage_description"><xliff:g id="client_name">%1$s</xliff:g>:
currently in use. You can manage your settings to stop it.</string>
<!-- Description of the main process in the details. -->
<string name="main_running_process_description">Main process that is in use.</string>
<!-- Message that a process's service is in use. -->
<string name="process_service_in_use_description">Service <xliff:g id="comp_name">%1$s</xliff:g>
is in use.</string>
<!-- Message that a process's provider is in use. -->
<string name="process_provider_in_use_description">Provider <xliff:g id="comp_name">%1$s</xliff:g>
is in use.</string>
<!-- Language Settings --> <skip /> <!-- Language Settings --> <skip />
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings realted to locale and text --> <!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings realted to locale and text -->

View File

@@ -59,6 +59,15 @@
android:targetClass="com.android.settings.RunningServices" /> android:targetClass="com.android.settings.RunningServices" />
</PreferenceScreen> </PreferenceScreen>
<PreferenceScreen
android:key="power_usage"
android:title="@string/power_usage_summary_title"
android:summary="@string/power_usage_summary">
<intent android:action="android.intent.action.MAIN"
android:targetPackage="com.android.settings"
android:targetClass="com.android.settings.fuelgauge.PowerUsageSummary" />
</PreferenceScreen>
<PreferenceScreen <PreferenceScreen
android:title="@string/development_settings_title" android:title="@string/development_settings_title"
android:summary="@string/development_settings_summary"> android:summary="@string/development_settings_summary">

File diff suppressed because it is too large Load Diff

View File

@@ -16,10 +16,14 @@
* under the License. * under the License.
*/ */
package com.android.settings; package com.android.settings.applications;
import com.android.internal.content.PackageHelper; import com.android.internal.content.PackageHelper;
import com.android.settings.R; 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.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.AlertDialog; import android.app.AlertDialog;
@@ -312,7 +316,7 @@ public class InstalledAppDetails extends Activity implements View.OnClickListene
// Get application's name from intent // Get application's name from intent
Intent intent = getIntent(); Intent intent = getIntent();
final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME); final String packageName = intent.getData().getSchemeSpecificPart();
if (! initAppInfo(packageName)) { if (! initAppInfo(packageName)) {
return; // could not find package, finish called return; // could not find package, finish called
} }

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.settings; package com.android.settings.applications;
import com.android.settings.R; import com.android.settings.R;
@@ -41,6 +41,7 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.SystemClock; import android.os.SystemClock;
import android.provider.Settings;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -119,7 +120,6 @@ public class ManageApplications extends TabActivity implements
private static final boolean DEBUG_TIME = false; private static final boolean DEBUG_TIME = false;
// attributes used as keys when passing values to InstalledAppDetails activity // 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"; public static final String APP_CHG = "chg";
// attribute name used in receiver for tagging names of added/deleted packages // 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; private static final int MENU_OPTIONS_BASE = 0;
// Filter options used for displayed list of applications // 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_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 + 1;
public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 2; public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2;
public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 3;
public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4; public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5; 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; private boolean mSizesFirst = false;
// ListView used to display list // ListView used to display list
private ListView mListView; private ListView mListView;
// Custom view used to display running processes
private RunningProcessesView mRunningProcessesView;
// State variables used to figure out menu options and also // State variables used to figure out menu options and also
// initiate the first computation and loading of resources // initiate the first computation and loading of resources
private boolean mJustCreated = true; private boolean mJustCreated = true;
@@ -227,6 +228,14 @@ public class ManageApplications extends TabActivity implements
private long mLoadTimeStart; private long mLoadTimeStart;
private boolean mSetListViewLater = true; 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. * Handler class to handle messages for various operations.
* Most of the operations that effect Application related data * Most of the operations that effect Application related data
@@ -268,10 +277,8 @@ public class ManageApplications extends TabActivity implements
boolean status; boolean status;
long size; long size;
String formattedSize; String formattedSize;
ApplicationInfo info;
Bundle data; Bundle data;
String pkgName = null; String pkgName = null;
AppInfo appInfo;
data = msg.getData(); data = msg.getData();
if(data != null) { if(data != null) {
pkgName = data.getString(ATTR_PKG_NAME); pkgName = data.getString(ATTR_PKG_NAME);
@@ -359,12 +366,6 @@ public class ManageApplications extends TabActivity implements
} }
break; break;
} }
try {
info = mPm.getApplicationInfo(pkgName, 0);
} catch (NameNotFoundException e) {
Log.w(TAG, "Couldnt find application info for:"+pkgName);
break;
}
mObserver.invokeGetSizeInfo(pkgName); mObserver.invokeGetSizeInfo(pkgName);
break; break;
case ADD_PKG_DONE: case ADD_PKG_DONE:
@@ -456,7 +457,6 @@ public class ManageApplications extends TabActivity implements
// Set the adapter here. // Set the adapter here.
mJustCreated = false; mJustCreated = false;
mListView.setAdapter(mAppInfoAdapter); mListView.setAdapter(mAppInfoAdapter);
dismissLoadingMsg();
} }
} }
@@ -645,32 +645,6 @@ public class ManageApplications extends TabActivity implements
} }
} }
return appList; 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 { } else {
return installedAppList; return installedAppList;
} }
@@ -728,31 +702,6 @@ public class ManageApplications extends TabActivity implements
} }
} }
return retList; 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 { } else {
for (ApplicationInfo appInfo : pAppList) { for (ApplicationInfo appInfo : pAppList) {
if (matchFilter(filter, filterMap, appInfo.packageName)) { 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 // Some initialization code used when kicking off the size computation
private void initAppList(List<ApplicationInfo> appList, int filterOption) { private void initAppList(List<ApplicationInfo> appList, int filterOption) {
setProgressBarIndeterminateVisibility(true); setProgressBarIndeterminateVisibility(true);
@@ -1261,14 +1205,7 @@ public class ManageApplications extends TabActivity implements
private boolean shouldBeInList(int filterOption, ApplicationInfo info) { private boolean shouldBeInList(int filterOption, ApplicationInfo info) {
// Match filter here // Match filter here
if (filterOption == FILTER_APPS_RUNNING) { if (filterOption == FILTER_APPS_THIRD_PARTY) {
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 ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
return true; return true;
} else if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { } 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_DOWNLOADED = "Downloaded";
static final String TAB_RUNNING = "Running"; static final String TAB_RUNNING = "Running";
static final String TAB_ALL = "All"; static final String TAB_ALL = "All";
static final String TAB_SDCARD = "OnSdCard"; static final String TAB_SDCARD = "OnSdCard";
private View mRootView; private View mRootView;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -1627,18 +1597,31 @@ public class ManageApplications extends TabActivity implements
Intent intent = getIntent(); Intent intent = getIntent();
String action = intent.getAction(); String action = intent.getAction();
String defaultTabTag = TAB_DOWNLOADED; String defaultTabTag = TAB_DOWNLOADED;
if (intent.getComponent().getClassName().equals(
"com.android.settings.RunningServices")) {
defaultTabTag = TAB_RUNNING;
}
if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) { if (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;
mSizesFirst = true; 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(); mPm = getPackageManager();
// initialize some window features // initialize some window features
requestWindowFeature(Window.FEATURE_RIGHT_ICON); requestWindowFeature(Window.FEATURE_RIGHT_ICON);
requestWindowFeature(Window.FEATURE_PROGRESS);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
showLoadingMsg();
mDefaultAppIcon = Resources.getSystem().getDrawable( mDefaultAppIcon = Resources.getSystem().getDrawable(
com.android.internal.R.drawable.sym_def_app_icon); com.android.internal.R.drawable.sym_def_app_icon);
mInvalidSizeStr = getText(R.string.invalid_size_value); mInvalidSizeStr = getText(R.string.invalid_size_value);
@@ -1658,6 +1641,8 @@ public class ManageApplications extends TabActivity implements
lv.setOnItemClickListener(this); lv.setOnItemClickListener(this);
lv.setTextFilterEnabled(true); lv.setTextFilterEnabled(true);
mListView = lv; mListView = lv;
mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
R.id.running_processes);
if (DEBUG_TIME) { if (DEBUG_TIME) {
Log.i(TAG, "Total time in Activity.create:: " + Log.i(TAG, "Total time in Activity.create:: " +
(SystemClock.elapsedRealtime() - sCreate)+ " ms"); (SystemClock.elapsedRealtime() - sCreate)+ " ms");
@@ -1677,10 +1662,6 @@ public class ManageApplications extends TabActivity implements
.setIndicator(getString(R.string.filter_apps_third_party), .setIndicator(getString(R.string.filter_apps_third_party),
getResources().getDrawable(R.drawable.ic_tab_download)) getResources().getDrawable(R.drawable.ic_tab_download))
.setContent(this)); .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) tabHost.addTab(tabHost.newTabSpec(TAB_ALL)
.setIndicator(getString(R.string.filter_apps_all), .setIndicator(getString(R.string.filter_apps_all),
getResources().getDrawable(R.drawable.ic_tab_all)) getResources().getDrawable(R.drawable.ic_tab_all))
@@ -1689,8 +1670,95 @@ public class ManageApplications extends TabActivity implements
.setIndicator(getString(R.string.filter_apps_onsdcard), .setIndicator(getString(R.string.filter_apps_onsdcard),
getResources().getDrawable(R.drawable.ic_tab_sdcard)) getResources().getDrawable(R.drawable.ic_tab_sdcard))
.setContent(this)); .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.setCurrentTabByTag(defaultTabTag);
tabHost.setOnTabChangedListener(this); tabHost.setOnTabChangedListener(this);
selectView(TAB_RUNNING.equals(defaultTabTag) ? VIEW_RUNNING : VIEW_LIST);
}
@Override
public void onStart() {
super.onStart();
// Register receiver
mReceiver.registerReceiver();
sendMessageToHandler(INIT_PKG_INFO);
}
@Override
protected void onResume() {
super.onResume();
mActivityResumed = true;
if (mCurView == VIEW_RUNNING) {
mRunningProcessesView.doResume();
mResumedRunning = true;
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("sortOrder", mSortOrder);
outState.putInt("filterApps", mFilterApps);
outState.putString("defautTabTag", getTabHost().getCurrentTabTag());
outState.putBoolean("sizesFirst", mSizesFirst);
}
@Override
public Object onRetainNonConfigurationInstance() {
return mRunningProcessesView.doRetainNonConfigurationInstance();
}
@Override
protected void onPause() {
super.onPause();
mActivityResumed = false;
if (mResumedRunning) {
mRunningProcessesView.doPause();
mResumedRunning = false;
}
}
@Override
public void onStop() {
super.onStop();
// Stop the background threads
if (mResourceThread != null) {
mResourceThread.setAbort();
}
if (mSizeComputor != null) {
mSizeComputor.setAbort();
}
// clear all messages related to application list
clearMessagesInHandler();
// register receiver here
unregisterReceiver(mReceiver);
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
// Refresh package attributes
try {
ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
Bundle rData = new Bundle();
rData.putString(ATTR_PKG_NAME, mCurrentPkgName);
sendMessageToHandler(REMOVE_PKG, rData);
mCurrentPkgName = null;
}
}
}
// Avoid the restart and pause when orientation changes
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
} }
@Override @Override
@@ -1700,34 +1768,6 @@ public class ManageApplications extends TabActivity implements
super.onDestroy(); super.onDestroy();
} }
@Override
public Dialog onCreateDialog(int id, Bundle args) {
if (id == DLG_LOADING) {
ProgressDialog dlg = new ProgressDialog(this);
dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dlg.setMessage(getText(R.string.loading));
dlg.setIndeterminate(true);
dlg.setOnCancelListener(this);
return dlg;
}
return null;
}
private void showLoadingMsg() {
if (DEBUG_TIME) {
mLoadTimeStart = SystemClock.elapsedRealtime();
}
showDialog(DLG_LOADING);
if(localLOGV) Log.i(TAG, "Displaying Loading message");
}
private void dismissLoadingMsg() {
if(localLOGV) Log.i(TAG, "Dismissing Loading message");
dismissDialog(DLG_LOADING);
if (DEBUG_TIME) Log.i(TAG, "Displayed loading message for "+
(SystemClock.elapsedRealtime() - mLoadTimeStart) + " ms");
}
class AppInfoCache { class AppInfoCache {
final static boolean FILE_CACHE = true; final static boolean FILE_CACHE = true;
private static final String mFileCacheName="ManageAppsInfo.txt"; private static final String mFileCacheName="ManageAppsInfo.txt";
@@ -1923,36 +1963,6 @@ public class ManageApplications extends TabActivity implements
} }
} }
@Override
public void onStart() {
super.onStart();
// Register receiver
mReceiver.registerReceiver();
sendMessageToHandler(INIT_PKG_INFO);
}
@Override
public void onStop() {
super.onStop();
// Stop the background threads
if (mResourceThread != null) {
mResourceThread.setAbort();
}
if (mSizeComputor != null) {
mSizeComputor.setAbort();
}
// clear all messages related to application list
clearMessagesInHandler();
// register receiver here
unregisterReceiver(mReceiver);
}
// Avoid the restart and pause when orientation changes
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
/* /*
* comparator class used to sort AppInfo objects based on size * 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 // utility method used to start sub activity
private void startApplicationDetailsActivity() { private void startApplicationDetailsActivity() {
// Create intent to start new activity // Create intent to start new activity
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
intent.setClass(this, InstalledAppDetails.class); Uri.fromParts("package", mCurrentPkgName, null));
intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
// start new activity to display extended information // start new activity to display extended information
startActivityForResult(intent, INSTALLED_APP_DETAILS); startActivityForResult(intent, INSTALLED_APP_DETAILS);
} }
@@ -2049,33 +2058,19 @@ public class ManageApplications extends TabActivity implements
int newOption; int newOption;
if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) { if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_THIRD_PARTY; newOption = FILTER_APPS_THIRD_PARTY;
} else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_RUNNING;
} else if (TAB_ALL.equalsIgnoreCase(tabId)) { } else if (TAB_ALL.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_ALL; newOption = FILTER_APPS_ALL;
} else if (TAB_SDCARD.equalsIgnoreCase(tabId)) { } else if (TAB_SDCARD.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_SDCARD; newOption = FILTER_APPS_SDCARD;
} else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
selectView(VIEW_RUNNING);
return;
} else { } else {
// Invalid option. Do nothing // Invalid option. Do nothing
return; return;
} }
selectView(VIEW_LIST);
sendMessageToHandler(REORDER_LIST, newOption); 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;
}
}
}
} }

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -40,9 +40,9 @@ import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.android.settings.InstalledAppDetails;
import com.android.settings.ManageApplications;
import com.android.settings.R; 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 { 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)); startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
break; break;
case ACTION_APP_DETAILS: 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.setClass(this, InstalledAppDetails.class);
intent.putExtra(ManageApplications.APP_PKG_NAME, mPackages[0]);
startActivity(intent); startActivity(intent);
break; break;
case ACTION_SECURITY_SETTINGS: case ACTION_SECURITY_SETTINGS: