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
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 78c0f5b..c3b0236 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -350,7 +350,7 @@
</intent-filter>
</activity>
- <activity android:name="ManageApplications"
+ <activity android:name=".applications.ManageApplications"
android:label="@string/manageapplications_settings_title"
android:clearTaskOnLaunch="true"
android:configChanges="orientation|keyboardHidden">
@@ -365,15 +365,22 @@
</intent-filter>
</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>
- <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.VOICE_LAUNCH" />
+ <data android:scheme="package" />
</intent-filter>
</activity>
- <activity android:name="RunningServices"
+ <!--
+ <activity android:name=".applications.RunningServices"
android:label="@string/runningservices_settings_title"
android:clearTaskOnLaunch="true">
<intent-filter>
@@ -384,6 +391,24 @@
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
</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"
android:label="@string/location_security_settings_title"
diff --git a/res/layout/compute_sizes.xml b/res/layout/compute_sizes.xml
index 4ba5b3e..502488f 100755
--- a/res/layout/compute_sizes.xml
+++ b/res/layout/compute_sizes.xml
@@ -14,9 +14,20 @@
limitations under the License.
-->
-<ListView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/list"
- android:drawSelectorOnTop="false"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
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>
\ No newline at end of file
diff --git a/res/layout/manage_applications_item.xml b/res/layout/manage_applications_item.xml
index cdf4c9d..48274ce 100755
--- a/res/layout/manage_applications_item.xml
+++ b/res/layout/manage_applications_item.xml
@@ -31,7 +31,6 @@
<ImageView android:id="@+id/app_icon"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
- android:layout_marginLeft="5dip"
android:layout_marginRight="11dip"
android:layout_gravity="center_vertical"
android:scaleType="fitCenter"/>
diff --git a/res/layout/running_processes_item.xml b/res/layout/running_processes_item.xml
new file mode 100644
index 0000000..dacee9e
--- /dev/null
+++ b/res/layout/running_processes_item.xml
@@ -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>
diff --git a/res/layout/running_processes_view.xml b/res/layout/running_processes_view.xml
new file mode 100644
index 0000000..6810ba0
--- /dev/null
+++ b/res/layout/running_processes_view.xml
@@ -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>
diff --git a/res/layout/running_service_details.xml b/res/layout/running_service_details.xml
new file mode 100644
index 0000000..98c144c
--- /dev/null
+++ b/res/layout/running_service_details.xml
@@ -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>
+
diff --git a/res/layout/running_service_details_process.xml b/res/layout/running_service_details_process.xml
new file mode 100644
index 0000000..d12b486
--- /dev/null
+++ b/res/layout/running_service_details_process.xml
@@ -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>
diff --git a/res/layout/running_service_details_service.xml b/res/layout/running_service_details_service.xml
new file mode 100644
index 0000000..b9f9bbe
--- /dev/null
+++ b/res/layout/running_service_details_service.xml
@@ -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>
diff --git a/res/layout/running_services.xml b/res/layout/running_services.xml
index 5c0da6f..faca22c 100644
--- a/res/layout/running_services.xml
+++ b/res/layout/running_services.xml
@@ -35,7 +35,7 @@
android:text="@string/no_running_services"
android:textAppearance="?android:attr/textAppearanceLarge" />
</FrameLayout>
- <view class="com.android.settings.RunningServices$LinearColorBar"
+ <view class="com.android.settings.applications.RunningServices$LinearColorBar"
android:id="@+id/color_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/layout/separator_label.xml b/res/layout/separator_label.xml
new file mode 100644
index 0000000..b0e69fa
--- /dev/null
+++ b/res/layout/separator_label.xml
@@ -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" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6b426c8..ebd11f8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1766,16 +1766,55 @@
<!-- Running services, button to cancel stopping of a service -->
<string name="confirm_stop_cancel">Cancel</string>
<!-- 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 -->
- <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 -->
- <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 -->
- <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. -->
- <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 />
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings realted to locale and text -->
<string name="language_settings">Language & keyboard</string>
diff --git a/res/xml/application_settings.xml b/res/xml/application_settings.xml
index 4df21da..6ac58d1 100644
--- a/res/xml/application_settings.xml
+++ b/res/xml/application_settings.xml
@@ -59,6 +59,15 @@
android:targetClass="com.android.settings.RunningServices" />
</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
android:title="@string/development_settings_title"
android:summary="@string/development_settings_summary">
diff --git a/src/com/android/settings/RunningServices.java b/src/com/android/settings/RunningServices.java
deleted file mode 100644
index e67adf0..0000000
--- a/src/com/android/settings/RunningServices.java
+++ /dev/null
@@ -1,1172 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings;
-
-import com.android.settings.R;
-import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ListActivity;
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.text.format.DateUtils;
-import android.text.format.Formatter;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import java.io.FileInputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-public class RunningServices extends ListActivity
- implements AbsListView.RecyclerListener,
- DialogInterface.OnClickListener {
- static final String TAG = "RunningServices";
-
- /** Maximum number of services to retrieve */
- static final int MAX_SERVICES = 100;
-
- static final int MSG_UPDATE_TIMES = 1;
- static final int MSG_UPDATE_CONTENTS = 2;
- static final int MSG_REFRESH_UI = 3;
-
- static final long TIME_UPDATE_DELAY = 1000;
- static final long CONTENTS_UPDATE_DELAY = 2000;
-
- // Memory pages are 4K.
- static final long PAGE_SIZE = 4*1024;
-
- long SECONDARY_SERVER_MEM;
-
- final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
-
- ActivityManager mAm;
-
- State mState;
-
- StringBuilder mBuilder = new StringBuilder(128);
-
- BaseItem mCurSelected;
-
- int mProcessBgColor;
-
- LinearColorBar mColorBar;
- TextView mBackgroundProcessText;
- TextView mForegroundProcessText;
-
- int mLastNumBackgroundProcesses = -1;
- int mLastNumForegroundProcesses = -1;
- int mLastNumServiceProcesses = -1;
- long mLastBackgroundProcessMemory = -1;
- long mLastForegroundProcessMemory = -1;
- long mLastServiceProcessMemory = -1;
- long mLastAvailMemory = -1;
-
- Dialog mCurDialog;
-
- byte[] mBuffer = new byte[1024];
-
- class ActiveItem {
- View mRootView;
- BaseItem mItem;
- ActivityManager.RunningServiceInfo mService;
- ViewHolder mHolder;
- long mFirstRunTime;
-
- void updateTime(Context context) {
- if (mItem.mIsProcess) {
- String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
- if (!size.equals(mItem.mCurSizeStr)) {
- mItem.mCurSizeStr = size;
- mHolder.size.setText(size);
- }
- } else {
- if (mItem.mActiveSince >= 0) {
- mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
- (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
- } else {
- mHolder.size.setText(context.getResources().getText(
- R.string.service_restarting));
- }
- }
- }
- }
-
- static class BaseItem {
- final boolean mIsProcess;
-
- PackageItemInfo mPackageInfo;
- CharSequence mDisplayLabel;
- String mLabel;
- String mDescription;
-
- int mCurSeq;
-
- long mActiveSince;
- long mSize;
- String mSizeStr;
- String mCurSizeStr;
- boolean mNeedDivider;
-
- public BaseItem(boolean isProcess) {
- mIsProcess = isProcess;
- }
- }
-
- static class ServiceItem extends BaseItem {
- ActivityManager.RunningServiceInfo mRunningService;
- ServiceInfo mServiceInfo;
- boolean mShownAsStarted;
-
- public ServiceItem() {
- super(false);
- }
- }
-
- static class ProcessItem extends BaseItem {
- final HashMap<ComponentName, ServiceItem> mServices
- = new HashMap<ComponentName, ServiceItem>();
- final SparseArray<ProcessItem> mDependentProcesses
- = new SparseArray<ProcessItem>();
-
- final int mUid;
- final String mProcessName;
- int mPid;
-
- ProcessItem mClient;
- int mLastNumDependentProcesses;
-
- int mRunningSeq;
- ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
-
- // Purely for sorting.
- boolean mIsSystem;
- boolean mIsStarted;
- long mActiveSince;
-
- public ProcessItem(Context context, int uid, String processName) {
- super(true);
- mDescription = context.getResources().getString(
- R.string.service_process_name, processName);
- mUid = uid;
- mProcessName = processName;
- }
-
- void ensureLabel(PackageManager pm) {
- if (mLabel != null) {
- return;
- }
-
- try {
- ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0);
- if (ai.uid == mUid) {
- mDisplayLabel = ai.loadLabel(pm);
- mLabel = mDisplayLabel.toString();
- mPackageInfo = ai;
- return;
- }
- } catch (PackageManager.NameNotFoundException e) {
- }
-
- // If we couldn't get information about the overall
- // process, try to find something about the uid.
- String[] pkgs = pm.getPackagesForUid(mUid);
-
- // If there is one package with this uid, that is what we want.
- if (pkgs.length == 1) {
- try {
- ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
- mDisplayLabel = ai.loadLabel(pm);
- mLabel = mDisplayLabel.toString();
- mPackageInfo = ai;
- return;
- } catch (PackageManager.NameNotFoundException e) {
- }
- }
-
- // If there are multiple, see if one gives us the official name
- // for this uid.
- for (String name : pkgs) {
- try {
- PackageInfo pi = pm.getPackageInfo(name, 0);
- if (pi.sharedUserLabel != 0) {
- CharSequence nm = pm.getText(name,
- pi.sharedUserLabel, pi.applicationInfo);
- if (nm != null) {
- mDisplayLabel = nm;
- mLabel = nm.toString();
- mPackageInfo = pi.applicationInfo;
- return;
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- }
- }
-
- // If still don't have anything to display, just use the
- // service info.
- if (mServices.size() > 0) {
- mPackageInfo = mServices.values().iterator().next()
- .mServiceInfo.applicationInfo;
- mDisplayLabel = mPackageInfo.loadLabel(pm);
- mLabel = mDisplayLabel.toString();
- return;
- }
-
- // Finally... whatever, just pick the first package's name.
- try {
- ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
- mDisplayLabel = ai.loadLabel(pm);
- mLabel = mDisplayLabel.toString();
- mPackageInfo = ai;
- return;
- } catch (PackageManager.NameNotFoundException e) {
- }
- }
-
- boolean updateService(Context context,
- ActivityManager.RunningServiceInfo service) {
- final PackageManager pm = context.getPackageManager();
-
- boolean changed = false;
- ServiceItem si = mServices.get(service.service);
- if (si == null) {
- changed = true;
- si = new ServiceItem();
- si.mRunningService = service;
- try {
- si.mServiceInfo = pm.getServiceInfo(service.service, 0);
- } catch (PackageManager.NameNotFoundException e) {
- }
- if (si.mServiceInfo != null && (si.mServiceInfo.labelRes != 0
- || si.mServiceInfo.nonLocalizedLabel != null)) {
- si.mDisplayLabel = si.mServiceInfo.loadLabel(pm);
- si.mLabel = si.mDisplayLabel.toString();
- } else {
- si.mLabel = si.mRunningService.service.getClassName();
- int tail = si.mLabel.lastIndexOf('.');
- if (tail >= 0) {
- si.mLabel = si.mLabel.substring(tail+1, si.mLabel.length());
- }
- si.mDisplayLabel = si.mLabel;
- }
- si.mPackageInfo = si.mServiceInfo.applicationInfo;
- mServices.put(service.service, si);
- }
- si.mCurSeq = mCurSeq;
- si.mRunningService = service;
- long activeSince = service.restarting == 0 ? service.activeSince : -1;
- if (si.mActiveSince != activeSince) {
- si.mActiveSince = activeSince;
- changed = true;
- }
- if (service.clientPackage != null && service.clientLabel != 0) {
- if (si.mShownAsStarted) {
- si.mShownAsStarted = false;
- changed = true;
- }
- try {
- Resources clientr = pm.getResourcesForApplication(service.clientPackage);
- String label = clientr.getString(service.clientLabel);
- si.mDescription = context.getResources().getString(
- R.string.service_client_name, label);
- } catch (PackageManager.NameNotFoundException e) {
- si.mDescription = null;
- }
- } else {
- if (!si.mShownAsStarted) {
- si.mShownAsStarted = true;
- changed = true;
- }
- si.mDescription = context.getResources().getString(
- R.string.service_started_by_app);
- }
-
- return changed;
- }
-
- boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) {
- mSize = ((long)mem.getTotalPss()) * 1024;
- if (mCurSeq == curSeq) {
- String sizeStr = Formatter.formatShortFileSize(
- context, mSize);
- if (!sizeStr.equals(mSizeStr)){
- mSizeStr = sizeStr;
- // We update this on the second tick where we update just
- // the text in the current items, so no need to say we
- // changed here.
- return false;
- }
- }
- return false;
- }
-
- boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
- final int NP = mDependentProcesses.size();
- boolean changed = false;
- for (int i=0; i<NP; i++) {
- ProcessItem proc = mDependentProcesses.valueAt(i);
- if (proc.mClient != this) {
- changed = true;
- proc.mClient = this;
- }
- proc.mCurSeq = curSeq;
- proc.ensureLabel(pm);
- changed |= proc.buildDependencyChain(context, pm, curSeq);
- }
-
- if (mLastNumDependentProcesses != mDependentProcesses.size()) {
- changed = true;
- mLastNumDependentProcesses = mDependentProcesses.size();
- }
-
- return changed;
- }
-
- void addDependentProcesses(ArrayList<BaseItem> dest,
- ArrayList<ProcessItem> destProc) {
- final int NP = mDependentProcesses.size();
- for (int i=0; i<NP; i++) {
- ProcessItem proc = mDependentProcesses.valueAt(i);
- proc.addDependentProcesses(dest, destProc);
- dest.add(proc);
- if (proc.mPid > 0) {
- destProc.add(proc);
- }
- }
- }
- }
-
- static class ServiceProcessComparator implements Comparator<ProcessItem> {
- public int compare(ProcessItem object1, ProcessItem object2) {
- if (object1.mIsStarted != object2.mIsStarted) {
- // Non-started processes go last.
- return object1.mIsStarted ? -1 : 1;
- }
- if (object1.mIsSystem != object2.mIsSystem) {
- // System processes go below non-system.
- return object1.mIsSystem ? 1 : -1;
- }
- if (object1.mActiveSince != object2.mActiveSince) {
- // Remaining ones are sorted with the longest running
- // services last.
- return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
- }
- return 0;
- }
- }
-
- static class State {
- final SparseArray<HashMap<String, ProcessItem>> mProcesses
- = new SparseArray<HashMap<String, ProcessItem>>();
- final SparseArray<ProcessItem> mActiveProcesses
- = new SparseArray<ProcessItem>();
- final ServiceProcessComparator mServiceProcessComparator
- = new ServiceProcessComparator();
-
- // Temporary for finding process dependencies.
- final SparseArray<ProcessItem> mRunningProcesses
- = new SparseArray<ProcessItem>();
-
- final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
- final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
-
- int mSequence = 0;
-
- // ----- following protected by mLock -----
-
- // Lock for protecting the state that will be shared between the
- // background update thread and the UI thread.
- final Object mLock = new Object();
-
- ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
-
- int mNumBackgroundProcesses;
- long mBackgroundProcessMemory;
- int mNumForegroundProcesses;
- long mForegroundProcessMemory;
- int mNumServiceProcesses;
- long mServiceProcessMemory;
-
- boolean update(Context context, ActivityManager am) {
- final PackageManager pm = context.getPackageManager();
-
- mSequence++;
-
- boolean changed = false;
-
- List<ActivityManager.RunningServiceInfo> services
- = am.getRunningServices(MAX_SERVICES);
- final int NS = services != null ? services.size() : 0;
- for (int i=0; i<NS; i++) {
- ActivityManager.RunningServiceInfo si = services.get(i);
- // We are not interested in services that have not been started
- // and don't have a known client, because
- // there is nothing the user can do about them.
- if (!si.started && si.clientLabel == 0) {
- continue;
- }
- // We likewise don't care about services running in a
- // persistent process like the system or phone.
- if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
- != 0) {
- continue;
- }
-
- HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
- if (procs == null) {
- procs = new HashMap<String, ProcessItem>();
- mProcesses.put(si.uid, procs);
- }
- ProcessItem proc = procs.get(si.process);
- if (proc == null) {
- changed = true;
- proc = new ProcessItem(context, si.uid, si.process);
- procs.put(si.process, proc);
- }
-
- if (proc.mCurSeq != mSequence) {
- int pid = si.restarting == 0 ? si.pid : 0;
- if (pid != proc.mPid) {
- changed = true;
- if (proc.mPid != pid) {
- if (proc.mPid != 0) {
- mActiveProcesses.remove(proc.mPid);
- }
- if (pid != 0) {
- mActiveProcesses.put(pid, proc);
- }
- proc.mPid = pid;
- }
- }
- proc.mDependentProcesses.clear();
- proc.mCurSeq = mSequence;
- }
- changed |= proc.updateService(context, si);
- }
-
- // Now update the map of other processes that are running (but
- // don't have services actively running inside them).
- List<ActivityManager.RunningAppProcessInfo> processes
- = am.getRunningAppProcesses();
- final int NP = processes != null ? processes.size() : 0;
- for (int i=0; i<NP; i++) {
- ActivityManager.RunningAppProcessInfo pi = processes.get(i);
- ProcessItem proc = mActiveProcesses.get(pi.pid);
- if (proc == null) {
- // This process is not one that is a direct container
- // of a service, so look for it in the secondary
- // running list.
- proc = mRunningProcesses.get(pi.pid);
- if (proc == null) {
- proc = new ProcessItem(context, pi.uid, pi.processName);
- proc.mPid = pi.pid;
- mRunningProcesses.put(pi.pid, proc);
- }
- proc.mDependentProcesses.clear();
- }
- proc.mRunningSeq = mSequence;
- proc.mRunningProcessInfo = pi;
- }
-
- // Build the chains from client processes to the process they are
- // dependent on; also remove any old running processes.
- int NRP = mRunningProcesses.size();
- for (int i=0; i<NRP; i++) {
- ProcessItem proc = mRunningProcesses.valueAt(i);
- if (proc.mRunningSeq == mSequence) {
- int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
- if (clientPid != 0) {
- ProcessItem client = mActiveProcesses.get(clientPid);
- if (client == null) {
- client = mRunningProcesses.get(clientPid);
- }
- if (client != null) {
- client.mDependentProcesses.put(proc.mPid, proc);
- }
- } else {
- // In this pass the process doesn't have a client.
- // Clear to make sure if it later gets the same one
- // that we will detect the change.
- proc.mClient = null;
- }
- } else {
- mRunningProcesses.remove(mRunningProcesses.keyAt(i));
- }
- }
-
- // Follow the tree from all primary service processes to all
- // processes they are dependent on, marking these processes as
- // still being active and determining if anything has changed.
- final int NAP = mActiveProcesses.size();
- for (int i=0; i<NAP; i++) {
- ProcessItem proc = mActiveProcesses.valueAt(i);
- if (proc.mCurSeq == mSequence) {
- changed |= proc.buildDependencyChain(context, pm, mSequence);
- }
- }
-
- // Look for services and their primary processes that no longer exist...
- for (int i=0; i<mProcesses.size(); i++) {
- HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
- Iterator<ProcessItem> pit = procs.values().iterator();
- while (pit.hasNext()) {
- ProcessItem pi = pit.next();
- if (pi.mCurSeq == mSequence) {
- pi.ensureLabel(pm);
- if (pi.mPid == 0) {
- // Sanity: a non-process can't be dependent on
- // anything.
- pi.mDependentProcesses.clear();
- }
- } else {
- changed = true;
- pit.remove();
- if (procs.size() == 0) {
- mProcesses.remove(mProcesses.keyAt(i));
- }
- if (pi.mPid != 0) {
- mActiveProcesses.remove(pi.mPid);
- }
- continue;
- }
- Iterator<ServiceItem> sit = pi.mServices.values().iterator();
- while (sit.hasNext()) {
- ServiceItem si = sit.next();
- if (si.mCurSeq != mSequence) {
- changed = true;
- sit.remove();
- }
- }
- }
- }
-
- if (changed) {
- // First determine an order for the services.
- ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
- for (int i=0; i<mProcesses.size(); i++) {
- for (ProcessItem pi : mProcesses.valueAt(i).values()) {
- pi.mIsSystem = false;
- pi.mIsStarted = true;
- pi.mActiveSince = Long.MAX_VALUE;
- for (ServiceItem si : pi.mServices.values()) {
- if (si.mServiceInfo != null
- && (si.mServiceInfo.applicationInfo.flags
- & ApplicationInfo.FLAG_SYSTEM) != 0) {
- pi.mIsSystem = true;
- }
- if (si.mRunningService != null
- && si.mRunningService.clientLabel != 0) {
- pi.mIsStarted = false;
- if (pi.mActiveSince > si.mRunningService.activeSince) {
- pi.mActiveSince = si.mRunningService.activeSince;
- }
- }
- }
- sortedProcesses.add(pi);
- }
- }
-
- Collections.sort(sortedProcesses, mServiceProcessComparator);
-
- ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
- mProcessItems.clear();
- for (int i=0; i<sortedProcesses.size(); i++) {
- ProcessItem pi = sortedProcesses.get(i);
- pi.mNeedDivider = false;
- // First add processes we are dependent on.
- pi.addDependentProcesses(newItems, mProcessItems);
- // And add the process itself.
- newItems.add(pi);
- if (pi.mPid > 0) {
- mProcessItems.add(pi);
- }
- // And finally the services running in it.
- boolean needDivider = false;
- for (ServiceItem si : pi.mServices.values()) {
- si.mNeedDivider = needDivider;
- needDivider = true;
- newItems.add(si);
- }
- }
- synchronized (mLock) {
- mItems = newItems;
- }
- }
-
- // Count number of interesting other (non-active) processes, and
- // build a list of all processes we will retrieve memory for.
- mAllProcessItems.clear();
- mAllProcessItems.addAll(mProcessItems);
- int numBackgroundProcesses = 0;
- int numForegroundProcesses = 0;
- int numServiceProcesses = 0;
- NRP = mRunningProcesses.size();
- for (int i=0; i<NRP; i++) {
- ProcessItem proc = mRunningProcesses.valueAt(i);
- if (proc.mCurSeq != mSequence) {
- // We didn't hit this process as a dependency on one
- // of our active ones, so add it up if needed.
- if (proc.mRunningProcessInfo.importance >=
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
- numBackgroundProcesses++;
- mAllProcessItems.add(proc);
- } else if (proc.mRunningProcessInfo.importance <=
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
- numForegroundProcesses++;
- mAllProcessItems.add(proc);
- } else {
- Log.i(TAG, "Unknown non-service process: "
- + proc.mProcessName + " #" + proc.mPid);
- }
- } else {
- numServiceProcesses++;
- }
- }
-
- long backgroundProcessMemory = 0;
- long foregroundProcessMemory = 0;
- long serviceProcessMemory = 0;
- try {
- final int numProc = mAllProcessItems.size();
- int[] pids = new int[numProc];
- for (int i=0; i<numProc; i++) {
- pids[i] = mAllProcessItems.get(i).mPid;
- }
- Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault()
- .getProcessMemoryInfo(pids);
- for (int i=pids.length-1; i>=0; i--) {
- ProcessItem proc = mAllProcessItems.get(i);
- changed |= proc.updateSize(context, mem[i], mSequence);
- if (proc.mCurSeq == mSequence) {
- serviceProcessMemory += proc.mSize;
- } else if (proc.mRunningProcessInfo.importance >=
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
- backgroundProcessMemory += proc.mSize;
- } else if (proc.mRunningProcessInfo.importance <=
- ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
- foregroundProcessMemory += proc.mSize;
- }
- }
- } catch (RemoteException e) {
- }
-
- synchronized (mLock) {
- mNumBackgroundProcesses = numBackgroundProcesses;
- mNumForegroundProcesses = numForegroundProcesses;
- mNumServiceProcesses = numServiceProcesses;
- mBackgroundProcessMemory = backgroundProcessMemory;
- mForegroundProcessMemory = foregroundProcessMemory;
- mServiceProcessMemory = serviceProcessMemory;
- }
-
- return changed;
- }
-
- ArrayList<BaseItem> getCurrentItems() {
- synchronized (mLock) {
- return mItems;
- }
- }
- }
-
- static class TimeTicker extends TextView {
- public TimeTicker(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- }
-
- static class ViewHolder {
- ImageView separator;
- ImageView icon;
- TextView name;
- TextView description;
- TextView size;
- }
-
- class ServiceListAdapter extends BaseAdapter {
- final State mState;
- final LayoutInflater mInflater;
- ArrayList<BaseItem> mItems;
-
- ServiceListAdapter(State state) {
- mState = state;
- mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- refreshItems();
- }
-
- void refreshItems() {
- ArrayList<BaseItem> newItems = mState.getCurrentItems();
- if (mItems != newItems) {
- mItems = newItems;
- }
- if (mItems == null) {
- mItems = new ArrayList<BaseItem>();
- }
- }
-
- public boolean hasStableIds() {
- return true;
- }
-
- public int getCount() {
- return mItems.size();
- }
-
- public Object getItem(int position) {
- return mItems.get(position);
- }
-
- public long getItemId(int position) {
- return mItems.get(position).hashCode();
- }
-
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- public boolean isEnabled(int position) {
- return !mItems.get(position).mIsProcess;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- View v;
- if (convertView == null) {
- v = newView(parent);
- } else {
- v = convertView;
- }
- bindView(v, position);
- return v;
- }
-
- public View newView(ViewGroup parent) {
- View v = mInflater.inflate(R.layout.running_services_item, parent, false);
- ViewHolder h = new ViewHolder();
- h.separator = (ImageView)v.findViewById(R.id.separator);
- h.icon = (ImageView)v.findViewById(R.id.icon);
- h.name = (TextView)v.findViewById(R.id.name);
- h.description = (TextView)v.findViewById(R.id.description);
- h.size = (TextView)v.findViewById(R.id.size);
- v.setTag(h);
- return v;
- }
-
- public void bindView(View view, int position) {
- synchronized (mState.mLock) {
- ViewHolder vh = (ViewHolder) view.getTag();
- if (position >= mItems.size()) {
- // List must have changed since we last reported its
- // size... ignore here, we will be doing a data changed
- // to refresh the entire list.
- return;
- }
- BaseItem item = mItems.get(position);
- vh.name.setText(item.mDisplayLabel);
- vh.separator.setVisibility(item.mNeedDivider
- ? View.VISIBLE : View.INVISIBLE);
- ActiveItem ai = new ActiveItem();
- ai.mRootView = view;
- ai.mItem = item;
- ai.mHolder = vh;
- ai.mFirstRunTime = item.mActiveSince;
- vh.description.setText(item.mDescription);
- if (item.mIsProcess) {
- view.setBackgroundColor(mProcessBgColor);
- vh.icon.setImageDrawable(null);
- vh.icon.setVisibility(View.GONE);
- vh.description.setText(item.mDescription);
- item.mCurSizeStr = null;
- } else {
- view.setBackgroundDrawable(null);
- vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager()));
- vh.icon.setVisibility(View.VISIBLE);
- vh.description.setText(item.mDescription);
- ai.mFirstRunTime = item.mActiveSince;
- }
- ai.updateTime(RunningServices.this);
- mActiveItems.put(view, ai);
- }
- }
- }
-
- public static class LinearColorBar extends LinearLayout {
- private float mRedRatio;
- private float mYellowRatio;
- private float mGreenRatio;
-
- final Rect mRect = new Rect();
- final Paint mPaint = new Paint();
-
- public LinearColorBar(Context context, AttributeSet attrs) {
- super(context, attrs);
- setWillNotDraw(false);
- mPaint.setStyle(Paint.Style.FILL);
- }
-
- public void setRatios(float red, float yellow, float green) {
- mRedRatio = red;
- mYellowRatio = yellow;
- mGreenRatio = green;
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- int width = getWidth();
- mRect.top = 0;
- mRect.bottom = getHeight();
-
- int left = 0;
-
- int right = left + (int)(width*mRedRatio);
- if (left < right) {
- mRect.left = left;
- mRect.right = right;
- mPaint.setColor(0xffff8080);
- canvas.drawRect(mRect, mPaint);
- width -= (right-left);
- left = right;
- }
-
- right = left + (int)(width*mYellowRatio);
- if (left < right) {
- mRect.left = left;
- mRect.right = right;
- mPaint.setColor(0xffffff00);
- canvas.drawRect(mRect, mPaint);
- width -= (right-left);
- left = right;
- }
-
- right = left + width;
- if (left < right) {
- mRect.left = left;
- mRect.right = right;
- mPaint.setColor(0xff80ff80);
- canvas.drawRect(mRect, mPaint);
- }
- }
- }
-
- HandlerThread mBackgroundThread;
- final class BackgroundHandler extends Handler {
- public BackgroundHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_CONTENTS:
- Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
- cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0;
- mHandler.sendMessage(cmd);
- removeMessages(MSG_UPDATE_CONTENTS);
- msg = obtainMessage(MSG_UPDATE_CONTENTS);
- sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
- break;
- }
- }
- };
- BackgroundHandler mBackgroundHandler;
-
- final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_TIMES:
- Iterator<ActiveItem> it = mActiveItems.values().iterator();
- while (it.hasNext()) {
- ActiveItem ai = it.next();
- if (ai.mRootView.getWindowToken() == null) {
- // Clean out any dead views, just in case.
- it.remove();
- continue;
- }
- ai.updateTime(RunningServices.this);
- }
- removeMessages(MSG_UPDATE_TIMES);
- msg = obtainMessage(MSG_UPDATE_TIMES);
- sendMessageDelayed(msg, TIME_UPDATE_DELAY);
- break;
- case MSG_REFRESH_UI:
- refreshUi(msg.arg1 != 0);
- break;
- }
- }
- };
-
- private boolean matchText(byte[] buffer, int index, String text) {
- int N = text.length();
- if ((index+N) >= buffer.length) {
- return false;
- }
- for (int i=0; i<N; i++) {
- if (buffer[index+i] != text.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- private long extractMemValue(byte[] buffer, int index) {
- while (index < buffer.length && buffer[index] != '\n') {
- if (buffer[index] >= '0' && buffer[index] <= '9') {
- int start = index;
- index++;
- while (index < buffer.length && buffer[index] >= '0'
- && buffer[index] <= '9') {
- index++;
- }
- String str = new String(buffer, 0, start, index-start);
- return ((long)Integer.parseInt(str)) * 1024;
- }
- index++;
- }
- return 0;
- }
-
- private long readAvailMem() {
- try {
- long memFree = 0;
- long memCached = 0;
- FileInputStream is = new FileInputStream("/proc/meminfo");
- int len = is.read(mBuffer);
- is.close();
- final int BUFLEN = mBuffer.length;
- for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
- if (matchText(mBuffer, i, "MemFree")) {
- i += 7;
- memFree = extractMemValue(mBuffer, i);
- } else if (matchText(mBuffer, i, "Cached")) {
- i += 6;
- memCached = extractMemValue(mBuffer, i);
- }
- while (i < BUFLEN && mBuffer[i] != '\n') {
- i++;
- }
- }
- return memFree + memCached;
- } catch (java.io.FileNotFoundException e) {
- } catch (java.io.IOException e) {
- }
- return 0;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- mState = (State)getLastNonConfigurationInstance();
- if (mState == null) {
- mState = new State();
- }
- mProcessBgColor = 0xff505050;
- setContentView(R.layout.running_services);
- getListView().setDivider(null);
- getListView().setAdapter(new ServiceListAdapter(mState));
- mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
- mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
- mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
-
- // Magic! Implementation detail! Don't count on this!
- SECONDARY_SERVER_MEM =
- Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
- }
-
- void refreshUi(boolean dataChanged) {
- if (dataChanged) {
- ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter());
- adapter.refreshItems();
- adapter.notifyDataSetChanged();
- }
-
- // This is the amount of available memory until we start killing
- // background services.
- long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
- if (availMem < 0) {
- availMem = 0;
- }
-
- synchronized (mState.mLock) {
- if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
- || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
- || mLastAvailMemory != availMem) {
- mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
- mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
- mLastAvailMemory = availMem;
- String availStr = availMem != 0
- ? Formatter.formatShortFileSize(this, availMem) : "0";
- String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory);
- mBackgroundProcessText.setText(getResources().getString(
- R.string.service_background_processes,
- mLastNumBackgroundProcesses, availStr, sizeStr));
- }
- if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
- || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
- mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
- mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
- String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory);
- mForegroundProcessText.setText(getResources().getString(
- R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr));
- }
- mLastNumServiceProcesses = mState.mNumServiceProcesses;
- mLastServiceProcessMemory = mState.mServiceProcessMemory;
-
- float totalMem = availMem + mLastBackgroundProcessMemory
- + mLastForegroundProcessMemory + mLastServiceProcessMemory;
- mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
- mLastServiceProcessMemory/totalMem,
- (availMem+mLastBackgroundProcessMemory)/totalMem);
- }
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- BaseItem bi = (BaseItem)l.getAdapter().getItem(position);
- if (!bi.mIsProcess) {
- ServiceItem si = (ServiceItem)bi;
- if (si.mRunningService.clientLabel != 0) {
- mCurSelected = null;
- PendingIntent pi = mAm.getRunningServiceControlPanel(
- si.mRunningService.service);
- if (pi != null) {
- try {
- this.startIntentSender(pi.getIntentSender(), null,
- Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
- Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
- } catch (IntentSender.SendIntentException e) {
- Log.w(TAG, e);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, e);
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, e);
- }
- }
- } else {
- mCurSelected = bi;
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.confirm_stop_service);
- String msg = getResources().getString(
- R.string.confirm_stop_service_msg,
- si.mPackageInfo.loadLabel(getPackageManager()));
- builder.setMessage(msg);
- builder.setPositiveButton(R.string.confirm_stop_stop, this);
- builder.setNegativeButton(R.string.confirm_stop_cancel, null);
- builder.setCancelable(true);
- mCurDialog = builder.show();
- }
- } else {
- mCurSelected = null;
- }
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (mCurSelected != null) {
- stopService(new Intent().setComponent(
- ((ServiceItem)mCurSelected).mRunningService.service));
- if (mBackgroundHandler != null) {
- mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
- }
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mHandler.removeMessages(MSG_UPDATE_TIMES);
- if (mBackgroundThread != null) {
- mBackgroundThread.quit();
- mBackgroundThread = null;
- mBackgroundHandler = null;
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- refreshUi(mState.update(this, mAm));
- mBackgroundThread = new HandlerThread("RunningServices");
- mBackgroundThread.start();
- mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
- mHandler.removeMessages(MSG_UPDATE_TIMES);
- Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
- mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
- mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
- msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
- mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
- }
-
- @Override
- public Object onRetainNonConfigurationInstance() {
- return mState;
- }
-
- public void onMovedToScrapHeap(View view) {
- mActiveItems.remove(view);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mCurDialog != null) {
- mCurDialog.dismiss();
- }
- }
-}
diff --git a/src/com/android/settings/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
similarity index 98%
rename from src/com/android/settings/InstalledAppDetails.java
rename to src/com/android/settings/applications/InstalledAppDetails.java
index 0ca35b8..92fe9d2 100644
--- a/src/com/android/settings/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -16,10 +16,14 @@
* under the License.
*/
-package com.android.settings;
+package com.android.settings.applications;
import com.android.internal.content.PackageHelper;
import com.android.settings.R;
+import com.android.settings.R.id;
+import com.android.settings.R.layout;
+import com.android.settings.R.string;
+
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
@@ -312,7 +316,7 @@
// Get application's name from intent
Intent intent = getIntent();
- final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME);
+ final String packageName = intent.getData().getSchemeSpecificPart();
if (! initAppInfo(packageName)) {
return; // could not find package, finish called
}
diff --git a/src/com/android/settings/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
similarity index 93%
rename from src/com/android/settings/ManageApplications.java
rename to src/com/android/settings/applications/ManageApplications.java
index dfff8c9..52ea376 100644
--- a/src/com/android/settings/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.applications;
import com.android.settings.R;
@@ -41,6 +41,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
+import android.provider.Settings;
import android.text.format.Formatter;
import android.util.Log;
import android.view.LayoutInflater;
@@ -119,7 +120,6 @@
private static final boolean DEBUG_TIME = false;
// attributes used as keys when passing values to InstalledAppDetails activity
- public static final String APP_PKG_NAME = "pkg";
public static final String APP_CHG = "chg";
// attribute name used in receiver for tagging names of added/deleted packages
@@ -140,9 +140,8 @@
private static final int MENU_OPTIONS_BASE = 0;
// Filter options used for displayed list of applications
public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0;
- public static final int FILTER_APPS_RUNNING = MENU_OPTIONS_BASE + 1;
- public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 2;
- public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 3;
+ public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1;
+ public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2;
public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
@@ -220,6 +219,8 @@
private boolean mSizesFirst = false;
// ListView used to display list
private ListView mListView;
+ // Custom view used to display running processes
+ private RunningProcessesView mRunningProcessesView;
// State variables used to figure out menu options and also
// initiate the first computation and loading of resources
private boolean mJustCreated = true;
@@ -227,6 +228,14 @@
private long mLoadTimeStart;
private boolean mSetListViewLater = true;
+ // These are for keeping track of activity and tab switch state.
+ private int mCurView;
+ private boolean mCreatedRunning;
+
+ private boolean mResumedRunning;
+ private boolean mActivityResumed;
+ private Object mNonConfigInstance;
+
/*
* Handler class to handle messages for various operations.
* Most of the operations that effect Application related data
@@ -268,10 +277,8 @@
boolean status;
long size;
String formattedSize;
- ApplicationInfo info;
Bundle data;
String pkgName = null;
- AppInfo appInfo;
data = msg.getData();
if(data != null) {
pkgName = data.getString(ATTR_PKG_NAME);
@@ -359,12 +366,6 @@
}
break;
}
- try {
- info = mPm.getApplicationInfo(pkgName, 0);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Couldnt find application info for:"+pkgName);
- break;
- }
mObserver.invokeGetSizeInfo(pkgName);
break;
case ADD_PKG_DONE:
@@ -456,7 +457,6 @@
// Set the adapter here.
mJustCreated = false;
mListView.setAdapter(mAppInfoAdapter);
- dismissLoadingMsg();
}
}
@@ -645,32 +645,6 @@
}
}
return appList;
- } else if (filterOption == FILTER_APPS_RUNNING) {
- List<ApplicationInfo> appList =new ArrayList<ApplicationInfo> ();
- List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
- if ((procList == null) || (procList.size() == 0)) {
- return appList;
- }
- // Retrieve running processes from ActivityManager
- for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
- if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
- int size = appProcInfo.pkgList.length;
- for (int i = 0; i < size; i++) {
- ApplicationInfo appInfo = null;
- try {
- appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i],
- PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]);
- continue;
- }
- if(appInfo != null) {
- appList.add(appInfo);
- }
- }
- }
- }
- return appList;
} else {
return installedAppList;
}
@@ -728,31 +702,6 @@
}
}
return retList;
- } else if (filterOption == FILTER_APPS_RUNNING) {
- List<ActivityManager.RunningAppProcessInfo> procList = getRunningAppProcessesList();
- if ((procList == null) || (procList.size() == 0)) {
- return retList;
- }
- // Retrieve running processes from ActivityManager
- HashMap<String, ActivityManager.RunningAppProcessInfo> runningMap =
- new HashMap<String, ActivityManager.RunningAppProcessInfo>();
- for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) {
- if ((appProcInfo != null) && (appProcInfo.pkgList != null)){
- int size = appProcInfo.pkgList.length;
- for (int i = 0; i < size; i++) {
- runningMap.put(appProcInfo.pkgList[i], appProcInfo);
- }
- }
- }
- // Query list to find running processes in current list
- for (ApplicationInfo appInfo : pAppList) {
- if (runningMap.get(appInfo.packageName) != null) {
- if (matchFilter(filter, filterMap, appInfo.packageName)) {
- retList.add(appInfo);
- }
- }
- }
- return retList;
} else {
for (ApplicationInfo appInfo : pAppList) {
if (matchFilter(filter, filterMap, appInfo.packageName)) {
@@ -763,11 +712,6 @@
}
}
- private List<ActivityManager.RunningAppProcessInfo> getRunningAppProcessesList() {
- ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- return am.getRunningAppProcesses();
- }
-
// Some initialization code used when kicking off the size computation
private void initAppList(List<ApplicationInfo> appList, int filterOption) {
setProgressBarIndeterminateVisibility(true);
@@ -1261,14 +1205,7 @@
private boolean shouldBeInList(int filterOption, ApplicationInfo info) {
// Match filter here
- if (filterOption == FILTER_APPS_RUNNING) {
- List<ApplicationInfo> runningList = getInstalledApps(FILTER_APPS_RUNNING);
- for (ApplicationInfo running : runningList) {
- if (running.packageName.equalsIgnoreCase(info.packageName)) {
- return true;
- }
- }
- } else if (filterOption == FILTER_APPS_THIRD_PARTY) {
+ if (filterOption == FILTER_APPS_THIRD_PARTY) {
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
return true;
} else if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
@@ -1611,11 +1548,44 @@
}
}
+ static final int VIEW_NOTHING = 0;
+ static final int VIEW_LIST = 1;
+ static final int VIEW_RUNNING = 2;
+
+ private void selectView(int which) {
+ if (mCurView == which) {
+ return;
+ }
+
+ mCurView = which;
+
+ if (which == VIEW_LIST) {
+ if (mResumedRunning) {
+ mRunningProcessesView.doPause();
+ mResumedRunning = false;
+ }
+ mRunningProcessesView.setVisibility(View.GONE);
+ mListView.setVisibility(View.VISIBLE);
+ } else if (which == VIEW_RUNNING) {
+ if (!mCreatedRunning) {
+ mRunningProcessesView.doCreate(null, mNonConfigInstance);
+ mCreatedRunning = true;
+ }
+ if (mActivityResumed && !mResumedRunning) {
+ mRunningProcessesView.doResume();
+ mResumedRunning = true;
+ }
+ mRunningProcessesView.setVisibility(View.VISIBLE);
+ mListView.setVisibility(View.GONE);
+ }
+ }
+
static final String TAB_DOWNLOADED = "Downloaded";
static final String TAB_RUNNING = "Running";
static final String TAB_ALL = "All";
static final String TAB_SDCARD = "OnSdCard";
private View mRootView;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -1627,18 +1597,31 @@
Intent intent = getIntent();
String action = intent.getAction();
String defaultTabTag = TAB_DOWNLOADED;
+ if (intent.getComponent().getClassName().equals(
+ "com.android.settings.RunningServices")) {
+ defaultTabTag = TAB_RUNNING;
+ }
if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
mSortOrder = SORT_ORDER_SIZE;
mFilterApps = FILTER_APPS_ALL;
defaultTabTag = TAB_ALL;
mSizesFirst = true;
}
+
+ if (savedInstanceState != null) {
+ mSortOrder = savedInstanceState.getInt("sortOrder", mSortOrder);
+ mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps);
+ String tmp = savedInstanceState.getString("defaultTabTag");
+ if (tmp != null) defaultTabTag = tmp;
+ mSizesFirst = savedInstanceState.getBoolean("sizesFirst", mSizesFirst);
+ }
+
+ mNonConfigInstance = getLastNonConfigurationInstance();
+
mPm = getPackageManager();
// initialize some window features
requestWindowFeature(Window.FEATURE_RIGHT_ICON);
- requestWindowFeature(Window.FEATURE_PROGRESS);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- showLoadingMsg();
mDefaultAppIcon = Resources.getSystem().getDrawable(
com.android.internal.R.drawable.sym_def_app_icon);
mInvalidSizeStr = getText(R.string.invalid_size_value);
@@ -1658,6 +1641,8 @@
lv.setOnItemClickListener(this);
lv.setTextFilterEnabled(true);
mListView = lv;
+ mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
+ R.id.running_processes);
if (DEBUG_TIME) {
Log.i(TAG, "Total time in Activity.create:: " +
(SystemClock.elapsedRealtime() - sCreate)+ " ms");
@@ -1677,10 +1662,6 @@
.setIndicator(getString(R.string.filter_apps_third_party),
getResources().getDrawable(R.drawable.ic_tab_download))
.setContent(this));
- tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING)
- .setIndicator(getString(R.string.filter_apps_running),
- getResources().getDrawable(R.drawable.ic_tab_running))
- .setContent(this));
tabHost.addTab(tabHost.newTabSpec(TAB_ALL)
.setIndicator(getString(R.string.filter_apps_all),
getResources().getDrawable(R.drawable.ic_tab_all))
@@ -1689,8 +1670,95 @@
.setIndicator(getString(R.string.filter_apps_onsdcard),
getResources().getDrawable(R.drawable.ic_tab_sdcard))
.setContent(this));
+ tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING)
+ .setIndicator(getString(R.string.filter_apps_running),
+ getResources().getDrawable(R.drawable.ic_tab_running))
+ .setContent(this));
tabHost.setCurrentTabByTag(defaultTabTag);
tabHost.setOnTabChangedListener(this);
+
+ selectView(TAB_RUNNING.equals(defaultTabTag) ? VIEW_RUNNING : VIEW_LIST);
+ }
+
+ @Override
+ 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
@@ -1700,34 +1768,6 @@
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 {
final static boolean FILE_CACHE = true;
private static final String mFileCacheName="ManageAppsInfo.txt";
@@ -1923,36 +1963,6 @@
}
}
- @Override
- public void onStart() {
- super.onStart();
- // Register receiver
- mReceiver.registerReceiver();
- sendMessageToHandler(INIT_PKG_INFO);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- // Stop the background threads
- if (mResourceThread != null) {
- mResourceThread.setAbort();
- }
- if (mSizeComputor != null) {
- mSizeComputor.setAbort();
- }
- // clear all messages related to application list
- clearMessagesInHandler();
- // register receiver here
- unregisterReceiver(mReceiver);
- }
-
- // Avoid the restart and pause when orientation changes
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- }
-
/*
* comparator class used to sort AppInfo objects based on size
*/
@@ -1994,9 +2004,8 @@
// utility method used to start sub activity
private void startApplicationDetailsActivity() {
// Create intent to start new activity
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setClass(this, InstalledAppDetails.class);
- intent.putExtra(APP_PKG_NAME, mCurrentPkgName);
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.fromParts("package", mCurrentPkgName, null));
// start new activity to display extended information
startActivityForResult(intent, INSTALLED_APP_DETAILS);
}
@@ -2049,33 +2058,19 @@
int newOption;
if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_THIRD_PARTY;
- } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
- newOption = FILTER_APPS_RUNNING;
} else if (TAB_ALL.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_ALL;
} else if (TAB_SDCARD.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_SDCARD;
+ } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
+ selectView(VIEW_RUNNING);
+ return;
} else {
// Invalid option. Do nothing
return;
}
+
+ selectView(VIEW_LIST);
sendMessageToHandler(REORDER_LIST, newOption);
}
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode,
- Intent data) {
- if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
- // Refresh package attributes
- try {
- ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- } catch (NameNotFoundException e) {
- Bundle rData = new Bundle();
- rData.putString(ATTR_PKG_NAME, mCurrentPkgName);
- sendMessageToHandler(REMOVE_PKG, rData);
- mCurrentPkgName = null;
- }
- }
- }
}
diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java
new file mode 100644
index 0000000..9fbae6d
--- /dev/null
+++ b/src/com/android/settings/applications/RunningProcessesView.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.ActivityManager;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class RunningProcessesView extends FrameLayout
+ implements AdapterView.OnItemClickListener {
+
+ /** Maximum number of services to retrieve */
+ static final int MAX_SERVICES = 100;
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+
+ static final long TIME_UPDATE_DELAY = 1000;
+ static final long CONTENTS_UPDATE_DELAY = 2000;
+
+ // Memory pages are 4K.
+ static final long PAGE_SIZE = 4*1024;
+
+ long SECONDARY_SERVER_MEM;
+
+ final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
+
+ ActivityManager mAm;
+
+ RunningState mState;
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ RunningState.BaseItem mCurSelected;
+
+ ListView mListView;
+ LinearColorBar mColorBar;
+ TextView mBackgroundProcessText;
+ TextView mForegroundProcessText;
+
+ int mLastNumBackgroundProcesses = -1;
+ int mLastNumForegroundProcesses = -1;
+ int mLastNumServiceProcesses = -1;
+ long mLastBackgroundProcessMemory = -1;
+ long mLastForegroundProcessMemory = -1;
+ long mLastServiceProcessMemory = -1;
+ long mLastAvailMemory = -1;
+
+ Dialog mCurDialog;
+
+ byte[] mBuffer = new byte[1024];
+
+ public static class ActiveItem {
+ View mRootView;
+ RunningState.BaseItem mItem;
+ ActivityManager.RunningServiceInfo mService;
+ ViewHolder mHolder;
+ long mFirstRunTime;
+
+ void updateTime(Context context, StringBuilder builder) {
+ TextView uptimeView = null;
+
+ if (mItem instanceof RunningState.ServiceItem) {
+ // If we are displaying a service, then the service
+ // uptime goes at the top.
+ uptimeView = mHolder.size;
+
+ } else {
+ String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
+ if (!size.equals(mItem.mCurSizeStr)) {
+ mItem.mCurSizeStr = size;
+ mHolder.size.setText(size);
+ }
+
+ if (mItem instanceof RunningState.MergedItem) {
+ // This item represents both services and proceses,
+ // so show the service uptime below.
+ uptimeView = mHolder.uptime;
+ }
+ }
+
+ if (uptimeView != null) {
+ if (mItem.mActiveSince >= 0) {
+ uptimeView.setText(DateUtils.formatElapsedTime(builder,
+ (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
+ } else {
+ uptimeView.setText(context.getResources().getText(
+ R.string.service_restarting));
+ }
+ }
+ }
+ }
+
+ public static class ViewHolder {
+ public View rootView;
+ public ImageView icon;
+ public TextView name;
+ public TextView description;
+ public TextView size;
+ public TextView uptime;
+
+ public ViewHolder(View v) {
+ rootView = v;
+ icon = (ImageView)v.findViewById(R.id.icon);
+ name = (TextView)v.findViewById(R.id.name);
+ description = (TextView)v.findViewById(R.id.description);
+ size = (TextView)v.findViewById(R.id.size);
+ uptime = (TextView)v.findViewById(R.id.uptime);
+ v.setTag(this);
+ }
+
+ public ActiveItem bind(RunningState state, RunningState.BaseItem item,
+ StringBuilder builder) {
+ synchronized (state.mLock) {
+ name.setText(item.mDisplayLabel);
+ ActiveItem ai = new ActiveItem();
+ ai.mRootView = rootView;
+ ai.mItem = item;
+ ai.mHolder = this;
+ ai.mFirstRunTime = item.mActiveSince;
+ description.setText(item.mDescription);
+ item.mCurSizeStr = null;
+ icon.setImageDrawable(item.mPackageInfo.loadIcon(
+ rootView.getContext().getPackageManager()));
+ icon.setVisibility(View.VISIBLE);
+ ai.updateTime(rootView.getContext(), builder);
+ return ai;
+ }
+ }
+ }
+
+ static class TimeTicker extends TextView {
+ public TimeTicker(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ class ServiceListAdapter extends BaseAdapter {
+ final RunningState mState;
+ final LayoutInflater mInflater;
+ ArrayList<RunningState.MergedItem> mItems;
+
+ ServiceListAdapter(RunningState state) {
+ mState = state;
+ mInflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ refreshItems();
+ }
+
+ void refreshItems() {
+ ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
+ if (mItems != newItems) {
+ mItems = newItems;
+ }
+ if (mItems == null) {
+ mItems = new ArrayList<RunningState.MergedItem>();
+ }
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mItems.size();
+ }
+
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ public long getItemId(int position) {
+ return mItems.get(position).hashCode();
+ }
+
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ public boolean isEnabled(int position) {
+ return !mItems.get(position).mIsProcess;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (convertView == null) {
+ v = newView(parent);
+ } else {
+ v = convertView;
+ }
+ bindView(v, position);
+ return v;
+ }
+
+ public View newView(ViewGroup parent) {
+ View v = mInflater.inflate(R.layout.running_processes_item, parent, false);
+ new ViewHolder(v);
+ return v;
+ }
+
+ public void bindView(View view, int position) {
+ synchronized (mState.mLock) {
+ if (position >= mItems.size()) {
+ // List must have changed since we last reported its
+ // size... ignore here, we will be doing a data changed
+ // to refresh the entire list.
+ return;
+ }
+ ViewHolder vh = (ViewHolder) view.getTag();
+ RunningState.MergedItem item = mItems.get(position);
+ ActiveItem ai = vh.bind(mState, item, mBuilder);
+ mActiveItems.put(view, ai);
+ }
+ }
+ }
+
+ public static class LinearColorBar extends LinearLayout {
+ private float mRedRatio;
+ private float mYellowRatio;
+ private float mGreenRatio;
+
+ final Rect mRect = new Rect();
+ final Paint mPaint = new Paint();
+
+ public LinearColorBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+ mPaint.setStyle(Paint.Style.FILL);
+ }
+
+ public void setRatios(float red, float yellow, float green) {
+ mRedRatio = red;
+ mYellowRatio = yellow;
+ mGreenRatio = green;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int width = getWidth();
+ mRect.top = 0;
+ mRect.bottom = getHeight();
+
+ int left = 0;
+
+ int right = left + (int)(width*mRedRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffff8080);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + (int)(width*mYellowRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffffff00);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + width;
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xff80ff80);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+ }
+
+ HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ Iterator<ActiveItem> it = mActiveItems.values().iterator();
+ while (it.hasNext()) {
+ ActiveItem ai = it.next();
+ if (ai.mRootView.getWindowToken() == null) {
+ // Clean out any dead views, just in case.
+ it.remove();
+ continue;
+ }
+ ai.updateTime(getContext(), mBuilder);
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ break;
+ case MSG_REFRESH_UI:
+ refreshUi(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ private boolean matchText(byte[] buffer, int index, String text) {
+ int N = text.length();
+ if ((index+N) >= buffer.length) {
+ return false;
+ }
+ for (int i=0; i<N; i++) {
+ if (buffer[index+i] != text.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private long extractMemValue(byte[] buffer, int index) {
+ while (index < buffer.length && buffer[index] != '\n') {
+ if (buffer[index] >= '0' && buffer[index] <= '9') {
+ int start = index;
+ index++;
+ while (index < buffer.length && buffer[index] >= '0'
+ && buffer[index] <= '9') {
+ index++;
+ }
+ String str = new String(buffer, 0, start, index-start);
+ return ((long)Integer.parseInt(str)) * 1024;
+ }
+ index++;
+ }
+ return 0;
+ }
+
+ private long readAvailMem() {
+ try {
+ long memFree = 0;
+ long memCached = 0;
+ FileInputStream is = new FileInputStream("/proc/meminfo");
+ int len = is.read(mBuffer);
+ is.close();
+ final int BUFLEN = mBuffer.length;
+ for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
+ if (matchText(mBuffer, i, "MemFree")) {
+ i += 7;
+ memFree = extractMemValue(mBuffer, i);
+ } else if (matchText(mBuffer, i, "Cached")) {
+ i += 6;
+ memCached = extractMemValue(mBuffer, i);
+ }
+ while (i < BUFLEN && mBuffer[i] != '\n') {
+ i++;
+ }
+ }
+ return memFree + memCached;
+ } catch (java.io.FileNotFoundException e) {
+ } catch (java.io.IOException e) {
+ }
+ return 0;
+ }
+
+
+ void refreshUi(boolean dataChanged) {
+ if (dataChanged) {
+ ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
+ adapter.refreshItems();
+ adapter.notifyDataSetChanged();
+ }
+
+ // This is the amount of available memory until we start killing
+ // background services.
+ long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
+ if (availMem < 0) {
+ availMem = 0;
+ }
+
+ synchronized (mState.mLock) {
+ if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
+ || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
+ || mLastAvailMemory != availMem) {
+ mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
+ mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
+ mLastAvailMemory = availMem;
+ String sizeStr = Formatter.formatShortFileSize(getContext(),
+ mLastAvailMemory + mLastBackgroundProcessMemory);
+ mBackgroundProcessText.setText(getResources().getString(
+ R.string.service_background_processes, sizeStr));
+ }
+ if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
+ || mLastForegroundProcessMemory != mState.mForegroundProcessMemory
+ || mLastNumServiceProcesses != mState.mNumServiceProcesses
+ || mLastServiceProcessMemory != mState.mServiceProcessMemory) {
+ mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
+ mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
+ mLastNumServiceProcesses = mState.mNumServiceProcesses;
+ mLastServiceProcessMemory = mState.mServiceProcessMemory;
+ String sizeStr = Formatter.formatShortFileSize(getContext(),
+ mLastForegroundProcessMemory + mLastServiceProcessMemory);
+ mForegroundProcessText.setText(getResources().getString(
+ R.string.service_foreground_processes, sizeStr));
+ }
+
+ float totalMem = availMem + mLastBackgroundProcessMemory
+ + mLastForegroundProcessMemory + mLastServiceProcessMemory;
+ mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
+ mLastServiceProcessMemory/totalMem,
+ (availMem+mLastBackgroundProcessMemory)/totalMem);
+ }
+ }
+
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ ListView l = (ListView)parent;
+ RunningState.MergedItem mi = (RunningState.MergedItem)l.getAdapter().getItem(position);
+ mCurSelected = mi;
+ Intent intent = new Intent();
+ intent.putExtra(RunningServiceDetails.KEY_UID, mi.mProcess.mUid);
+ intent.putExtra(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName);
+ intent.setClass(getContext(), RunningServiceDetails.class);
+ getContext().startActivity(intent);
+ }
+
+ public void onMovedToScrapHeap(View view) {
+ mActiveItems.remove(view);
+ }
+
+ public RunningProcessesView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
+ mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ mState = (RunningState)nonConfigurationInstace;
+ if (mState == null) {
+ mState = new RunningState();
+ }
+ LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.running_processes_view, this);
+ mListView = (ListView)findViewById(android.R.id.list);
+ View emptyView = findViewById(com.android.internal.R.id.empty);
+ if (emptyView != null) {
+ mListView.setEmptyView(emptyView);
+ }
+ mListView.setOnItemClickListener(this);
+ mListView.setAdapter(new ServiceListAdapter(mState));
+ mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
+ mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
+ mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
+
+ // Magic! Implementation detail! Don't count on this!
+ SECONDARY_SERVER_MEM =
+ Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
+ }
+
+ public void doPause() {
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quit();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ }
+ }
+
+ public void doResume() {
+ refreshUi(mState.update(getContext(), mAm));
+ mBackgroundThread = new HandlerThread("RunningServices");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ }
+
+ public Object doRetainNonConfigurationInstance() {
+ return mState;
+ }
+}
diff --git a/src/com/android/settings/applications/RunningProcessesViewOld.java b/src/com/android/settings/applications/RunningProcessesViewOld.java
new file mode 100644
index 0000000..dc7302c
--- /dev/null
+++ b/src/com/android/settings/applications/RunningProcessesViewOld.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class RunningProcessesViewOld extends FrameLayout
+ implements AdapterView.OnItemClickListener {
+
+ static final String TAG = "RunningServices";
+
+ /** Maximum number of services to retrieve */
+ static final int MAX_SERVICES = 100;
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+
+ static final long TIME_UPDATE_DELAY = 1000;
+ static final long CONTENTS_UPDATE_DELAY = 2000;
+
+ // Memory pages are 4K.
+ static final long PAGE_SIZE = 4*1024;
+
+ long SECONDARY_SERVER_MEM;
+
+ final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
+
+ ActivityManager mAm;
+
+ RunningState mState;
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ RunningState.BaseItem mCurSelected;
+
+ int mProcessBgColor;
+
+ ListView mListView;
+ LinearColorBar mColorBar;
+ TextView mBackgroundProcessText;
+ TextView mForegroundProcessText;
+
+ int mLastNumBackgroundProcesses = -1;
+ int mLastNumForegroundProcesses = -1;
+ int mLastNumServiceProcesses = -1;
+ long mLastBackgroundProcessMemory = -1;
+ long mLastForegroundProcessMemory = -1;
+ long mLastServiceProcessMemory = -1;
+ long mLastAvailMemory = -1;
+
+ Dialog mCurDialog;
+
+ byte[] mBuffer = new byte[1024];
+
+ class ActiveItem {
+ View mRootView;
+ RunningState.BaseItem mItem;
+ ActivityManager.RunningServiceInfo mService;
+ ViewHolder mHolder;
+ long mFirstRunTime;
+
+ void updateTime(Context context) {
+ if (mItem.mIsProcess) {
+ String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
+ if (!size.equals(mItem.mCurSizeStr)) {
+ mItem.mCurSizeStr = size;
+ mHolder.size.setText(size);
+ }
+ } else {
+ if (mItem.mActiveSince >= 0) {
+ mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
+ (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
+ } else {
+ mHolder.size.setText(context.getResources().getText(
+ R.string.service_restarting));
+ }
+ }
+ }
+ }
+
+ static class ViewHolder {
+ ImageView separator;
+ ImageView icon;
+ TextView name;
+ TextView description;
+ TextView size;
+ }
+
+ static class TimeTicker extends TextView {
+ public TimeTicker(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ class ServiceListAdapter extends BaseAdapter {
+ final RunningState mState;
+ final LayoutInflater mInflater;
+ ArrayList<RunningState.BaseItem> mItems;
+
+ ServiceListAdapter(RunningState state) {
+ mState = state;
+ mInflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ refreshItems();
+ }
+
+ void refreshItems() {
+ ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
+ if (mItems != newItems) {
+ mItems = newItems;
+ }
+ if (mItems == null) {
+ mItems = new ArrayList<RunningState.BaseItem>();
+ }
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mItems.size();
+ }
+
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ public long getItemId(int position) {
+ return mItems.get(position).hashCode();
+ }
+
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ public boolean isEnabled(int position) {
+ return !mItems.get(position).mIsProcess;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (convertView == null) {
+ v = newView(parent);
+ } else {
+ v = convertView;
+ }
+ bindView(v, position);
+ return v;
+ }
+
+ public View newView(ViewGroup parent) {
+ View v = mInflater.inflate(R.layout.running_services_item, parent, false);
+ ViewHolder h = new ViewHolder();
+ h.separator = (ImageView)v.findViewById(R.id.separator);
+ h.icon = (ImageView)v.findViewById(R.id.icon);
+ h.name = (TextView)v.findViewById(R.id.name);
+ h.description = (TextView)v.findViewById(R.id.description);
+ h.size = (TextView)v.findViewById(R.id.size);
+ v.setTag(h);
+ return v;
+ }
+
+ public void bindView(View view, int position) {
+ synchronized (mState.mLock) {
+ ViewHolder vh = (ViewHolder) view.getTag();
+ if (position >= mItems.size()) {
+ // List must have changed since we last reported its
+ // size... ignore here, we will be doing a data changed
+ // to refresh the entire list.
+ return;
+ }
+ RunningState.BaseItem item = mItems.get(position);
+ vh.name.setText(item.mDisplayLabel);
+ vh.separator.setVisibility(item.mNeedDivider
+ ? View.VISIBLE : View.INVISIBLE);
+ ActiveItem ai = new ActiveItem();
+ ai.mRootView = view;
+ ai.mItem = item;
+ ai.mHolder = vh;
+ ai.mFirstRunTime = item.mActiveSince;
+ vh.description.setText(item.mDescription);
+ if (item.mIsProcess) {
+ view.setBackgroundColor(mProcessBgColor);
+ vh.icon.setImageDrawable(null);
+ vh.icon.setVisibility(View.GONE);
+ vh.description.setText(item.mDescription);
+ item.mCurSizeStr = null;
+ } else {
+ view.setBackgroundDrawable(null);
+ vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(
+ getContext().getPackageManager()));
+ vh.icon.setVisibility(View.VISIBLE);
+ vh.description.setText(item.mDescription);
+ ai.mFirstRunTime = item.mActiveSince;
+ }
+ ai.updateTime(getContext());
+ mActiveItems.put(view, ai);
+ }
+ }
+ }
+
+ public static class LinearColorBar extends LinearLayout {
+ private float mRedRatio;
+ private float mYellowRatio;
+ private float mGreenRatio;
+
+ final Rect mRect = new Rect();
+ final Paint mPaint = new Paint();
+
+ public LinearColorBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+ mPaint.setStyle(Paint.Style.FILL);
+ }
+
+ public void setRatios(float red, float yellow, float green) {
+ mRedRatio = red;
+ mYellowRatio = yellow;
+ mGreenRatio = green;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int width = getWidth();
+ mRect.top = 0;
+ mRect.bottom = getHeight();
+
+ int left = 0;
+
+ int right = left + (int)(width*mRedRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffff8080);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + (int)(width*mYellowRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffffff00);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + width;
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xff80ff80);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+ }
+
+ HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ Iterator<ActiveItem> it = mActiveItems.values().iterator();
+ while (it.hasNext()) {
+ ActiveItem ai = it.next();
+ if (ai.mRootView.getWindowToken() == null) {
+ // Clean out any dead views, just in case.
+ it.remove();
+ continue;
+ }
+ ai.updateTime(getContext());
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ break;
+ case MSG_REFRESH_UI:
+ refreshUi(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ private boolean matchText(byte[] buffer, int index, String text) {
+ int N = text.length();
+ if ((index+N) >= buffer.length) {
+ return false;
+ }
+ for (int i=0; i<N; i++) {
+ if (buffer[index+i] != text.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private long extractMemValue(byte[] buffer, int index) {
+ while (index < buffer.length && buffer[index] != '\n') {
+ if (buffer[index] >= '0' && buffer[index] <= '9') {
+ int start = index;
+ index++;
+ while (index < buffer.length && buffer[index] >= '0'
+ && buffer[index] <= '9') {
+ index++;
+ }
+ String str = new String(buffer, 0, start, index-start);
+ return ((long)Integer.parseInt(str)) * 1024;
+ }
+ index++;
+ }
+ return 0;
+ }
+
+ private long readAvailMem() {
+ try {
+ long memFree = 0;
+ long memCached = 0;
+ FileInputStream is = new FileInputStream("/proc/meminfo");
+ int len = is.read(mBuffer);
+ is.close();
+ final int BUFLEN = mBuffer.length;
+ for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
+ if (matchText(mBuffer, i, "MemFree")) {
+ i += 7;
+ memFree = extractMemValue(mBuffer, i);
+ } else if (matchText(mBuffer, i, "Cached")) {
+ i += 6;
+ memCached = extractMemValue(mBuffer, i);
+ }
+ while (i < BUFLEN && mBuffer[i] != '\n') {
+ i++;
+ }
+ }
+ return memFree + memCached;
+ } catch (java.io.FileNotFoundException e) {
+ } catch (java.io.IOException e) {
+ }
+ return 0;
+ }
+
+
+ void refreshUi(boolean dataChanged) {
+ if (dataChanged) {
+ ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
+ adapter.refreshItems();
+ adapter.notifyDataSetChanged();
+ }
+
+ // This is the amount of available memory until we start killing
+ // background services.
+ long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
+ if (availMem < 0) {
+ availMem = 0;
+ }
+
+ synchronized (mState.mLock) {
+ if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
+ || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
+ || mLastAvailMemory != availMem) {
+ mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
+ mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
+ mLastAvailMemory = availMem;
+ String sizeStr = Formatter.formatShortFileSize(getContext(),
+ mLastAvailMemory + mLastBackgroundProcessMemory);
+ mBackgroundProcessText.setText(getResources().getString(
+ R.string.service_background_processes, sizeStr));
+ }
+ if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
+ || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
+ mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
+ mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
+ String sizeStr = Formatter.formatShortFileSize(getContext(),
+ mLastForegroundProcessMemory);
+ mForegroundProcessText.setText(getResources().getString(
+ R.string.service_foreground_processes, sizeStr));
+ }
+ mLastNumServiceProcesses = mState.mNumServiceProcesses;
+ mLastServiceProcessMemory = mState.mServiceProcessMemory;
+
+ float totalMem = availMem + mLastBackgroundProcessMemory
+ + mLastForegroundProcessMemory + mLastServiceProcessMemory;
+ mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
+ mLastServiceProcessMemory/totalMem,
+ (availMem+mLastBackgroundProcessMemory)/totalMem);
+ }
+ }
+
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ ListView l = (ListView)parent;
+ RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
+ mCurSelected = bi;
+ }
+
+ public void onMovedToScrapHeap(View view) {
+ mActiveItems.remove(view);
+ }
+
+ public RunningProcessesViewOld(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
+ mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ mState = (RunningState)nonConfigurationInstace;
+ if (mState == null) {
+ mState = new RunningState();
+ }
+ mProcessBgColor = 0xff505050;
+ LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.running_processes_view, this);
+ mListView = (ListView)findViewById(android.R.id.list);
+ View emptyView = findViewById(com.android.internal.R.id.empty);
+ if (emptyView != null) {
+ mListView.setEmptyView(emptyView);
+ }
+ mListView.setOnItemClickListener(this);
+ mListView.setDivider(null);
+ mListView.setAdapter(new ServiceListAdapter(mState));
+ mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
+ mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
+ mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
+
+ // Magic! Implementation detail! Don't count on this!
+ SECONDARY_SERVER_MEM =
+ Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
+ }
+
+ public void doPause() {
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quit();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ }
+ }
+
+ public void doResume() {
+ refreshUi(mState.update(getContext(), mAm));
+ mBackgroundThread = new HandlerThread("RunningServices");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ }
+
+ public Object doRetainNonConfigurationInstance() {
+ return mState;
+ }
+}
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
new file mode 100644
index 0000000..f33625e
--- /dev/null
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -0,0 +1,390 @@
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RunningServiceDetails extends Activity {
+ static final String TAG = "RunningServicesDetails";
+
+ static final String KEY_UID = "uid";
+ static final String KEY_PROCESS = "process";
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+
+ ActivityManager mAm;
+ LayoutInflater mInflater;
+
+ RunningState mState;
+
+ int mUid;
+ String mProcessName;
+
+ RunningState.MergedItem mMergedItem;
+
+ ViewGroup mAllDetails;
+ ViewGroup mSnippet;
+ RunningProcessesView.ActiveItem mSnippetActiveItem;
+ RunningProcessesView.ViewHolder mSnippetViewHolder;
+
+ TextView mServicesHeader;
+ TextView mProcessesHeader;
+ final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
+
+ class ActiveDetail implements View.OnClickListener {
+ View mRootView;
+ RunningProcessesView.ActiveItem mActiveItem;
+ RunningProcessesView.ViewHolder mViewHolder;
+ PendingIntent mManageIntent;
+
+ public void onClick(View v) {
+ if (mManageIntent != null) {
+ try {
+ startIntentSender(mManageIntent.getIntentSender(), null,
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
+ Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Log.w(TAG, e);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, e);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, e);
+ }
+ } else {
+ RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
+ stopService(new Intent().setComponent(si.mRunningService.service));
+ if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
+ // If there was only one service, we are finishing it,
+ // so no reason for the UI to stick around.
+ finish();
+ } else {
+ if (mBackgroundHandler != null) {
+ mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
+ }
+ }
+ }
+ }
+ }
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ if (mSnippetActiveItem != null) {
+ mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
+ }
+ for (int i=0; i<mActiveDetails.size(); i++) {
+ mActiveDetails.get(i).mActiveItem.updateTime(
+ RunningServiceDetails.this, mBuilder);
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
+ break;
+ case MSG_REFRESH_UI:
+ refreshUi(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ boolean findMergedItem() {
+ RunningState.MergedItem item = null;
+ ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
+ if (newItems != null) {
+ for (int i=0; i<newItems.size(); i++) {
+ RunningState.MergedItem mi = newItems.get(i);
+ if (mi.mProcess.mUid == mUid
+ && mi.mProcess.mProcessName.equals(mProcessName)) {
+ item = mi;
+ break;
+ }
+ }
+ }
+ if (mMergedItem != item) {
+ mMergedItem = item;
+ return true;
+ }
+ return false;
+ }
+
+ void addDetailViews() {
+ for (int i=mActiveDetails.size()-1; i>=0; i--) {
+ mAllDetails.removeView(mActiveDetails.get(i).mRootView);
+ }
+ mActiveDetails.clear();
+
+ if (mServicesHeader != null) {
+ mAllDetails.removeView(mServicesHeader);
+ mServicesHeader = null;
+ }
+
+ if (mProcessesHeader != null) {
+ mAllDetails.removeView(mProcessesHeader);
+ mProcessesHeader = null;
+ }
+
+ if (mMergedItem != null) {
+ for (int i=0; i<mMergedItem.mServices.size(); i++) {
+ if (i == 0) {
+ mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
+ mAllDetails, false);
+ mServicesHeader.setText(R.string.runningservicedetails_services_title);
+ mAllDetails.addView(mServicesHeader);
+ }
+ RunningState.ServiceItem si = mMergedItem.mServices.get(i);
+ ActiveDetail detail = new ActiveDetail();
+ View root = mInflater.inflate(R.layout.running_service_details_service,
+ mAllDetails, false);
+ mAllDetails.addView(root);
+ detail.mRootView = root;
+ detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
+ detail.mActiveItem = detail.mViewHolder.bind(mState, si, mBuilder);
+
+ if (si.mRunningService.clientLabel != 0) {
+ detail.mManageIntent = mAm.getRunningServiceControlPanel(
+ si.mRunningService.service);
+ }
+
+ TextView description = (TextView)root.findViewById(R.id.comp_description);
+ if (si.mServiceInfo.descriptionRes != 0) {
+ description.setText(getPackageManager().getText(
+ si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
+ si.mServiceInfo.applicationInfo));
+ } else {
+ if (detail.mManageIntent != null) {
+ try {
+ Resources clientr = getPackageManager().getResourcesForApplication(
+ si.mRunningService.clientPackage);
+ String label = clientr.getString(si.mRunningService.clientLabel);
+ description.setText(getString(R.string.service_manage_description,
+ label));
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ } else {
+ description.setText(getText(R.string.service_stop_description));
+ }
+ }
+
+ View button = root.findViewById(R.id.right_button);
+ button.setOnClickListener(detail);
+ ((TextView)button).setText(getText(detail.mManageIntent != null
+ ? R.string.service_manage : R.string.service_stop));
+ root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
+
+ mActiveDetails.add(detail);
+ }
+
+ boolean didProcess = false;
+ for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
+ RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
+ : mMergedItem.mOtherProcesses.get(i);
+ if (pi.mPid <= 0) {
+ continue;
+ }
+
+ if (!didProcess) {
+ mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
+ mAllDetails, false);
+ mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
+ mAllDetails.addView(mProcessesHeader);
+ didProcess = true;
+ }
+ ActiveDetail detail = new ActiveDetail();
+ View root = mInflater.inflate(R.layout.running_service_details_process,
+ mAllDetails, false);
+ mAllDetails.addView(root);
+ detail.mRootView = root;
+ detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
+ detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
+
+ TextView description = (TextView)root.findViewById(R.id.comp_description);
+ if (i < 0) {
+ description.setText(R.string.main_running_process_description);
+ } else {
+ int textid = 0;
+ CharSequence label = null;
+ ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
+ final ComponentName comp = rpi.importanceReasonComponent;
+ //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
+ // + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
+ switch (rpi.importanceReasonCode) {
+ case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
+ textid = R.string.process_provider_in_use_description;
+ List<ProviderInfo> providers = null;
+ if (comp != null) {
+ providers = getPackageManager()
+ .queryContentProviders(comp.getPackageName(),
+ rpi.uid, 0);
+ }
+ if (providers != null) {
+ for (int j=0; j<providers.size(); j++) {
+ ProviderInfo prov = providers.get(i);
+ if (comp.getClassName().equals(prov.name)) {
+ label = RunningState.makeLabel(getPackageManager(),
+ prov.name, prov);
+ break;
+ }
+ }
+ }
+ break;
+ case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
+ textid = R.string.process_service_in_use_description;
+ if (rpi.importanceReasonComponent != null) {
+ try {
+ ServiceInfo serv = getPackageManager().getServiceInfo(
+ rpi.importanceReasonComponent, 0);
+ label = RunningState.makeLabel(getPackageManager(),
+ serv.name, serv);
+ } catch (NameNotFoundException e) {
+ }
+ }
+ break;
+ }
+ if (textid != 0 && label != null) {
+ description.setText(getString(textid, label));
+ }
+ }
+
+ mActiveDetails.add(detail);
+ }
+ }
+ }
+
+ void refreshUi(boolean dataChanged) {
+ if (findMergedItem()) {
+ dataChanged = true;
+ }
+ if (dataChanged) {
+ if (mMergedItem != null) {
+ mSnippetActiveItem = mSnippetViewHolder.bind(mState,
+ mMergedItem, mBuilder);
+ } else if (mSnippetActiveItem != null) {
+ // Clear whatever is currently being shown.
+ mSnippetActiveItem.mHolder.size.setText("");
+ mSnippetActiveItem.mHolder.uptime.setText("");
+ mSnippetActiveItem.mHolder.description.setText(R.string.no_services);
+ } else {
+ // No merged item, never had one. Nothing to do.
+ finish();
+ return;
+ }
+ addDetailViews();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUid = getIntent().getIntExtra(KEY_UID, 0);
+ mProcessName = getIntent().getStringExtra(KEY_PROCESS);
+
+ mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ mState = (RunningState)getLastNonConfigurationInstance();
+ if (mState == null) {
+ mState = new RunningState();
+ }
+
+ setContentView(R.layout.running_service_details);
+
+ mAllDetails = (ViewGroup)findViewById(R.id.all_details);
+ mSnippet = (ViewGroup)findViewById(R.id.snippet);
+ mSnippet.setBackgroundResource(com.android.internal.R.drawable.title_bar_medium);
+ mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom());
+ mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quit();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ refreshUi(mState.update(this, mAm));
+ mBackgroundThread = new HandlerThread("RunningServices");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ return mState;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ }
+}
diff --git a/src/com/android/settings/applications/RunningServices.java b/src/com/android/settings/applications/RunningServices.java
new file mode 100644
index 0000000..703e8c7
--- /dev/null
+++ b/src/com/android/settings/applications/RunningServices.java
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class RunningServices extends ListActivity
+ implements AbsListView.RecyclerListener,
+ DialogInterface.OnClickListener {
+ static final String TAG = "RunningServices";
+
+ /** Maximum number of services to retrieve */
+ static final int MAX_SERVICES = 100;
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+
+ static final long TIME_UPDATE_DELAY = 1000;
+ static final long CONTENTS_UPDATE_DELAY = 2000;
+
+ // Memory pages are 4K.
+ static final long PAGE_SIZE = 4*1024;
+
+ long SECONDARY_SERVER_MEM;
+
+ final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
+
+ ActivityManager mAm;
+
+ RunningState mState;
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ RunningState.BaseItem mCurSelected;
+
+ int mProcessBgColor;
+
+ LinearColorBar mColorBar;
+ TextView mBackgroundProcessText;
+ TextView mForegroundProcessText;
+
+ int mLastNumBackgroundProcesses = -1;
+ int mLastNumForegroundProcesses = -1;
+ int mLastNumServiceProcesses = -1;
+ long mLastBackgroundProcessMemory = -1;
+ long mLastForegroundProcessMemory = -1;
+ long mLastServiceProcessMemory = -1;
+ long mLastAvailMemory = -1;
+
+ Dialog mCurDialog;
+
+ byte[] mBuffer = new byte[1024];
+
+ class ActiveItem {
+ View mRootView;
+ RunningState.BaseItem mItem;
+ ActivityManager.RunningServiceInfo mService;
+ ViewHolder mHolder;
+ long mFirstRunTime;
+
+ void updateTime(Context context) {
+ if (mItem.mIsProcess) {
+ String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
+ if (!size.equals(mItem.mCurSizeStr)) {
+ mItem.mCurSizeStr = size;
+ mHolder.size.setText(size);
+ }
+ } else {
+ if (mItem.mActiveSince >= 0) {
+ mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
+ (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
+ } else {
+ mHolder.size.setText(context.getResources().getText(
+ R.string.service_restarting));
+ }
+ }
+ }
+ }
+
+ static class ViewHolder {
+ ImageView separator;
+ ImageView icon;
+ TextView name;
+ TextView description;
+ TextView size;
+ }
+
+ static class TimeTicker extends TextView {
+ public TimeTicker(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ class ServiceListAdapter extends BaseAdapter {
+ final RunningState mState;
+ final LayoutInflater mInflater;
+ ArrayList<RunningState.BaseItem> mItems;
+
+ ServiceListAdapter(RunningState state) {
+ mState = state;
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ refreshItems();
+ }
+
+ void refreshItems() {
+ ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
+ if (mItems != newItems) {
+ mItems = newItems;
+ }
+ if (mItems == null) {
+ mItems = new ArrayList<RunningState.BaseItem>();
+ }
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mItems.size();
+ }
+
+ public Object getItem(int position) {
+ return mItems.get(position);
+ }
+
+ public long getItemId(int position) {
+ return mItems.get(position).hashCode();
+ }
+
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ public boolean isEnabled(int position) {
+ return !mItems.get(position).mIsProcess;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (convertView == null) {
+ v = newView(parent);
+ } else {
+ v = convertView;
+ }
+ bindView(v, position);
+ return v;
+ }
+
+ public View newView(ViewGroup parent) {
+ View v = mInflater.inflate(R.layout.running_services_item, parent, false);
+ ViewHolder h = new ViewHolder();
+ h.separator = (ImageView)v.findViewById(R.id.separator);
+ h.icon = (ImageView)v.findViewById(R.id.icon);
+ h.name = (TextView)v.findViewById(R.id.name);
+ h.description = (TextView)v.findViewById(R.id.description);
+ h.size = (TextView)v.findViewById(R.id.size);
+ v.setTag(h);
+ return v;
+ }
+
+ public void bindView(View view, int position) {
+ synchronized (mState.mLock) {
+ ViewHolder vh = (ViewHolder) view.getTag();
+ if (position >= mItems.size()) {
+ // List must have changed since we last reported its
+ // size... ignore here, we will be doing a data changed
+ // to refresh the entire list.
+ return;
+ }
+ RunningState.BaseItem item = mItems.get(position);
+ vh.name.setText(item.mDisplayLabel);
+ vh.separator.setVisibility(item.mNeedDivider
+ ? View.VISIBLE : View.INVISIBLE);
+ ActiveItem ai = new ActiveItem();
+ ai.mRootView = view;
+ ai.mItem = item;
+ ai.mHolder = vh;
+ ai.mFirstRunTime = item.mActiveSince;
+ vh.description.setText(item.mDescription);
+ if (item.mIsProcess) {
+ view.setBackgroundColor(mProcessBgColor);
+ vh.icon.setImageDrawable(null);
+ vh.icon.setVisibility(View.GONE);
+ vh.description.setText(item.mDescription);
+ item.mCurSizeStr = null;
+ } else {
+ view.setBackgroundDrawable(null);
+ vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager()));
+ vh.icon.setVisibility(View.VISIBLE);
+ vh.description.setText(item.mDescription);
+ ai.mFirstRunTime = item.mActiveSince;
+ }
+ ai.updateTime(RunningServices.this);
+ mActiveItems.put(view, ai);
+ }
+ }
+ }
+
+ public static class LinearColorBar extends LinearLayout {
+ private float mRedRatio;
+ private float mYellowRatio;
+ private float mGreenRatio;
+
+ final Rect mRect = new Rect();
+ final Paint mPaint = new Paint();
+
+ public LinearColorBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setWillNotDraw(false);
+ mPaint.setStyle(Paint.Style.FILL);
+ }
+
+ public void setRatios(float red, float yellow, float green) {
+ mRedRatio = red;
+ mYellowRatio = yellow;
+ mGreenRatio = green;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ int width = getWidth();
+ mRect.top = 0;
+ mRect.bottom = getHeight();
+
+ int left = 0;
+
+ int right = left + (int)(width*mRedRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffff8080);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + (int)(width*mYellowRatio);
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xffffff00);
+ canvas.drawRect(mRect, mPaint);
+ width -= (right-left);
+ left = right;
+ }
+
+ right = left + width;
+ if (left < right) {
+ mRect.left = left;
+ mRect.right = right;
+ mPaint.setColor(0xff80ff80);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+ }
+
+ HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ Iterator<ActiveItem> it = mActiveItems.values().iterator();
+ while (it.hasNext()) {
+ ActiveItem ai = it.next();
+ if (ai.mRootView.getWindowToken() == null) {
+ // Clean out any dead views, just in case.
+ it.remove();
+ continue;
+ }
+ ai.updateTime(RunningServices.this);
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ break;
+ case MSG_REFRESH_UI:
+ refreshUi(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ private boolean matchText(byte[] buffer, int index, String text) {
+ int N = text.length();
+ if ((index+N) >= buffer.length) {
+ return false;
+ }
+ for (int i=0; i<N; i++) {
+ if (buffer[index+i] != text.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private long extractMemValue(byte[] buffer, int index) {
+ while (index < buffer.length && buffer[index] != '\n') {
+ if (buffer[index] >= '0' && buffer[index] <= '9') {
+ int start = index;
+ index++;
+ while (index < buffer.length && buffer[index] >= '0'
+ && buffer[index] <= '9') {
+ index++;
+ }
+ String str = new String(buffer, 0, start, index-start);
+ return ((long)Integer.parseInt(str)) * 1024;
+ }
+ index++;
+ }
+ return 0;
+ }
+
+ private long readAvailMem() {
+ try {
+ long memFree = 0;
+ long memCached = 0;
+ FileInputStream is = new FileInputStream("/proc/meminfo");
+ int len = is.read(mBuffer);
+ is.close();
+ final int BUFLEN = mBuffer.length;
+ for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
+ if (matchText(mBuffer, i, "MemFree")) {
+ i += 7;
+ memFree = extractMemValue(mBuffer, i);
+ } else if (matchText(mBuffer, i, "Cached")) {
+ i += 6;
+ memCached = extractMemValue(mBuffer, i);
+ }
+ while (i < BUFLEN && mBuffer[i] != '\n') {
+ i++;
+ }
+ }
+ return memFree + memCached;
+ } catch (java.io.FileNotFoundException e) {
+ } catch (java.io.IOException e) {
+ }
+ return 0;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ mState = (RunningState)getLastNonConfigurationInstance();
+ if (mState == null) {
+ mState = new RunningState();
+ }
+ mProcessBgColor = 0xff505050;
+ setContentView(R.layout.running_services);
+ getListView().setDivider(null);
+ getListView().setAdapter(new ServiceListAdapter(mState));
+ mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
+ mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
+ mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
+
+ // Magic! Implementation detail! Don't count on this!
+ SECONDARY_SERVER_MEM =
+ Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
+ }
+
+ void refreshUi(boolean dataChanged) {
+ if (dataChanged) {
+ ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter());
+ adapter.refreshItems();
+ adapter.notifyDataSetChanged();
+ }
+
+ // This is the amount of available memory until we start killing
+ // background services.
+ long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
+ if (availMem < 0) {
+ availMem = 0;
+ }
+
+ synchronized (mState.mLock) {
+ if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
+ || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
+ || mLastAvailMemory != availMem) {
+ mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
+ mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
+ mLastAvailMemory = availMem;
+ String availStr = availMem != 0
+ ? Formatter.formatShortFileSize(this, availMem) : "0";
+ String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory);
+ mBackgroundProcessText.setText(getResources().getString(
+ R.string.service_background_processes,
+ mLastNumBackgroundProcesses, availStr, sizeStr));
+ }
+ if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
+ || mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
+ mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
+ mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
+ String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory);
+ mForegroundProcessText.setText(getResources().getString(
+ R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr));
+ }
+ mLastNumServiceProcesses = mState.mNumServiceProcesses;
+ mLastServiceProcessMemory = mState.mServiceProcessMemory;
+
+ float totalMem = availMem + mLastBackgroundProcessMemory
+ + mLastForegroundProcessMemory + mLastServiceProcessMemory;
+ mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
+ mLastServiceProcessMemory/totalMem,
+ (availMem+mLastBackgroundProcessMemory)/totalMem);
+ }
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
+ if (!bi.mIsProcess) {
+ RunningState.ServiceItem si = (RunningState.ServiceItem)bi;
+ if (si.mRunningService.clientLabel != 0) {
+ mCurSelected = null;
+ PendingIntent pi = mAm.getRunningServiceControlPanel(
+ si.mRunningService.service);
+ if (pi != null) {
+ try {
+ this.startIntentSender(pi.getIntentSender(), null,
+ Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
+ Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
+ } catch (IntentSender.SendIntentException e) {
+ Log.w(TAG, e);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, e);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, e);
+ }
+ }
+ } else {
+ mCurSelected = bi;
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.confirm_stop_service);
+ String msg = getResources().getString(
+ R.string.confirm_stop_service_msg,
+ si.mPackageInfo.loadLabel(getPackageManager()));
+ builder.setMessage(msg);
+ builder.setPositiveButton(R.string.confirm_stop_stop, this);
+ builder.setNegativeButton(R.string.confirm_stop_cancel, null);
+ builder.setCancelable(true);
+ mCurDialog = builder.show();
+ }
+ } else {
+ mCurSelected = null;
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (mCurSelected != null) {
+ stopService(new Intent().setComponent(
+ ((RunningState.ServiceItem)mCurSelected).mRunningService.service));
+ if (mBackgroundHandler != null) {
+ mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
+ }
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ if (mBackgroundThread != null) {
+ mBackgroundThread.quit();
+ mBackgroundThread = null;
+ mBackgroundHandler = null;
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ refreshUi(mState.update(this, mAm));
+ mBackgroundThread = new HandlerThread("RunningServices");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ return mState;
+ }
+
+ public void onMovedToScrapHeap(View view) {
+ mActiveItems.remove(view);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mCurDialog != null) {
+ mCurDialog.dismiss();
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java
new file mode 100644
index 0000000..11b40be
--- /dev/null
+++ b/src/com/android/settings/applications/RunningState.java
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import com.android.settings.R;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.os.Debug;
+import android.os.RemoteException;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Singleton for retrieving and monitoring the state about all running
+ * applications/processes/services.
+ */
+public class RunningState {
+
+ final SparseArray<HashMap<String, ProcessItem>> mProcesses
+ = new SparseArray<HashMap<String, ProcessItem>>();
+ final SparseArray<ProcessItem> mActiveProcesses
+ = new SparseArray<ProcessItem>();
+ final ServiceProcessComparator mServiceProcessComparator
+ = new ServiceProcessComparator();
+
+ // Temporary for finding process dependencies.
+ final SparseArray<ProcessItem> mRunningProcesses
+ = new SparseArray<ProcessItem>();
+
+ final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
+ final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
+
+ int mSequence = 0;
+
+ // ----- following protected by mLock -----
+
+ // Lock for protecting the state that will be shared between the
+ // background update thread and the UI thread.
+ final Object mLock = new Object();
+
+ ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
+ ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
+
+ int mNumBackgroundProcesses;
+ long mBackgroundProcessMemory;
+ int mNumForegroundProcesses;
+ long mForegroundProcessMemory;
+ int mNumServiceProcesses;
+ long mServiceProcessMemory;
+
+ static class BaseItem {
+ final boolean mIsProcess;
+
+ PackageItemInfo mPackageInfo;
+ CharSequence mDisplayLabel;
+ String mLabel;
+ String mDescription;
+
+ int mCurSeq;
+
+ long mActiveSince;
+ long mSize;
+ String mSizeStr;
+ String mCurSizeStr;
+ boolean mNeedDivider;
+
+ public BaseItem(boolean isProcess) {
+ mIsProcess = isProcess;
+ }
+ }
+
+ static class ServiceItem extends BaseItem {
+ ActivityManager.RunningServiceInfo mRunningService;
+ ServiceInfo mServiceInfo;
+ boolean mShownAsStarted;
+
+ MergedItem mMergedItem;
+
+ public ServiceItem() {
+ super(false);
+ }
+ }
+
+ static class ProcessItem extends BaseItem {
+ final HashMap<ComponentName, ServiceItem> mServices
+ = new HashMap<ComponentName, ServiceItem>();
+ final SparseArray<ProcessItem> mDependentProcesses
+ = new SparseArray<ProcessItem>();
+
+ final int mUid;
+ final String mProcessName;
+ int mPid;
+
+ ProcessItem mClient;
+ int mLastNumDependentProcesses;
+
+ int mRunningSeq;
+ ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
+
+ // Purely for sorting.
+ boolean mIsSystem;
+ boolean mIsStarted;
+ long mActiveSince;
+
+ public ProcessItem(Context context, int uid, String processName) {
+ super(true);
+ mDescription = context.getResources().getString(
+ R.string.service_process_name, processName);
+ mUid = uid;
+ mProcessName = processName;
+ }
+
+ void ensureLabel(PackageManager pm) {
+ if (mLabel != null) {
+ return;
+ }
+
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0);
+ if (ai.uid == mUid) {
+ mDisplayLabel = ai.loadLabel(pm);
+ mLabel = mDisplayLabel.toString();
+ mPackageInfo = ai;
+ return;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+
+ // If we couldn't get information about the overall
+ // process, try to find something about the uid.
+ String[] pkgs = pm.getPackagesForUid(mUid);
+
+ // If there is one package with this uid, that is what we want.
+ if (pkgs.length == 1) {
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
+ mDisplayLabel = ai.loadLabel(pm);
+ mLabel = mDisplayLabel.toString();
+ mPackageInfo = ai;
+ return;
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ // If there are multiple, see if one gives us the official name
+ // for this uid.
+ for (String name : pkgs) {
+ try {
+ PackageInfo pi = pm.getPackageInfo(name, 0);
+ if (pi.sharedUserLabel != 0) {
+ CharSequence nm = pm.getText(name,
+ pi.sharedUserLabel, pi.applicationInfo);
+ if (nm != null) {
+ mDisplayLabel = nm;
+ mLabel = nm.toString();
+ mPackageInfo = pi.applicationInfo;
+ return;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ // If still don't have anything to display, just use the
+ // service info.
+ if (mServices.size() > 0) {
+ mPackageInfo = mServices.values().iterator().next()
+ .mServiceInfo.applicationInfo;
+ mDisplayLabel = mPackageInfo.loadLabel(pm);
+ mLabel = mDisplayLabel.toString();
+ return;
+ }
+
+ // Finally... whatever, just pick the first package's name.
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
+ mDisplayLabel = ai.loadLabel(pm);
+ mLabel = mDisplayLabel.toString();
+ mPackageInfo = ai;
+ return;
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ boolean updateService(Context context,
+ ActivityManager.RunningServiceInfo service) {
+ final PackageManager pm = context.getPackageManager();
+
+ boolean changed = false;
+ ServiceItem si = mServices.get(service.service);
+ if (si == null) {
+ changed = true;
+ si = new ServiceItem();
+ si.mRunningService = service;
+ try {
+ si.mServiceInfo = pm.getServiceInfo(service.service, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ si.mDisplayLabel = makeLabel(pm,
+ si.mRunningService.service.getClassName(), si.mServiceInfo);
+ mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
+ si.mPackageInfo = si.mServiceInfo.applicationInfo;
+ mServices.put(service.service, si);
+ }
+ si.mCurSeq = mCurSeq;
+ si.mRunningService = service;
+ long activeSince = service.restarting == 0 ? service.activeSince : -1;
+ if (si.mActiveSince != activeSince) {
+ si.mActiveSince = activeSince;
+ changed = true;
+ }
+ if (service.clientPackage != null && service.clientLabel != 0) {
+ if (si.mShownAsStarted) {
+ si.mShownAsStarted = false;
+ changed = true;
+ }
+ try {
+ Resources clientr = pm.getResourcesForApplication(service.clientPackage);
+ String label = clientr.getString(service.clientLabel);
+ si.mDescription = context.getResources().getString(
+ R.string.service_client_name, label);
+ } catch (PackageManager.NameNotFoundException e) {
+ si.mDescription = null;
+ }
+ } else {
+ if (!si.mShownAsStarted) {
+ si.mShownAsStarted = true;
+ changed = true;
+ }
+ si.mDescription = context.getResources().getString(
+ R.string.service_started_by_app);
+ }
+
+ return changed;
+ }
+
+ boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) {
+ mSize = ((long)mem.getTotalPss()) * 1024;
+ if (mCurSeq == curSeq) {
+ String sizeStr = Formatter.formatShortFileSize(
+ context, mSize);
+ if (!sizeStr.equals(mSizeStr)){
+ mSizeStr = sizeStr;
+ // We update this on the second tick where we update just
+ // the text in the current items, so no need to say we
+ // changed here.
+ return false;
+ }
+ }
+ return false;
+ }
+
+ boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
+ final int NP = mDependentProcesses.size();
+ boolean changed = false;
+ for (int i=0; i<NP; i++) {
+ ProcessItem proc = mDependentProcesses.valueAt(i);
+ if (proc.mClient != this) {
+ changed = true;
+ proc.mClient = this;
+ }
+ proc.mCurSeq = curSeq;
+ proc.ensureLabel(pm);
+ changed |= proc.buildDependencyChain(context, pm, curSeq);
+ }
+
+ if (mLastNumDependentProcesses != mDependentProcesses.size()) {
+ changed = true;
+ mLastNumDependentProcesses = mDependentProcesses.size();
+ }
+
+ return changed;
+ }
+
+ void addDependentProcesses(ArrayList<BaseItem> dest,
+ ArrayList<ProcessItem> destProc) {
+ final int NP = mDependentProcesses.size();
+ for (int i=0; i<NP; i++) {
+ ProcessItem proc = mDependentProcesses.valueAt(i);
+ proc.addDependentProcesses(dest, destProc);
+ dest.add(proc);
+ if (proc.mPid > 0) {
+ destProc.add(proc);
+ }
+ }
+ }
+ }
+
+ static class MergedItem extends BaseItem {
+ ProcessItem mProcess;
+ final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
+ final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
+
+ MergedItem() {
+ super(false);
+ }
+
+ boolean update(Context context) {
+ mPackageInfo = mProcess.mPackageInfo;
+ mDisplayLabel = mProcess.mDisplayLabel;
+ mLabel = mProcess.mLabel;
+
+ int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size();
+ int numServices = mServices.size();
+ int resid = R.string.running_processes_item_description_s_s;
+ if (numProcesses != 1) {
+ resid = numServices != 1
+ ? R.string.running_processes_item_description_p_p
+ : R.string.running_processes_item_description_p_s;
+ } else if (numServices != 1) {
+ resid = R.string.running_processes_item_description_s_p;
+ }
+ mDescription = context.getResources().getString(resid, numProcesses, numServices);
+
+ mActiveSince = -1;
+ for (int i=0; i<mServices.size(); i++) {
+ ServiceItem si = mServices.get(i);
+ if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
+ mActiveSince = si.mActiveSince;
+ }
+ }
+
+ return false;
+ }
+
+ boolean updateSize(Context context) {
+ mSize = mProcess.mSize;
+ for (int i=0; i<mOtherProcesses.size(); i++) {
+ mSize += mOtherProcesses.get(i).mSize;
+ }
+
+ String sizeStr = Formatter.formatShortFileSize(
+ context, mSize);
+ if (!sizeStr.equals(mSizeStr)){
+ mSizeStr = sizeStr;
+ // We update this on the second tick where we update just
+ // the text in the current items, so no need to say we
+ // changed here.
+ return false;
+ }
+ return false;
+ }
+ }
+
+ static class ServiceProcessComparator implements Comparator<ProcessItem> {
+ public int compare(ProcessItem object1, ProcessItem object2) {
+ if (object1.mIsStarted != object2.mIsStarted) {
+ // Non-started processes go last.
+ return object1.mIsStarted ? -1 : 1;
+ }
+ if (object1.mIsSystem != object2.mIsSystem) {
+ // System processes go below non-system.
+ return object1.mIsSystem ? 1 : -1;
+ }
+ if (object1.mActiveSince != object2.mActiveSince) {
+ // Remaining ones are sorted with the longest running
+ // services last.
+ return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
+ }
+ return 0;
+ }
+ }
+
+ static CharSequence makeLabel(PackageManager pm,
+ String className, PackageItemInfo item) {
+ if (item != null && (item.labelRes != 0
+ || item.nonLocalizedLabel != null)) {
+ CharSequence label = item.loadLabel(pm);
+ if (label != null) {
+ return label;
+ }
+ }
+
+ String label = className;
+ int tail = label.lastIndexOf('.');
+ if (tail >= 0) {
+ label = label.substring(tail+1, label.length());
+ }
+ return label;
+ }
+
+ boolean update(Context context, ActivityManager am) {
+ final PackageManager pm = context.getPackageManager();
+
+ mSequence++;
+
+ boolean changed = false;
+
+ List<ActivityManager.RunningServiceInfo> services
+ = am.getRunningServices(RunningServices.MAX_SERVICES);
+ final int NS = services != null ? services.size() : 0;
+ for (int i=0; i<NS; i++) {
+ ActivityManager.RunningServiceInfo si = services.get(i);
+ // We are not interested in services that have not been started
+ // and don't have a known client, because
+ // there is nothing the user can do about them.
+ if (!si.started && si.clientLabel == 0) {
+ continue;
+ }
+ // We likewise don't care about services running in a
+ // persistent process like the system or phone.
+ if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
+ != 0) {
+ continue;
+ }
+
+ HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
+ if (procs == null) {
+ procs = new HashMap<String, ProcessItem>();
+ mProcesses.put(si.uid, procs);
+ }
+ ProcessItem proc = procs.get(si.process);
+ if (proc == null) {
+ changed = true;
+ proc = new ProcessItem(context, si.uid, si.process);
+ procs.put(si.process, proc);
+ }
+
+ if (proc.mCurSeq != mSequence) {
+ int pid = si.restarting == 0 ? si.pid : 0;
+ if (pid != proc.mPid) {
+ changed = true;
+ if (proc.mPid != pid) {
+ if (proc.mPid != 0) {
+ mActiveProcesses.remove(proc.mPid);
+ }
+ if (pid != 0) {
+ mActiveProcesses.put(pid, proc);
+ }
+ proc.mPid = pid;
+ }
+ }
+ proc.mDependentProcesses.clear();
+ proc.mCurSeq = mSequence;
+ }
+ changed |= proc.updateService(context, si);
+ }
+
+ // Now update the map of other processes that are running (but
+ // don't have services actively running inside them).
+ List<ActivityManager.RunningAppProcessInfo> processes
+ = am.getRunningAppProcesses();
+ final int NP = processes != null ? processes.size() : 0;
+ for (int i=0; i<NP; i++) {
+ ActivityManager.RunningAppProcessInfo pi = processes.get(i);
+ ProcessItem proc = mActiveProcesses.get(pi.pid);
+ if (proc == null) {
+ // This process is not one that is a direct container
+ // of a service, so look for it in the secondary
+ // running list.
+ proc = mRunningProcesses.get(pi.pid);
+ if (proc == null) {
+ proc = new ProcessItem(context, pi.uid, pi.processName);
+ proc.mPid = pi.pid;
+ mRunningProcesses.put(pi.pid, proc);
+ }
+ proc.mDependentProcesses.clear();
+ }
+ proc.mRunningSeq = mSequence;
+ proc.mRunningProcessInfo = pi;
+ }
+
+ // Build the chains from client processes to the process they are
+ // dependent on; also remove any old running processes.
+ int NRP = mRunningProcesses.size();
+ for (int i=0; i<NRP; i++) {
+ ProcessItem proc = mRunningProcesses.valueAt(i);
+ if (proc.mRunningSeq == mSequence) {
+ int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
+ if (clientPid != 0) {
+ ProcessItem client = mActiveProcesses.get(clientPid);
+ if (client == null) {
+ client = mRunningProcesses.get(clientPid);
+ }
+ if (client != null) {
+ client.mDependentProcesses.put(proc.mPid, proc);
+ }
+ } else {
+ // In this pass the process doesn't have a client.
+ // Clear to make sure if it later gets the same one
+ // that we will detect the change.
+ proc.mClient = null;
+ }
+ } else {
+ mRunningProcesses.remove(mRunningProcesses.keyAt(i));
+ }
+ }
+
+ // Follow the tree from all primary service processes to all
+ // processes they are dependent on, marking these processes as
+ // still being active and determining if anything has changed.
+ final int NAP = mActiveProcesses.size();
+ for (int i=0; i<NAP; i++) {
+ ProcessItem proc = mActiveProcesses.valueAt(i);
+ if (proc.mCurSeq == mSequence) {
+ changed |= proc.buildDependencyChain(context, pm, mSequence);
+ }
+ }
+
+ // Look for services and their primary processes that no longer exist...
+ for (int i=0; i<mProcesses.size(); i++) {
+ HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
+ Iterator<ProcessItem> pit = procs.values().iterator();
+ while (pit.hasNext()) {
+ ProcessItem pi = pit.next();
+ if (pi.mCurSeq == mSequence) {
+ pi.ensureLabel(pm);
+ if (pi.mPid == 0) {
+ // Sanity: a non-process can't be dependent on
+ // anything.
+ pi.mDependentProcesses.clear();
+ }
+ } else {
+ changed = true;
+ pit.remove();
+ if (procs.size() == 0) {
+ mProcesses.remove(mProcesses.keyAt(i));
+ }
+ if (pi.mPid != 0) {
+ mActiveProcesses.remove(pi.mPid);
+ }
+ continue;
+ }
+ Iterator<ServiceItem> sit = pi.mServices.values().iterator();
+ while (sit.hasNext()) {
+ ServiceItem si = sit.next();
+ if (si.mCurSeq != mSequence) {
+ changed = true;
+ sit.remove();
+ }
+ }
+ }
+ }
+
+ if (changed) {
+ // First determine an order for the services.
+ ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
+ for (int i=0; i<mProcesses.size(); i++) {
+ for (ProcessItem pi : mProcesses.valueAt(i).values()) {
+ pi.mIsSystem = false;
+ pi.mIsStarted = true;
+ pi.mActiveSince = Long.MAX_VALUE;
+ for (ServiceItem si : pi.mServices.values()) {
+ if (si.mServiceInfo != null
+ && (si.mServiceInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ pi.mIsSystem = true;
+ }
+ if (si.mRunningService != null
+ && si.mRunningService.clientLabel != 0) {
+ pi.mIsStarted = false;
+ if (pi.mActiveSince > si.mRunningService.activeSince) {
+ pi.mActiveSince = si.mRunningService.activeSince;
+ }
+ }
+ }
+ sortedProcesses.add(pi);
+ }
+ }
+
+ Collections.sort(sortedProcesses, mServiceProcessComparator);
+
+ ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
+ ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
+ mProcessItems.clear();
+ for (int i=0; i<sortedProcesses.size(); i++) {
+ ProcessItem pi = sortedProcesses.get(i);
+ pi.mNeedDivider = false;
+
+ int firstProc = mProcessItems.size();
+ // First add processes we are dependent on.
+ pi.addDependentProcesses(newItems, mProcessItems);
+ // And add the process itself.
+ newItems.add(pi);
+ if (pi.mPid > 0) {
+ mProcessItems.add(pi);
+ }
+
+ // Now add the services running in it.
+ MergedItem mergedItem = null;
+ boolean haveAllMerged = false;
+ boolean needDivider = false;
+ for (ServiceItem si : pi.mServices.values()) {
+ si.mNeedDivider = needDivider;
+ needDivider = true;
+ newItems.add(si);
+ if (si.mMergedItem != null) {
+ if (mergedItem != null && mergedItem != si.mMergedItem) {
+ haveAllMerged = false;
+ }
+ mergedItem = si.mMergedItem;
+ } else {
+ haveAllMerged = false;
+ }
+ }
+
+ if (!haveAllMerged || mergedItem == null
+ || mergedItem.mServices.size() != pi.mServices.size()) {
+ // Whoops, we need to build a new MergedItem!
+ mergedItem = new MergedItem();
+ for (ServiceItem si : pi.mServices.values()) {
+ mergedItem.mServices.add(si);
+ si.mMergedItem = mergedItem;
+ }
+ mergedItem.mProcess = pi;
+ mergedItem.mOtherProcesses.clear();
+ for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
+ mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
+ }
+ }
+
+ mergedItem.update(context);
+ newMergedItems.add(mergedItem);
+ }
+ synchronized (mLock) {
+ mItems = newItems;
+ mMergedItems = newMergedItems;
+ }
+ }
+
+ // Count number of interesting other (non-active) processes, and
+ // build a list of all processes we will retrieve memory for.
+ mAllProcessItems.clear();
+ mAllProcessItems.addAll(mProcessItems);
+ int numBackgroundProcesses = 0;
+ int numForegroundProcesses = 0;
+ int numServiceProcesses = 0;
+ NRP = mRunningProcesses.size();
+ for (int i=0; i<NRP; i++) {
+ ProcessItem proc = mRunningProcesses.valueAt(i);
+ if (proc.mCurSeq != mSequence) {
+ // We didn't hit this process as a dependency on one
+ // of our active ones, so add it up if needed.
+ if (proc.mRunningProcessInfo.importance >=
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+ numBackgroundProcesses++;
+ mAllProcessItems.add(proc);
+ } else if (proc.mRunningProcessInfo.importance <=
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
+ numForegroundProcesses++;
+ mAllProcessItems.add(proc);
+ } else {
+ Log.i(RunningServices.TAG, "Unknown non-service process: "
+ + proc.mProcessName + " #" + proc.mPid);
+ }
+ } else {
+ numServiceProcesses++;
+ }
+ }
+
+ long backgroundProcessMemory = 0;
+ long foregroundProcessMemory = 0;
+ long serviceProcessMemory = 0;
+ try {
+ final int numProc = mAllProcessItems.size();
+ int[] pids = new int[numProc];
+ for (int i=0; i<numProc; i++) {
+ pids[i] = mAllProcessItems.get(i).mPid;
+ }
+ Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault()
+ .getProcessMemoryInfo(pids);
+ for (int i=pids.length-1; i>=0; i--) {
+ ProcessItem proc = mAllProcessItems.get(i);
+ changed |= proc.updateSize(context, mem[i], mSequence);
+ if (proc.mCurSeq == mSequence) {
+ serviceProcessMemory += proc.mSize;
+ } else if (proc.mRunningProcessInfo.importance >=
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
+ backgroundProcessMemory += proc.mSize;
+ } else if (proc.mRunningProcessInfo.importance <=
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
+ foregroundProcessMemory += proc.mSize;
+ }
+ }
+ } catch (RemoteException e) {
+ }
+
+ for (int i=0; i<mMergedItems.size(); i++) {
+ mMergedItems.get(i).updateSize(context);
+ }
+
+ synchronized (mLock) {
+ mNumBackgroundProcesses = numBackgroundProcesses;
+ mNumForegroundProcesses = numForegroundProcesses;
+ mNumServiceProcesses = numServiceProcesses;
+ mBackgroundProcessMemory = backgroundProcessMemory;
+ mForegroundProcessMemory = foregroundProcessMemory;
+ mServiceProcessMemory = serviceProcessMemory;
+ }
+
+ return changed;
+ }
+
+ ArrayList<BaseItem> getCurrentItems() {
+ synchronized (mLock) {
+ return mItems;
+ }
+ }
+
+ ArrayList<MergedItem> getCurrentMergedItems() {
+ synchronized (mLock) {
+ return mMergedItems;
+ }
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java
index 4db968a..cc112f8 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java
@@ -40,9 +40,9 @@
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.settings.InstalledAppDetails;
-import com.android.settings.ManageApplications;
import com.android.settings.R;
+import com.android.settings.applications.InstalledAppDetails;
+import com.android.settings.applications.ManageApplications;
public class PowerUsageDetail extends Activity implements Button.OnClickListener {
@@ -248,9 +248,9 @@
startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
break;
case ACTION_APP_DETAILS:
- Intent intent = new Intent(Intent.ACTION_VIEW);
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.fromParts("package", mPackages[0], null));
intent.setClass(this, InstalledAppDetails.class);
- intent.putExtra(ManageApplications.APP_PKG_NAME, mPackages[0]);
startActivity(intent);
break;
case ACTION_SECURITY_SETTINGS: