OSDN Git Service

Move process stats screen into memory screen.
authorJason Monk <jmonk@google.com>
Wed, 25 Mar 2015 13:46:30 +0000 (09:46 -0400)
committerJason Monk <jmonk@google.com>
Mon, 27 Apr 2015 14:08:52 +0000 (10:08 -0400)
 - Move process stats from developer options to top level
   settings item.
 - Some minor UI changes to the top of the page
 - Major UI updates to detail page, now shows info with processes
   as pref categories and services as prefs, with a way to kill
   them (taken from Running Services page).
 - Some major refactorings in code, in attempt to make it more
   usable
 - Added color bar on per app basis to visualize the avg/max
   relationship
 - Updated the way avg is calculated across multiple entries in
   ProcStatsPackageEntry to be more accurate
 - Change the way max memory is calculated in
   ProcStatsPackageEntry to be less accurate but more useful

Bug: 19443802
Change-Id: Ia6aaabe42c415c50997a09bfb814a6f6e5731772

23 files changed:
res/drawable/ic_cancel.xml [new file with mode: 0644]
res/drawable/ic_settings_memory.xml [new file with mode: 0644]
res/layout/app_item_linear_color.xml [new file with mode: 0755]
res/layout/cancel_pref_widget.xml [new file with mode: 0644]
res/layout/memory_key.xml [new file with mode: 0644]
res/layout/proc_stats_ui.xml [new file with mode: 0644]
res/layout/process_preference_category.xml [new file with mode: 0644]
res/layout/process_stats_details.xml
res/values/colors.xml
res/values/strings.xml
res/xml/app_memory_settings.xml [new file with mode: 0644]
res/xml/dashboard_categories.xml
res/xml/development_prefs.xml
res/xml/process_stats_summary.xml
src/com/android/settings/CancellablePreference.java [new file with mode: 0644]
src/com/android/settings/DevelopmentSettings.java
src/com/android/settings/SettingsActivity.java
src/com/android/settings/applications/ProcStatsData.java [new file with mode: 0644]
src/com/android/settings/applications/ProcStatsEntry.java
src/com/android/settings/applications/ProcStatsPackageEntry.java
src/com/android/settings/applications/ProcessStatsDetail.java
src/com/android/settings/applications/ProcessStatsPreference.java
src/com/android/settings/applications/ProcessStatsUi.java

diff --git a/res/drawable/ic_cancel.xml b/res/drawable/ic_cancel.xml
new file mode 100644 (file)
index 0000000..b90c18c
--- /dev/null
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.0,4.0C12.9,4.0 4.0,12.9 4.0,24.0s8.9,20.0 20.0,20.0c11.1,0.0 20.0,-8.9 20.0,-20.0S35.1,4.0 24.0,4.0zM34.0,31.2L31.2,34.0L24.0,26.8L16.8,34.0L14.0,31.2l7.2,-7.2L14.0,16.8l2.8,-2.8l7.2,7.2l7.2,-7.2l2.8,2.8L26.8,24.0L34.0,31.2z"/>
+</vector>
diff --git a/res/drawable/ic_settings_memory.xml b/res/drawable/ic_settings_memory.xml
new file mode 100644 (file)
index 0000000..a459465
--- /dev/null
@@ -0,0 +1,37 @@
+<!--
+     Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32dp"
+        android:height="32dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M6.0,4.0l0.0,16.0 12.0,0.0 0.0,-16.0 -12.0,0.0z
+        M6.0,5.25l0,1.5 -3,0 0,-1.5 3,0z
+        M6.0,9.25l0,1.5 -3,0 0,-1.5 3,0z
+        M6.0,13.25l0,1.5 -3,0 0,-1.5 3,0z
+        M6.0,17.25l0,1.5 -3,0 0,-1.5 3,0z
+        M21.0,5.25l0,1.5 -3,0 0,-1.5 3,0z
+        M21.0,9.25l0,1.5 -3,0 0,-1.5 3,0z
+        M21.0,13.25l0,1.5 -3,0 0,-1.5 3,0z
+        M21.0,17.25l0,1.5 -3,0 0,-1.5 3,0z
+
+        M11.5,14.5l1,0 0,3 -1,0 0,-3z
+        M13.25,14.5l1,0 0,3 -1,0 0,-3z
+        M15.0,14.5l1,0 0,3 -1,0 0,-3z
+        "
+        android:fillColor="#ff009587" />
+</vector>
diff --git a/res/layout/app_item_linear_color.xml b/res/layout/app_item_linear_color.xml
new file mode 100755 (executable)
index 0000000..62eebb9
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 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:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingTop="8dip"
+    android:paddingBottom="8dip">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="@android:dimen/app_icon_size"
+        android:layout_height="@android:dimen/app_icon_size"
+        android:layout_marginEnd="8dip"
+        android:scaleType="centerInside"
+        android:contentDescription="@null"
+        android:duplicateParentState="true" />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:duplicateParentState="true">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dip"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+            android:textAlignment="viewStart"
+            android:duplicateParentState="true" />
+
+        <TextView
+            android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@android:style/TextAppearance.Material.Body1"
+            android:textColor="?android:attr/textColorSecondary"
+            android:textAlignment="viewStart"
+            android:duplicateParentState="true" />
+
+    </LinearLayout>
+
+    <com.android.settings.applications.LinearColorBar
+        android:id="@+id/linear_color_bar"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="5dp"
+        android:duplicateParentState="true" />
+
+</LinearLayout>
diff --git a/res/layout/cancel_pref_widget.xml b/res/layout/cancel_pref_widget.xml
new file mode 100644 (file)
index 0000000..0b3ef21
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/cancel"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clickable="true"
+    android:src="@drawable/ic_cancel"
+    />
diff --git a/res/layout/memory_key.xml b/res/layout/memory_key.xml
new file mode 100644 (file)
index 0000000..62db2fc
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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:gravity="center_vertical|end"
+    android:layout_marginBottom="5dp" >
+
+    <ImageView
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:scaleType="centerInside"
+        android:src="@color/memory_avg_use"
+        android:contentDescription="@null" />
+
+    <TextView
+        android:id="@+id/memory_avg"
+        android:text="@string/memory_avg_use"
+        android:textAppearance="@android:style/TextAppearance.Material.Body1"
+        android:textColor="?android:attr/textColorSecondary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="16dp" />
+
+    <ImageView
+        android:layout_width="16dp"
+        android:layout_height="16dp"
+        android:scaleType="centerInside"
+        android:src="@color/memory_max_use"
+        android:contentDescription="@null" />
+
+    <TextView
+        android:id="@+id/memory_max"
+        android:text="@string/memory_max_use"
+        android:textAppearance="@android:style/TextAppearance.Material.Body1"
+        android:textColor="?android:attr/textColorSecondary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="16dp" />
+
+</LinearLayout>
diff --git a/res/layout/proc_stats_ui.xml b/res/layout/proc_stats_ui.xml
new file mode 100644 (file)
index 0000000..5f78178
--- /dev/null
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 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:id="@+id/all_details"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/memory_state"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dp"
+        android:layout_marginBottom="10dp"
+        android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+        />
+
+    <com.android.settings.applications.LinearColorBar
+        android:id="@+id/color_bar"
+        android:layout_width="match_parent"
+        android:layout_height="30dp"
+        />
+
+    <TextView
+        android:id="@+id/memory_used"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="2dp"
+        android:layout_marginBottom="10dp"
+        android:textAppearance="@android:style/TextAppearance.Material.Small"
+        android:textColor="?android:attr/textColorSecondary"
+        />
+
+    <include layout="@layout/memory_key" />
+
+</LinearLayout>
+
diff --git a/res/layout/process_preference_category.xml b/res/layout/process_preference_category.xml
new file mode 100644 (file)
index 0000000..18ea23c
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginBottom="16dip"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingTop="16dip">
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@android:style/TextAppearance.Material.Body2"
+        android:textColor="?android:attr/colorAccent" />
+
+    <TextView
+        android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:textAppearance="@android:style/TextAppearance.Material.Body2"
+        android:textColor="?android:attr/colorAccent" />
+
+</RelativeLayout>
index 738805a..aa402f6 100644 (file)
      limitations under the License.
 -->
 
-<ScrollView
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/all_details"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:clipToPadding="false"
-    android:scrollbarStyle="@integer/preference_scrollbar_style">
+    android:orientation="vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
 
-    <LinearLayout
-        android:id="@+id/all_details"
+    <com.android.settings.applications.LinearColorBar
+        android:id="@+id/color_bar"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:paddingTop="5dip"
-        android:paddingBottom="5dip"
-        android:orientation="vertical"
-        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
-        <include layout="@layout/app_percentage_item" />
-
-        <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:orientation="vertical">
-
-            <!-- Force stop and report buttons -->
-            <LinearLayout
-                    android:id="@+id/two_buttons_panel"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:paddingBottom="6dip"
-                    android:orientation="vertical">
-
-                <include layout="@layout/two_buttons_panel"/>
-            </LinearLayout>
-
-            <LinearLayout
-                    android:id="@+id/processes"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:orientation="vertical">
-
-                <!-- Insert process items here -->
+        android:layout_height="40dp"
+        android:layout_marginTop="12dp"
+        android:layout_marginBottom="10dp"
+        />
 
-            </LinearLayout>
+    <include layout="@layout/memory_key" />
 
-            <TextView
-                    android:id="@+id/services_label"
-                    style="?android:attr/listSeparatorTextViewStyle"
-                    android:text="@string/services_subtitle" />
-
-            <LinearLayout
-                    android:id="@+id/services"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:orientation="vertical">
-
-                <!-- Insert service items here -->
-
-            </LinearLayout>
-
-        </LinearLayout>
+    <!-- Force stop and report buttons -->
+    <LinearLayout
+        android:id="@+id/two_buttons_panel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingBottom="6dip"
+        android:orientation="vertical">
 
+        <include layout="@layout/two_buttons_panel"/>
     </LinearLayout>
 
-</ScrollView>
+</LinearLayout>
index 25a7341..1fbfc18 100644 (file)
@@ -86,4 +86,9 @@
     <color name="voice_interaction_highlight">#33b5e5</color>
 
     <drawable name="fp_enrollment_header_landscape">#009688</drawable>
+
+    <color name="memory_avg_use">#ff384248</color>
+    <color name="memory_max_use">#ff009587</color>
+    <color name="memory_remaining">#ffced7db</color>
+
 </resources>
index 41b96c2..0b98428 100644 (file)
     <!-- [CHAR LIMIT=NONE] Label for process stats, duration of time the stats are over;
          timeDuration is a duration such as "1h 30m" -->
     <string name="process_stats_total_duration"><xliff:g id="usedRam">%1$s</xliff:g>
-        of <xliff:g id="totalRam">%2$s</xliff:g> used over
+        of <xliff:g id="totalRam">%2$s</xliff:g> used over last
         <xliff:g id="timeDuration">%3$s</xliff:g></string>
+    <!-- [CHAR LIMIT=NONE] Label for process stats, duration of time the stats are over;
+         timeDuration is a duration such as "1h 30m" -->
+    <string name="process_stats_total_duration_percentage"><xliff:g id="percent" example="50%">%1$s</xliff:g>
+        of RAM used over
+        <xliff:g id="timeDuration">%2$s</xliff:g></string>
     <!-- [CHAR LIMIT=NONE] Label for process stats, text for stats type -->
     <string name="process_stats_type_background">Background</string>
     <!-- [CHAR LIMIT=NONE] Label for process stats, text for stats type -->
     <!-- [CHAR LIMIT=NONE] Label for process stats, text for stats type -->
     <string name="process_stats_type_cached">Cached</string>
     <!-- [CHAR LIMIT=NONE] Label for process stats, duration of time the stats are over -->
-    <string name="process_stats_memory_status">Device memory is currently
+    <string name="process_stats_memory_status">Memory is 
         <xliff:g id="memstate">%1$s</xliff:g></string>
     <!-- [CHAR LIMIT=NONE] Label OS "process" app -->
     <string name="process_stats_os_label">Android OS</string>
     <string name="menu_show_system">Show system</string>
     <!-- [CHAR LIMIT=NONE] Menu for manage apps to control whether system processes are hidden -->
     <string name="menu_hide_system">Hide system</string>
+    <!-- [CHAR LIMIT=NONE] Menu for process stats to control whether stats are shown
+         as percentages-->
+    <string name="menu_show_percentage">Show percentages</string>
     <!-- [CHAR LIMIT=NONE] Menu for process stats to control whether computation should be based
         on Uss (Unique Set Size) instead of Pss (Proportional Set Size) -->
     <string name="menu_use_uss">Use Uss</string>
     <!-- Description of the usage access setting [CHAR LIMIT=NONE] -->
     <string name="usage_access_description">Usage access allows an app to track what other apps you\'re using and how often, as well as your carrier, language settings, and other details.</string>
 
+    <!-- Title for screen showing recent memory usage of device [CHAR LIMIT=30]-->
+    <string name="memory_settings_title">Memory</string>
+
+    <!-- Title for screen showing recent memory usage of specific app [CHAR LIMIT=30]-->
+    <string name="memory_details_title">Memory details</string>
+
+    <!-- Description of app always running [CHAR LIMIT=45] -->
+    <string name="always_running">Always running</string>
+
+    <!-- Description of app sometimes running [CHAR LIMIT=45] -->
+    <string name="sometimes_running">Sometimes running</string>
+
+    <!-- Description of app rarely running [CHAR LIMIT=45] -->
+    <string name="rarely_running">Rarely running</string>
+
+    <!-- Maximum memory usage key [CHAR LIMIT=25] -->
+    <string name="memory_max_use">Maximum</string>
+
+    <!-- Average memory usage key [CHAR LIMIT=25] -->
+    <string name="memory_avg_use">Average</string>
+
+    <!-- Maximum memory used by an app [CHAR LIMIT=25] -->
+    <string name="memory_max_desc">Maximum <xliff:g id="memory" example="30MB">%1$s</xliff:g></string>
+
+    <!-- Average memory used by an app [CHAR LIMIT=25] -->
+    <string name="memory_avg_desc">Average <xliff:g id="memory" example="30MB">%1$s</xliff:g></string>
+
+    <!-- Formatting for memory description [CHAR LIMIT=25] -->
+    <string name="memory_use_running_format"><xliff:g id="memory" example="30MB">%1$s</xliff:g> / <xliff:g id="running" example="Always running">%2$s</xliff:g></string>
+
+    <!-- Label for process (singular) [CHAR LIMIT=25] -->
+    <string name="process">Process</string>
+
+    <!-- Label for process [CHAR LIMIT=25] -->
+    <string name="process_format">Process <xliff:g id="count" example="3">%1$d</xliff:g></string>
+
 </resources>
diff --git a/res/xml/app_memory_settings.xml b/res/xml/app_memory_settings.xml
new file mode 100644 (file)
index 0000000..2c3db21
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:title="@string/memory_details_title">
+
+    <com.android.settings.applications.LayoutPreference
+        android:key="details_header"
+        android:layout="@layout/process_stats_details" />
+
+</PreferenceScreen>
index 78fe0e7..cdc1166 100644 (file)
                 android:icon="@drawable/ic_settings_multiuser"
                 />
 
+        <!-- Memory -->
+        <dashboard-tile
+                android:id="@+id/manage_memory"
+                android:title="@string/memory_settings_title"
+                android:fragment="com.android.settings.applications.ProcessStatsUi"
+                android:icon="@drawable/ic_settings_memory"
+                />
+
         <!-- Manage NFC payment apps -->
         <dashboard-tile
                 android:id="@+id/nfc_payment_settings"
index c36b3e7..6911892 100644 (file)
         android:title="@string/oem_unlock_enable"
         android:summary="@string/oem_unlock_enable_summary"/>
 
-    <PreferenceScreen android:key="proc_stats"
-            android:title="@string/process_stats_summary_title"
-            android:summary="@string/process_stats_summary"
-            android:fragment="com.android.settings.applications.ProcessStatsUi">
-    </PreferenceScreen>
-
     <PreferenceScreen
         android:key="running_apps"
         android:title="@string/runningservices_settings_title"
index fbd87fd..46bb160 100644 (file)
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-        android:title="@string/app_memory_use"
-        android:key="app_list">
-    <Preference
-        style="?android:attr/preferenceInformationStyle"
-        android:key="mem_status"
-        android:persistent="false"
-    />
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:title="@string/app_memory_use"
+    android:key="app_list">
+    <com.android.settings.applications.LayoutPreference
+        android:key="status_header"
+        android:layout="@layout/proc_stats_ui" />
 </PreferenceScreen>
diff --git a/src/com/android/settings/CancellablePreference.java b/src/com/android/settings/CancellablePreference.java
new file mode 100644 (file)
index 0000000..ab723ca
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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 android.content.Context;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+
+public class CancellablePreference extends Preference implements OnClickListener {
+
+    private boolean mCancellable;
+    private OnCancelListener mListener;
+
+    public CancellablePreference(Context context) {
+        super(context);
+        setWidgetLayoutResource(R.layout.cancel_pref_widget);
+    }
+
+    public CancellablePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWidgetLayoutResource(R.layout.cancel_pref_widget);
+    }
+
+    public void setCancellable(boolean isCancellable) {
+        mCancellable = isCancellable;
+        notifyChanged();
+    }
+
+    public void setOnCancelListener(OnCancelListener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        super.onBindView(view);
+
+        ImageView cancel = (ImageView) view.findViewById(R.id.cancel);
+        cancel.setVisibility(mCancellable ? View.VISIBLE : View.INVISIBLE);
+        cancel.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mListener != null) {
+            mListener.onCancel(this);
+        }
+    }
+
+    public interface OnCancelListener {
+        void onCancel(CancellablePreference preference);
+    }
+
+}
index 5c7e0e9..6c2f135 100644 (file)
@@ -164,8 +164,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
 
     private static final String SHOW_ALL_ANRS_KEY = "show_all_anrs";
 
-    private static final String PROCESS_STATS = "proc_stats";
-
     private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
 
     private static final String TERMINAL_APP_PACKAGE = "com.android.terminal";
@@ -246,7 +244,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
 
     private SwitchPreference mShowAllANRs;
 
-    private PreferenceScreen mProcessStats;
     private final ArrayList<Preference> mAllPrefs = new ArrayList<Preference>();
 
     private final ArrayList<SwitchPreference> mResetSwitchPrefs
@@ -397,9 +394,6 @@ public class DevelopmentSettings extends SettingsPreferenceFragment
             mAllPrefs.add(hdcpChecking);
             removePreferenceForProduction(hdcpChecking);
         }
-
-        mProcessStats = (PreferenceScreen) findPreference(PROCESS_STATS);
-        mAllPrefs.add(mProcessStats);
     }
 
     private ListPreference addListPreference(String prefKey) {
index caf2491..ee2b1b4 100644 (file)
@@ -340,6 +340,7 @@ public class SettingsActivity extends Activity
             ZenModePrioritySettings.class.getName(),
             ZenModeScheduleRuleSettings.class.getName(),
             ZenModeExternalRuleSettings.class.getName(),
+            ProcessStatsUi.class.getName(),
     };
 
 
diff --git a/src/com/android/settings/applications/ProcStatsData.java b/src/com/android/settings/applications/ProcStatsData.java
new file mode 100644 (file)
index 0000000..1e32cc9
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2015 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 android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.text.format.Formatter;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.app.IProcessStats;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.app.ProcessStats;
+import com.android.internal.app.ProcessStats.ProcessDataCollection;
+import com.android.internal.app.ProcessStats.TotalMemoryUseCollection;
+import com.android.internal.util.MemInfoReader;
+import com.android.settings.R;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+public class ProcStatsData {
+
+    private static final String TAG = "ProcStatsManager";
+
+    private static final boolean DEBUG = ProcessStatsUi.DEBUG;
+
+    private static ProcessStats sStatsXfer;
+
+    private PackageManager mPm;
+    private Context mContext;
+    private long memTotalTime;
+
+    private IProcessStats mProcessStats;
+    private ProcessStats mStats;
+
+    private int mMemState;
+
+    private boolean mUseUss;
+    private long mDuration;
+
+    private int[] mMemStates;
+
+    private int[] mStates;
+
+    private MemInfo mMemInfo;
+
+    private ArrayList<ProcStatsPackageEntry> pkgEntries;
+
+    public ProcStatsData(Context context, boolean useXfer) {
+        mContext = context;
+        mPm = context.getPackageManager();
+        mProcessStats = IProcessStats.Stub.asInterface(
+                ServiceManager.getService(ProcessStats.SERVICE_NAME));
+        mMemStates = ProcessStats.ALL_MEM_ADJ;
+        mStates = ProcessStats.BACKGROUND_PROC_STATES;
+        if (useXfer) {
+            mStats = sStatsXfer;
+        }
+    }
+
+    public void setTotalTime(int totalTime) {
+        memTotalTime = totalTime;
+    }
+
+    public void xferStats() {
+        sStatsXfer = mStats;
+    }
+
+    public void setMemStates(int[] memStates) {
+        mMemStates = memStates;
+        refreshStats(false);
+    }
+
+    public void setStats(int[] stats) {
+        this.mStates = stats;
+        refreshStats(false);
+    }
+
+    public int getMemState() {
+        return mMemState;
+    }
+
+    public MemInfo getMemInfo() {
+        return mMemInfo;
+    }
+
+    public long getElapsedTime() {
+        return mStats.mTimePeriodEndRealtime - mStats.mTimePeriodStartRealtime;
+    }
+
+    public void setDuration(long duration) {
+        if (duration != mDuration) {
+            mDuration = duration;
+            refreshStats(true);
+        }
+    }
+
+    public long getDuration() {
+        return mDuration;
+    }
+
+    public List<ProcStatsPackageEntry> getEntries() {
+        return pkgEntries;
+    }
+
+    public void refreshStats(boolean forceLoad) {
+        if (mStats == null || forceLoad) {
+            load();
+        }
+
+        pkgEntries = new ArrayList<>();
+
+        long now = SystemClock.uptimeMillis();
+
+        memTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations,
+                mStats.mMemFactor, mStats.mStartTime, now);
+
+        ProcessStats.TotalMemoryUseCollection totalMem = new ProcessStats.TotalMemoryUseCollection(
+                ProcessStats.ALL_SCREEN_ADJ, mMemStates);
+        mStats.computeTotalMemoryUse(totalMem, now);
+
+        mMemInfo = new MemInfo(mContext, totalMem, memTotalTime);
+
+        ProcessDataCollection bgTotals = new ProcessDataCollection(
+                ProcessStats.ALL_SCREEN_ADJ, mMemStates, mStates);
+        ProcessDataCollection runTotals = new ProcessDataCollection(
+                ProcessStats.ALL_SCREEN_ADJ, mMemStates, ProcessStats.NON_CACHED_PROC_STATES);
+
+        createPkgMap(getProcs(bgTotals, runTotals), bgTotals, runTotals);
+
+        ProcStatsPackageEntry osPkg = createOsEntry(bgTotals, runTotals, totalMem,
+                mMemInfo.baseCacheRam);
+        pkgEntries.add(osPkg);
+    }
+
+    private void createPkgMap(ArrayList<ProcStatsEntry> procEntries, ProcessDataCollection bgTotals,
+            ProcessDataCollection runTotals) {
+        // Combine processes into packages.
+        ArrayMap<String, ProcStatsPackageEntry> pkgMap = new ArrayMap<>();
+        for (int i = procEntries.size() - 1; i >= 0; i--) {
+            ProcStatsEntry proc = procEntries.get(i);
+            proc.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
+            ProcStatsPackageEntry pkg = pkgMap.get(proc.mBestTargetPackage);
+            if (pkg == null) {
+                pkg = new ProcStatsPackageEntry(proc.mBestTargetPackage, memTotalTime);
+                pkgMap.put(proc.mBestTargetPackage, pkg);
+                pkgEntries.add(pkg);
+            }
+            pkg.addEntry(proc);
+        }
+    }
+
+    private ProcStatsPackageEntry createOsEntry(ProcessDataCollection bgTotals,
+            ProcessDataCollection runTotals, TotalMemoryUseCollection totalMem, long baseCacheRam) {
+        // Add in fake entry representing the OS itself.
+        ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime);
+        ProcStatsEntry osEntry;
+        if (totalMem.sysMemNativeWeight > 0) {
+            osEntry = new ProcStatsEntry("os", 0,
+                    mContext.getString(R.string.process_stats_os_native), memTotalTime,
+                    (long) (totalMem.sysMemNativeWeight / memTotalTime));
+            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
+            osPkg.addEntry(osEntry);
+        }
+        if (totalMem.sysMemKernelWeight > 0) {
+            osEntry = new ProcStatsEntry("os", 0,
+                    mContext.getString(R.string.process_stats_os_kernel), memTotalTime,
+                    (long) (totalMem.sysMemKernelWeight / memTotalTime));
+            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
+            osPkg.addEntry(osEntry);
+        }
+        if (totalMem.sysMemZRamWeight > 0) {
+            osEntry = new ProcStatsEntry("os", 0,
+                    mContext.getString(R.string.process_stats_os_zram), memTotalTime,
+                    (long) (totalMem.sysMemZRamWeight / memTotalTime));
+            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
+            osPkg.addEntry(osEntry);
+        }
+        if (baseCacheRam > 0) {
+            osEntry = new ProcStatsEntry("os", 0,
+                    mContext.getString(R.string.process_stats_os_cache), memTotalTime,
+                    baseCacheRam / 1024);
+            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
+            osPkg.addEntry(osEntry);
+        }
+        return osPkg;
+    }
+
+    private ArrayList<ProcStatsEntry> getProcs(ProcessDataCollection bgTotals,
+            ProcessDataCollection runTotals) {
+        final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>();
+        if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES");
+
+        final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>();
+        for (int ipkg = 0, N = mStats.mPackages.getMap().size(); ipkg < N; ipkg++) {
+            final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids = mStats.mPackages
+                    .getMap().valueAt(ipkg);
+            for (int iu = 0; iu < pkgUids.size(); iu++) {
+                final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu);
+                for (int iv = 0; iv < vpkgs.size(); iv++) {
+                    final ProcessStats.PackageState st = vpkgs.valueAt(iv);
+                    for (int iproc = 0; iproc < st.mProcesses.size(); iproc++) {
+                        final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc);
+                        final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName,
+                                pkgProc.mUid);
+                        if (proc == null) {
+                            Log.w(TAG, "No process found for pkg " + st.mPackageName
+                                    + "/" + st.mUid + " proc name " + pkgProc.mName);
+                            continue;
+                        }
+                        ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid);
+                        if (ent == null) {
+                            ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals,
+                                    mUseUss);
+                            if (ent.mRunWeight > 0) {
+                                if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/"
+                                            + proc.mUid + ": time="
+                                            + ProcessStatsUi.makeDuration(ent.mRunDuration) + " ("
+                                            + ((((double) ent.mRunDuration) / memTotalTime) * 100)
+                                            + "%)"
+                                            + " pss=" + ent.mAvgRunMem);
+                                entriesMap.put(proc.mName, proc.mUid, ent);
+                                procEntries.add(ent);
+                            }
+                        } else {
+                            ent.addPackage(st.mPackageName);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES");
+
+        // Add in service info.
+        for (int ip = 0, N = mStats.mPackages.getMap().size(); ip < N; ip++) {
+            SparseArray<SparseArray<ProcessStats.PackageState>> uids = mStats.mPackages.getMap()
+                    .valueAt(ip);
+            for (int iu = 0; iu < uids.size(); iu++) {
+                SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu);
+                for (int iv = 0; iv < vpkgs.size(); iv++) {
+                    ProcessStats.PackageState ps = vpkgs.valueAt(iv);
+                    for (int is = 0, NS = ps.mServices.size(); is < NS; is++) {
+                        ProcessStats.ServiceState ss = ps.mServices.valueAt(is);
+                        if (ss.mProcessName != null) {
+                            ProcStatsEntry ent = entriesMap.get(ss.mProcessName,
+                                    uids.keyAt(iu));
+                            if (ent != null) {
+                                if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName
+                                            + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc "
+                                            + ss.mProcessName);
+                                ent.addService(ss);
+                            } else {
+                                Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu)
+                                        + " for service " + ss.mName);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return procEntries;
+    }
+
+    private void load() {
+        try {
+            mMemState = mProcessStats.getCurrentMemoryState();
+            ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration);
+            mStats = new ProcessStats(false);
+            InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+            mStats.read(is);
+            try {
+                is.close();
+            } catch (IOException e) {
+            }
+            if (mStats.mReadError != null) {
+                Log.w(TAG, "Failure reading process stats: " + mStats.mReadError);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException:", e);
+        }
+    }
+
+    public static class MemInfo {
+        double realUsedRam;
+        double realFreeRam;
+        double realTotalRam;
+        long baseCacheRam;
+
+        double[] mMemStateWeights = new double[ProcessStats.STATE_COUNT];
+        double freeWeight;
+        double usedWeight;
+        double weightToRam;
+        double totalRam;
+        double totalScale;
+
+        private MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem,
+                long memTotalTime) {
+            calculateWeightInfo(context, totalMem, memTotalTime);
+
+            double usedRam = (usedWeight * 1024) / memTotalTime;
+            double freeRam = (freeWeight * 1024) / memTotalTime;
+            totalRam = usedRam + freeRam;
+            totalScale = realTotalRam / totalRam;
+            weightToRam = totalScale / memTotalTime * 1024;
+
+            realUsedRam = usedRam * totalScale;
+            realFreeRam = freeRam * totalScale;
+            if (DEBUG) {
+                Log.i(TAG, "Scaled Used RAM: " + Formatter.formatShortFileSize(context,
+                        (long) realUsedRam));
+                Log.i(TAG, "Scaled Free RAM: " + Formatter.formatShortFileSize(context,
+                        (long) realFreeRam));
+            }
+            if (DEBUG) {
+                Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(context,
+                        (long) realUsedRam));
+                Log.i(TAG, "Adj Scaled Free RAM: " + Formatter.formatShortFileSize(context,
+                        (long) realFreeRam));
+            }
+
+            ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
+            ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(
+                    memInfo);
+            if (memInfo.hiddenAppThreshold >= realFreeRam) {
+                realUsedRam = freeRam;
+                realFreeRam = 0;
+                baseCacheRam = (long) realFreeRam;
+            } else {
+                realUsedRam += memInfo.hiddenAppThreshold;
+                realFreeRam -= memInfo.hiddenAppThreshold;
+                baseCacheRam = memInfo.hiddenAppThreshold;
+            }
+        }
+
+        private void calculateWeightInfo(Context context, TotalMemoryUseCollection totalMem,
+                long memTotalTime) {
+            MemInfoReader memReader = new MemInfoReader();
+            memReader.readMemInfo();
+            realTotalRam = memReader.getTotalSize();
+            freeWeight = totalMem.sysMemFreeWeight + totalMem.sysMemCachedWeight;
+            usedWeight = totalMem.sysMemKernelWeight + totalMem.sysMemNativeWeight
+                    + totalMem.sysMemZRamWeight;
+            for (int i = 0; i < ProcessStats.STATE_COUNT; i++) {
+                if (i == ProcessStats.STATE_SERVICE_RESTARTING) {
+                    // These don't really run.
+                    mMemStateWeights[i] = 0;
+                } else {
+                    mMemStateWeights[i] = totalMem.processStateWeight[i];
+                    if (i >= ProcessStats.STATE_HOME) {
+                        freeWeight += totalMem.processStateWeight[i];
+                    } else {
+                        usedWeight += totalMem.processStateWeight[i];
+                    }
+                }
+            }
+            if (DEBUG) {
+                Log.i(TAG, "Used RAM: " + Formatter.formatShortFileSize(context,
+                        (long) ((usedWeight * 1024) / memTotalTime)));
+                Log.i(TAG, "Free RAM: " + Formatter.formatShortFileSize(context,
+                        (long) ((freeWeight * 1024) / memTotalTime)));
+                Log.i(TAG, "Total RAM: " + Formatter.formatShortFileSize(context,
+                        (long) (((freeWeight + usedWeight) * 1024) / memTotalTime)));
+            }
+        }
+    }
+
+    final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
+        @Override
+        public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
+            if (lhs.mRunWeight < rhs.mRunWeight) {
+                return 1;
+            } else if (lhs.mRunWeight > rhs.mRunWeight) {
+                return -1;
+            } else if (lhs.mRunDuration < rhs.mRunDuration) {
+                return 1;
+            } else if (lhs.mRunDuration > rhs.mRunDuration) {
+                return -1;
+            }
+            return 0;
+        }
+    };
+}
index 41f7a53..637003c 100644 (file)
@@ -36,6 +36,7 @@ public final class ProcStatsEntry implements Parcelable {
     final String mPackage;
     final int mUid;
     final String mName;
+    public CharSequence mLabel;
     final ArrayList<String> mPackages = new ArrayList<String>();
     final long mBgDuration;
     final long mAvgBgMem;
index 3925d1d..feb11d8 100644 (file)
@@ -30,6 +30,9 @@ public class ProcStatsPackageEntry implements Parcelable {
     private static final String TAG = "ProcStatsEntry";
     private static boolean DEBUG = ProcessStatsUi.DEBUG;
 
+    private static final float ALWAYS_THRESHOLD = .95f;
+    private static final float SOMETIMES_THRESHOLD = .25f;
+
     final String mPackage;
     final ArrayList<ProcStatsEntry> mEntries = new ArrayList<ProcStatsEntry>();
 
@@ -44,9 +47,11 @@ public class ProcStatsPackageEntry implements Parcelable {
 
     public ApplicationInfo mUiTargetApp;
     public String mUiLabel;
+    private long mWindowLength;
 
-    public ProcStatsPackageEntry(String pkg) {
+    public ProcStatsPackageEntry(String pkg, long windowLength) {
         mPackage = pkg;
+        mWindowLength = windowLength;
     }
 
     public ProcStatsPackageEntry(Parcel in) {
@@ -62,6 +67,16 @@ public class ProcStatsPackageEntry implements Parcelable {
         mRunWeight = in.readDouble();
     }
 
+    public CharSequence getRunningFrequency(Context context) {
+        float amountRunning = mRunDuration / (float) mWindowLength;
+        return getFrequency(amountRunning, context);
+    }
+
+    public CharSequence getBackgroundFrequency(Context context) {
+        float amountRunning = mBgDuration / (float) mWindowLength;
+        return getFrequency(amountRunning, context);
+    }
+
     public void addEntry(ProcStatsEntry entry) {
         mEntries.add(entry);
     }
@@ -72,23 +87,28 @@ public class ProcStatsPackageEntry implements Parcelable {
         mRunDuration = mAvgRunMem = mMaxRunMem = 0;
         mRunWeight = 0;
         final int N = mEntries.size();
-        for (int i=0; i<N; i++) {
+        for (int i=0; i < N; i++) {
             ProcStatsEntry entry = mEntries.get(i);
             mBgDuration += entry.mBgDuration;
-            mAvgBgMem += entry.mAvgBgMem;
-            if (entry.mMaxBgMem > mMaxBgMem) {
-                mMaxBgMem = entry.mMaxBgMem;
-            }
+            mAvgBgMem += entry.mAvgBgMem * entry.mBgDuration;
             mBgWeight += entry.mBgWeight;
             mRunDuration += entry.mRunDuration;
-            mAvgRunMem += entry.mAvgRunMem;
-            if (entry.mMaxRunMem > mMaxRunMem) {
-                mMaxRunMem = entry.mMaxRunMem;
-            }
+            mAvgRunMem += entry.mAvgRunMem * entry.mRunDuration;
             mRunWeight += entry.mRunWeight;
+
+            // Each entry is generally a process or something similar.  Since it is extremely
+            // unlikely that any apps are going to avoid running processes at the same time
+            // to avoid memory usage, we will sum the maximum memory usage to create a
+            // hypothetical worst case scenario of memory.
+            mMaxBgMem += entry.mMaxBgMem;
+            mMaxRunMem += entry.mMaxRunMem;
+        }
+        if (mBgDuration != 0) {
+            mAvgBgMem = mAvgBgMem * N / mBgDuration;
+        }
+        if (mRunDuration != 0) {
+            mAvgRunMem = mAvgRunMem * N / mRunDuration;
         }
-        mAvgBgMem /= N;
-        mAvgRunMem /= N;
     }
 
     public void retrieveUiData(Context context, PackageManager pm) {
@@ -142,4 +162,15 @@ public class ProcStatsPackageEntry implements Parcelable {
             return new ProcStatsPackageEntry[size];
         }
     };
+
+    // TODO: Find better place for this.
+    public static CharSequence getFrequency(float amount, Context context) {
+        if (amount> ALWAYS_THRESHOLD) {
+            return context.getString(R.string.always_running);
+        } else if (amount> SOMETIMES_THRESHOLD) {
+            return context.getString(R.string.sometimes_running);
+        } else {
+            return context.getString(R.string.rarely_running);
+        }
+    }
 }
index 1e7cf09..191e7e9 100644 (file)
 package com.android.settings.applications;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.app.AlertDialog;
 import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.os.Process;
+import android.preference.PreferenceCategory;
 import android.text.format.Formatter;
-import android.view.LayoutInflater;
+import android.util.ArrayMap;
+import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
 import android.widget.TextView;
+
 import com.android.internal.logging.MetricsLogger;
-import com.android.settings.InstrumentedFragment;
+import com.android.settings.AppHeader;
+import com.android.settings.CancellablePreference;
+import com.android.settings.CancellablePreference.OnCancelListener;
 import com.android.settings.R;
-import com.android.settings.Utils;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.applications.ProcStatsEntry.Service;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.List;
 
-import static com.android.settings.Utils.prepareCustomPreferencesList;
+public class ProcessStatsDetail extends SettingsPreferenceFragment
+        implements Button.OnClickListener {
 
-public class ProcessStatsDetail extends InstrumentedFragment implements Button.OnClickListener {
     private static final String TAG = "ProcessStatsDetail";
 
     public static final int ACTION_FORCE_STOP = 1;
 
     public static final String EXTRA_PACKAGE_ENTRY = "package_entry";
     public static final String EXTRA_USE_USS = "use_uss";
-    public static final String EXTRA_MAX_WEIGHT = "max_weight";
     public static final String EXTRA_WEIGHT_TO_RAM = "weight_to_ram";
     public static final String EXTRA_TOTAL_TIME = "total_time";
+    public static final String EXTRA_MAX_MEMORY_USAGE = "max_memory_usage";
+    public static final String EXTRA_TOTAL_SCALE = "total_scale";
+
+    private static final String KEY_DETAILS_HEADER = "details_header";
+
+    private final ArrayMap<ComponentName, CancellablePreference> mServiceMap = new ArrayMap<>();
 
     private PackageManager mPm;
     private DevicePolicyManager mDpm;
 
     private ProcStatsPackageEntry mApp;
     private boolean mUseUss;
-    private double mMaxWeight;
     private double mWeightToRam;
     private long mTotalTime;
     private long mOnePercentTime;
 
-    private View mRootView;
-    private TextView mTitleView;
-    private ViewGroup mTwoButtonsPanel;
     private Button mForceStopButton;
     private Button mReportButton;
-    private ViewGroup mProcessesParent;
-    private ViewGroup mServicesParent;
+
+    private LinearColorBar mColorBar;
+
+    private float mMaxMemoryUsage;
+
+    private double mTotalScale;
 
     @Override
     public void onCreate(Bundle icicle) {
@@ -81,21 +98,23 @@ public class ProcessStatsDetail extends InstrumentedFragment implements Button.O
         mApp = args.getParcelable(EXTRA_PACKAGE_ENTRY);
         mApp.retrieveUiData(getActivity(), mPm);
         mUseUss = args.getBoolean(EXTRA_USE_USS);
-        mMaxWeight = args.getDouble(EXTRA_MAX_WEIGHT);
         mWeightToRam = args.getDouble(EXTRA_WEIGHT_TO_RAM);
         mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
+        mMaxMemoryUsage = args.getFloat(EXTRA_MAX_MEMORY_USAGE);
+        mTotalScale = args.getDouble(EXTRA_TOTAL_SCALE);
         mOnePercentTime = mTotalTime/100;
+
+        mServiceMap.clear();
+        createDetails();
     }
 
     @Override
-    public View onCreateView(
-            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        final View view = inflater.inflate(R.layout.process_stats_details, container, false);
-        prepareCustomPreferencesList(container, view, view, false);
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
 
-        mRootView = view;
-        createDetails();
-        return view;
+        AppHeader.createAppHeader(getActivity(),
+                mApp.mUiTargetApp != null ? mApp.mUiTargetApp.loadIcon(mPm) : new ColorDrawable(0),
+                mApp.mUiLabel, null);
     }
 
     @Override
@@ -107,56 +126,88 @@ public class ProcessStatsDetail extends InstrumentedFragment implements Button.O
     public void onResume() {
         super.onResume();
         checkForceStop();
-    }
 
-    @Override
-    public void onPause() {
-        super.onPause();
+        updateRunningServices();
     }
 
-    private void createDetails() {
-        final double percentOfWeight = (mApp.mBgWeight / mMaxWeight) * 100;
-
-        int appLevel = (int) Math.ceil(percentOfWeight);
-        String appLevelText = Formatter.formatShortFileSize(getActivity(),
-                (long)(mApp.mRunWeight * mWeightToRam));
-
-        // Set all values in the header.
-        mTitleView = (TextView) mRootView.findViewById(android.R.id.title);
-        mTitleView.setText(mApp.mUiLabel);
-        final TextView text1 = (TextView)mRootView.findViewById(android.R.id.text1);
-        text1.setText(appLevelText);
-        final ProgressBar progress = (ProgressBar) mRootView.findViewById(android.R.id.progress);
-        progress.setProgress(appLevel);
-        final ImageView icon = (ImageView) mRootView.findViewById(android.R.id.icon);
-        if (mApp.mUiTargetApp != null) {
-            icon.setImageDrawable(mApp.mUiTargetApp.loadIcon(mPm));
+    private void updateRunningServices() {
+        ActivityManager activityManager = (ActivityManager)
+                getActivity().getSystemService(Context.ACTIVITY_SERVICE);
+        List<RunningServiceInfo> runningServices =
+                activityManager.getRunningServices(Integer.MAX_VALUE);
+
+        // Set all services as not running, then turn back on the ones we find.
+        int N = mServiceMap.size();
+        for (int i = 0; i < N; i++) {
+            mServiceMap.valueAt(i).setCancellable(false);
         }
 
-        mTwoButtonsPanel = (ViewGroup)mRootView.findViewById(R.id.two_buttons_panel);
-        mForceStopButton = (Button)mRootView.findViewById(R.id.right_button);
-        mReportButton = (Button)mRootView.findViewById(R.id.left_button);
-        mForceStopButton.setEnabled(false);
-        mReportButton.setVisibility(View.INVISIBLE);
+        N = runningServices.size();
+        for (int i = 0; i < N; i++) {
+            RunningServiceInfo runningService = runningServices.get(i);
+            if (!runningService.started && runningService.clientLabel == 0) {
+                continue;
+            }
+            if ((runningService.flags & RunningServiceInfo.FLAG_PERSISTENT_PROCESS) != 0) {
+                continue;
+            }
+            final ComponentName service = runningService.service;
+            CancellablePreference pref = mServiceMap.get(service);
+            if (pref != null) {
+                pref.setOnCancelListener(new OnCancelListener() {
+                    @Override
+                    public void onCancel(CancellablePreference preference) {
+                        stopService(service.getPackageName(), service.getClassName());
+                    }
+                });
+                pref.setCancellable(true);
+            }
+        }
+    }
 
-        mProcessesParent = (ViewGroup)mRootView.findViewById(R.id.processes);
-        mServicesParent = (ViewGroup)mRootView.findViewById(R.id.services);
+    private void createDetails() {
+        addPreferencesFromResource(R.xml.app_memory_settings);
 
         fillProcessesSection();
-        fillServicesSection();
-        if (mServicesParent.getChildCount() <= 0) {
-            mServicesParent.setVisibility(View.GONE);
-            mRootView.findViewById(R.id.services_label).setVisibility(View.GONE);
-        }
+
+        LayoutPreference headerLayout = (LayoutPreference) findPreference(KEY_DETAILS_HEADER);
+
+        TextView avgUsed = (TextView) headerLayout.findViewById(R.id.memory_avg);
+        TextView maxUsed = (TextView) headerLayout.findViewById(R.id.memory_max);
+        avgUsed.setText(getString(R.string.memory_avg_desc,
+                Formatter.formatShortFileSize(getActivity(),
+                        (long) (Math.max(mApp.mBgWeight, mApp.mRunWeight) * mWeightToRam))));
+        maxUsed.setText(getString(R.string.memory_max_desc,
+                Formatter.formatShortFileSize(getActivity(),
+                        (long) (Math.max(mApp.mMaxBgMem, mApp.mMaxRunMem) * 1024 * mTotalScale))));
+
+        mForceStopButton = (Button) headerLayout.findViewById(R.id.right_button);
+        mReportButton = (Button) headerLayout.findViewById(R.id.left_button);
 
         if (mApp.mEntries.get(0).mUid >= android.os.Process.FIRST_APPLICATION_UID) {
+            mForceStopButton.setEnabled(false);
+            mReportButton.setVisibility(View.INVISIBLE);
+
             mForceStopButton.setText(R.string.force_stop);
             mForceStopButton.setTag(ACTION_FORCE_STOP);
             mForceStopButton.setOnClickListener(this);
-            mTwoButtonsPanel.setVisibility(View.VISIBLE);
         } else {
-            mTwoButtonsPanel.setVisibility(View.GONE);
+            mReportButton.setVisibility(View.GONE);
+            mForceStopButton.setVisibility(View.GONE);
         }
+
+        // TODO: Find way to share this code with ProcessStatsPreference.
+        boolean statsForeground = mApp.mRunWeight > mApp.mBgWeight;
+        float mAvgRatio = (statsForeground ? mApp.mAvgRunMem : mApp.mAvgBgMem) / mMaxMemoryUsage;
+        float mMaxRatio = (statsForeground ? mApp.mMaxRunMem : mApp.mMaxBgMem) / mMaxMemoryUsage
+                - mAvgRatio;
+        float mRemainingRatio = 1 - mAvgRatio - mMaxRatio;
+        mColorBar = (LinearColorBar) headerLayout.findViewById(R.id.color_bar);
+        Context context = getActivity();
+        mColorBar.setColors(context.getColor(R.color.memory_avg_use),
+                context.getColor(R.color.memory_max_use),
+                context.getColor(R.color.memory_remaining));
+        mColorBar.setRatios(mAvgRatio, mMaxRatio, mRemainingRatio);
     }
 
     public void onClick(View v) {
@@ -171,34 +222,6 @@ public class ProcessStatsDetail extends InstrumentedFragment implements Button.O
         }
     }
 
-    private void addPackageHeaderItem(ViewGroup parent, String packageName) {
-        LayoutInflater inflater = getActivity().getLayoutInflater();
-        ViewGroup item = (ViewGroup) inflater.inflate(R.layout.running_processes_item,
-                null);
-        parent.addView(item);
-        final ImageView icon = (ImageView) item.findViewById(R.id.icon);
-        TextView nameView = (TextView) item.findViewById(R.id.name);
-        TextView descriptionView = (TextView) item.findViewById(R.id.description);
-        try {
-            ApplicationInfo ai = mPm.getApplicationInfo(packageName, 0);
-            icon.setImageDrawable(ai.loadIcon(mPm));
-            nameView.setText(ai.loadLabel(mPm));
-        } catch (PackageManager.NameNotFoundException e) {
-        }
-        descriptionView.setText(packageName);
-    }
-
-    private void addDetailsItem(ViewGroup parent, CharSequence label, CharSequence value) {
-        LayoutInflater inflater = getActivity().getLayoutInflater();
-        ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
-                null);
-        parent.addView(item);
-        TextView labelView = (TextView) item.findViewById(R.id.label);
-        TextView valueView = (TextView) item.findViewById(R.id.value);
-        labelView.setText(label);
-        valueView.setText(value);
-    }
-
     final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
         @Override
         public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
@@ -213,28 +236,35 @@ public class ProcessStatsDetail extends InstrumentedFragment implements Button.O
 
     private void fillProcessesSection() {
         final ArrayList<ProcStatsEntry> entries = new ArrayList<>();
-        for (int ie=0; ie<mApp.mEntries.size(); ie++) {
+        for (int ie = 0; ie < mApp.mEntries.size(); ie++) {
             ProcStatsEntry entry = mApp.mEntries.get(ie);
+            if (entry.mPackage.equals("os")) {
+                entry.mLabel = entry.mName;
+            } else {
+                if (mApp.mEntries.size() > 1) {
+                    entry.mLabel = getString(R.string.process_format, (ie + 1));
+                } else {
+                    entry.mLabel = getString(R.string.process);
+                }
+            }
             entries.add(entry);
         }
         Collections.sort(entries, sEntryCompare);
-        for (int ie=0; ie<entries.size(); ie++) {
+        for (int ie = 0; ie < entries.size(); ie++) {
             ProcStatsEntry entry = entries.get(ie);
-            LayoutInflater inflater = getActivity().getLayoutInflater();
-            ViewGroup item = (ViewGroup) inflater.inflate(R.layout.process_stats_proc_details,
-                    null);
-            mProcessesParent.addView(item);
-            ((TextView)item.findViewById(R.id.processes_name)).setText(entry.mName);
-            addDetailsItem(item, getResources().getText(R.string.process_stats_ram_use),
-                    Formatter.formatShortFileSize(getActivity(),
-                            (long)(entry.mRunWeight * mWeightToRam)));
-            if (entry.mBgWeight > 0) {
-                addDetailsItem(item, getResources().getText(R.string.process_stats_bg_ram_use),
-                        Formatter.formatShortFileSize(getActivity(),
-                                (long)(entry.mBgWeight * mWeightToRam)));
-            }
-            addDetailsItem(item, getResources().getText(R.string.process_stats_run_time),
-                    Utils.formatPercentage(entry.mRunDuration, mTotalTime));
+            PreferenceCategory processPref = new PreferenceCategory(getActivity());
+            processPref.setLayoutResource(R.layout.process_preference_category);
+            processPref.setTitle(entry.mLabel);
+
+            long memoryUse = Math.max((long)(entry.mRunWeight * mWeightToRam),
+                    (long)(entry.mBgWeight * mWeightToRam));
+            String memoryString = Formatter.formatShortFileSize(getActivity(), memoryUse);
+            CharSequence frequency = ProcStatsPackageEntry.getFrequency(entry.mRunDuration
+                    / (float)mTotalTime, getActivity());
+            processPref.setSummary(
+                    getString(R.string.memory_use_running_format, memoryString, frequency));
+            getPreferenceScreen().addPreference(processPref);
+            fillServicesSection(entry, processPref);
         }
     }
 
@@ -268,52 +298,97 @@ public class ProcessStatsDetail extends InstrumentedFragment implements Button.O
         long mDuration;
     }
 
-    private void fillServicesSection() {
+    private void fillServicesSection(ProcStatsEntry entry, PreferenceCategory processPref) {
         final HashMap<String, PkgService> pkgServices = new HashMap<>();
         final ArrayList<PkgService> pkgList = new ArrayList<>();
-        for (int ie=0; ie< mApp.mEntries.size(); ie++) {
-            ProcStatsEntry ent = mApp.mEntries.get(ie);
-            for (int ip=0; ip<ent.mServices.size(); ip++) {
-                String pkg = ent.mServices.keyAt(ip);
-                PkgService psvc = null;
-                ArrayList<ProcStatsEntry.Service> services = ent.mServices.valueAt(ip);
-                for (int is=services.size()-1; is>=0; is--) {
-                    ProcStatsEntry.Service pent = services.get(is);
-                    if (pent.mDuration >= mOnePercentTime) {
+        for (int ip = 0; ip < entry.mServices.size(); ip++) {
+            String pkg = entry.mServices.keyAt(ip);
+            PkgService psvc = null;
+            ArrayList<ProcStatsEntry.Service> services = entry.mServices.valueAt(ip);
+            for (int is=services.size()-1; is>=0; is--) {
+                ProcStatsEntry.Service pent = services.get(is);
+                if (pent.mDuration >= mOnePercentTime) {
+                    if (psvc == null) {
+                        psvc = pkgServices.get(pkg);
                         if (psvc == null) {
-                            psvc = pkgServices.get(pkg);
-                            if (psvc == null) {
-                                psvc = new PkgService();
-                                pkgServices.put(pkg, psvc);
-                                pkgList.add(psvc);
-                            }
+                            psvc = new PkgService();
+                            pkgServices.put(pkg, psvc);
+                            pkgList.add(psvc);
                         }
-                        psvc.mServices.add(pent);
-                        psvc.mDuration += pent.mDuration;
                     }
+                    psvc.mServices.add(pent);
+                    psvc.mDuration += pent.mDuration;
                 }
             }
         }
         Collections.sort(pkgList, sServicePkgCompare);
-        for (int ip=0; ip<pkgList.size(); ip++) {
+        for (int ip = 0; ip < pkgList.size(); ip++) {
             ArrayList<ProcStatsEntry.Service> services = pkgList.get(ip).mServices;
             Collections.sort(services, sServiceCompare);
-            if (pkgList.size() > 1) {
-                addPackageHeaderItem(mServicesParent, services.get(0).mPackage);
-            }
             for (int is=0; is<services.size(); is++) {
-                ProcStatsEntry.Service service = services.get(is);
-                String label = service.mName;
-                int tail = label.lastIndexOf('.');
-                if (tail >= 0 && tail < (label.length()-1)) {
-                    label = label.substring(tail+1);
-                }
-                String percentage = Utils.formatPercentage(service.mDuration, mTotalTime);
-                addDetailsItem(mServicesParent, label, percentage);
+                final ProcStatsEntry.Service service = services.get(is);
+                CharSequence label = getLabel(service);
+                CancellablePreference servicePref = new CancellablePreference(getActivity());
+                servicePref.setSelectable(false);
+                servicePref.setTitle(label);
+                servicePref.setSummary(ProcStatsPackageEntry.getFrequency(
+                        service.mDuration / (float) mTotalTime, getActivity()));
+                processPref.addPreference(servicePref);
+                mServiceMap.put(new ComponentName(service.mPackage, service.mName), servicePref);
             }
         }
     }
 
+    private CharSequence getLabel(Service service) {
+        // Try to get the service label, on the off chance that one exists.
+        try {
+            ServiceInfo serviceInfo = getPackageManager().getServiceInfo(
+                    new ComponentName(service.mPackage, service.mName), 0);
+            if (serviceInfo.labelRes != 0) {
+                return serviceInfo.loadLabel(getPackageManager());
+            }
+        } catch (NameNotFoundException e) {
+        }
+        String label = service.mName;
+        int tail = label.lastIndexOf('.');
+        if (tail >= 0 && tail < (label.length()-1)) {
+            label = label.substring(tail+1);
+        }
+        return label;
+    }
+
+    private void stopService(String pkg, String name) {
+        try {
+            ApplicationInfo appInfo = getActivity().getPackageManager().getApplicationInfo(pkg, 0);
+            if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                showStopServiceDialog(pkg, name);
+                return;
+            }
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Can't find app " + pkg, e);
+            return;
+        }
+        doStopService(pkg, name);
+    }
+
+    private void showStopServiceDialog(final String pkg, final String name) {
+        new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.runningservicedetails_stop_dlg_title)
+                .setMessage(R.string.runningservicedetails_stop_dlg_text)
+                .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int which) {
+                        doStopService(pkg, name);
+                    }
+                })
+                .setNegativeButton(R.string.dlg_cancel, null)
+                .show();
+    }
+
+    private void doStopService(String pkg, String name) {
+        getActivity().stopService(new Intent().setClassName(pkg, name));
+        updateRunningServices();
+    }
+
     private void killProcesses() {
         ActivityManager am = (ActivityManager)getActivity().getSystemService(
                 Context.ACTIVITY_SERVICE);
index 182cf0e..a1725e3 100644 (file)
 package com.android.settings.applications;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
 import android.preference.Preference;
-import android.text.format.Formatter;
+import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
-import android.widget.ProgressBar;
-import android.widget.TextView;
+
 import com.android.settings.R;
 
 public class ProcessStatsPreference extends Preference {
+
     private ProcStatsPackageEntry mEntry;
-    private int mProgress;
-    private CharSequence mProgressText;
+    private final int mAvgColor;
+    private final int mMaxColor;
+    private final int mRemainingColor;
+    private float mAvgRatio;
+    private float mMaxRatio;
+    private float mRemainingRatio;
 
     public ProcessStatsPreference(Context context) {
         this(context, null);
@@ -47,33 +52,38 @@ public class ProcessStatsPreference extends Preference {
     public ProcessStatsPreference(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        setLayoutResource(R.layout.preference_app_percentage);
+        setLayoutResource(R.layout.app_item_linear_color);
+        mAvgColor = context.getColor(R.color.memory_avg_use);
+        mMaxColor = context.getColor(R.color.memory_max_use);
+        mRemainingColor = context.getColor(R.color.memory_remaining);
     }
 
-    public void init(Drawable icon, ProcStatsPackageEntry entry) {
+    public void init(ProcStatsPackageEntry entry, PackageManager pm, float maxMemory) {
         mEntry = entry;
-        setIcon(icon != null ? icon : new ColorDrawable(0));
+        setTitle(TextUtils.isEmpty(entry.mUiLabel) ? entry.mPackage : entry.mUiLabel);
+        if (entry.mUiTargetApp != null) {
+            setIcon(entry.mUiTargetApp.loadIcon(pm));
+        } else {
+            setIcon(new ColorDrawable(0));
+        }
+        boolean statsForeground = entry.mRunWeight > entry.mBgWeight;
+        setSummary(statsForeground ? entry.getRunningFrequency(getContext())
+                : entry.getBackgroundFrequency(getContext()));
+        mAvgRatio = (statsForeground ? entry.mAvgRunMem : entry.mAvgBgMem) / maxMemory;
+        mMaxRatio = (statsForeground ? entry.mMaxRunMem : entry.mMaxBgMem) / maxMemory - mAvgRatio;
+        mRemainingRatio = 1 - mAvgRatio - mMaxRatio;
     }
 
     public ProcStatsPackageEntry getEntry() {
         return mEntry;
     }
 
-    public void setPercent(double percentOfWeight, double percentOfTime, long memory) {
-        mProgress = (int) Math.ceil(percentOfWeight);
-        //mProgressText = Utils.formatPercentage((int) percentOfTime);
-        mProgressText = Formatter.formatShortFileSize(getContext(), memory);
-        notifyChanged();
-    }
-
     @Override
     protected void onBindView(View view) {
         super.onBindView(view);
 
-        final ProgressBar progress = (ProgressBar) view.findViewById(android.R.id.progress);
-        progress.setProgress(mProgress);
-
-        final TextView text1 = (TextView) view.findViewById(android.R.id.text1);
-        text1.setText(mProgressText);
+        LinearColorBar linearColorBar = (LinearColorBar) view.findViewById(R.id.linear_color_bar);
+        linearColorBar.setColors(mAvgColor, mMaxColor, mRemainingColor);
+        linearColorBar.setRatios(mAvgRatio, mMaxRatio, mRemainingRatio);
     }
 }
index 45e24dd..96f76c0 100644 (file)
 
 package com.android.settings.applications;
 
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserManager;
 import android.preference.Preference;
-import android.preference.PreferenceFragment;
 import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
 import android.text.format.Formatter;
 import android.util.Log;
-import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.SubMenu;
-import com.android.internal.app.IProcessStats;
-import com.android.internal.app.ProcessMap;
+import android.widget.TextView;
+
 import com.android.internal.app.ProcessStats;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.util.MemInfoReader;
 import com.android.settings.InstrumentedPreferenceFragment;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.Utils;
+import com.android.settings.applications.ProcStatsData.MemInfo;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
-
-public class ProcessStatsUi extends InstrumentedPreferenceFragment
-        implements LinearColorBar.OnRegionTappedListener {
+import java.util.List;
+
+public class ProcessStatsUi extends InstrumentedPreferenceFragment {
+    private static final String MEM_REGION = "mem_region";
+    private static final String STATS_TYPE = "stats_type";
+    private static final String USE_USS = "use_uss";
+    private static final String SHOW_SYSTEM = "show_system";
+    private static final String SHOW_PERCENTAGE = "show_percentage";
+    private static final String DURATION = "duration";
     static final String TAG = "ProcessStatsUi";
     static final boolean DEBUG = false;
 
     private static final String KEY_APP_LIST = "app_list";
-    private static final String KEY_MEM_STATUS = "mem_status";
+    private static final String KEY_STATUS_HEADER = "status_header";
 
     private static final int NUM_DURATIONS = 4;
 
     private static final int MENU_STATS_REFRESH = Menu.FIRST;
     private static final int MENU_DURATION = Menu.FIRST + 1;
-    private static final int MENU_SHOW_SYSTEM = MENU_DURATION + NUM_DURATIONS;
+    private static final int MENU_SHOW_PERCENTAGE = MENU_DURATION + NUM_DURATIONS;
+    private static final int MENU_SHOW_SYSTEM = MENU_SHOW_PERCENTAGE + 1;
     private static final int MENU_USE_USS = MENU_SHOW_SYSTEM + 1;
     private static final int MENU_TYPE_BACKGROUND = MENU_USE_USS + 1;
     private static final int MENU_TYPE_FOREGROUND = MENU_TYPE_BACKGROUND + 1;
     private static final int MENU_TYPE_CACHED = MENU_TYPE_FOREGROUND + 1;
-    private static final int MENU_HELP = MENU_TYPE_CACHED + 1;
 
     static final int MAX_ITEMS_TO_LIST = 60;
 
-    final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
-        @Override
-        public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
-            if (lhs.mRunWeight < rhs.mRunWeight) {
-                return 1;
-            } else if (lhs.mRunWeight > rhs.mRunWeight) {
-                return -1;
-            } else if (lhs.mRunDuration < rhs.mRunDuration) {
-                return 1;
-            } else if (lhs.mRunDuration > rhs.mRunDuration) {
-                return -1;
-            }
-            return 0;
-        }
-    };
-
     final static Comparator<ProcStatsPackageEntry> sPackageEntryCompare
             = new Comparator<ProcStatsPackageEntry>() {
         @Override
@@ -109,21 +86,13 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
         }
     };
 
-    private static ProcessStats sStatsXfer;
-
-    IProcessStats mProcessStats;
-    UserManager mUm;
-    ProcessStats mStats;
-    int mMemState;
-
-    private long mDuration;
-    private long mLastDuration;
+    private boolean mShowPercentage;
     private boolean mShowSystem;
     private boolean mUseUss;
-    private int mStatsType;
     private int mMemRegion;
 
     private MenuItem[] mDurationMenus = new MenuItem[NUM_DURATIONS];
+    private MenuItem mShowPercentageMenu;
     private MenuItem mShowSystemMenu;
     private MenuItem mUseUssMenu;
     private MenuItem mTypeBackgroundMenu;
@@ -131,20 +100,18 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
     private MenuItem mTypeCachedMenu;
 
     private PreferenceGroup mAppListGroup;
-    private Preference mMemStatusPref;
+    private TextView mMemStatus;
 
-    double mMaxWeight;
-    long mTotalTime;
+    private long mTotalTime;
 
-    long[] mMemTimes = new long[ProcessStats.ADJ_MEM_FACTOR_COUNT];
-    double[] mMemStateWeights = new double[ProcessStats.STATE_COUNT];
-    double mMemCachedWeight;
-    double mMemFreeWeight;
-    double mMemZRamWeight;
-    double mMemKernelWeight;
-    double mMemNativeWeight;
-    double mMemTotalWeight;
-    double mWeightToRam;
+    private long[] mMemTimes = new long[ProcessStats.ADJ_MEM_FACTOR_COUNT];
+    private LinearColorBar mColors;
+    private TextView mMemUsed;
+    private LayoutPreference mHeader;
+    private PackageManager mPm;
+    private long memTotalTime;
+
+    private int mStatsType;
 
     // The actual duration value to use for each duration option.  Note these
     // are lower than the actual duration, since our durations are computed in
@@ -161,26 +128,31 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
             R.string.menu_duration_12h, R.string.menu_duration_1d
     };
 
+    private ProcStatsData mStatsManager;
+    private float mMaxMemoryUsage;
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        if (icicle != null) {
-            mStats = sStatsXfer;
-        }
+        mStatsManager = new ProcStatsData(getActivity(), icicle != null);
+
+        mPm = getActivity().getPackageManager();
 
         addPreferencesFromResource(R.xml.process_stats_summary);
-        mProcessStats = IProcessStats.Stub.asInterface(
-                ServiceManager.getService(ProcessStats.SERVICE_NAME));
-        mUm = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
         mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST);
-        mMemStatusPref = mAppListGroup.findPreference(KEY_MEM_STATUS);
-        mDuration = icicle != null ? icicle.getLong("duration", sDurations[0]) : sDurations[0];
-        mShowSystem = icicle != null ? icicle.getBoolean("show_system") : false;
-        mUseUss = icicle != null ? icicle.getBoolean("use_uss") : false;
-        mStatsType = icicle != null ? icicle.getInt("stats_type", MENU_TYPE_BACKGROUND)
+        mHeader = (LayoutPreference)mAppListGroup.findPreference(KEY_STATUS_HEADER);
+        mMemStatus = (TextView) mHeader.findViewById(R.id.memory_state);
+        mColors = (LinearColorBar) mHeader.findViewById(R.id.color_bar);
+        mMemUsed = (TextView) mHeader.findViewById(R.id.memory_used);
+        mStatsManager.setDuration(icicle != null
+                ? icicle.getLong(DURATION, sDurations[0]) : sDurations[0]);
+        mShowPercentage = icicle != null ? icicle.getBoolean(SHOW_PERCENTAGE) : true;
+        mShowSystem = icicle != null ? icicle.getBoolean(SHOW_SYSTEM) : false;
+        mUseUss = icicle != null ? icicle.getBoolean(USE_USS) : false;
+        mStatsType = icicle != null ? icicle.getInt(STATS_TYPE, MENU_TYPE_BACKGROUND)
                 : MENU_TYPE_BACKGROUND;
-        mMemRegion = icicle != null ? icicle.getInt("mem_region", LinearColorBar.REGION_GREEN)
+        mMemRegion = icicle != null ? icicle.getInt(MEM_REGION, LinearColorBar.REGION_GREEN)
                 : LinearColorBar.REGION_GREEN;
         setHasOptionsMenu(true);
     }
@@ -193,47 +165,31 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
     @Override
     public void onResume() {
         super.onResume();
-        refreshStats();
+        mStatsManager.refreshStats(false);
+        refreshUi();
     }
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putLong("duration", mDuration);
-        outState.putBoolean("show_system", mShowSystem);
-        outState.putBoolean("use_uss", mUseUss);
-        outState.putInt("stats_type", mStatsType);
-        outState.putInt("mem_region", mMemRegion);
+        outState.putLong(DURATION, mStatsManager.getDuration());
+        outState.putBoolean(SHOW_PERCENTAGE, mShowPercentage);
+        outState.putBoolean(SHOW_SYSTEM, mShowSystem);
+        outState.putBoolean(USE_USS, mUseUss);
+        outState.putInt(STATS_TYPE, mStatsType);
+        outState.putInt(MEM_REGION, mMemRegion);
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         if (getActivity().isChangingConfigurations()) {
-            sStatsXfer = mStats;
+            mStatsManager.xferStats();
         }
     }
 
     @Override
     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
-        if (preference instanceof LinearColorPreference) {
-            Bundle args = new Bundle();
-            args.putLongArray(ProcessStatsMemDetail.EXTRA_MEM_TIMES, mMemTimes);
-            args.putDoubleArray(ProcessStatsMemDetail.EXTRA_MEM_STATE_WEIGHTS, mMemStateWeights);
-            args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_CACHED_WEIGHT, mMemCachedWeight);
-            args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_FREE_WEIGHT, mMemFreeWeight);
-            args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_ZRAM_WEIGHT, mMemZRamWeight);
-            args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_KERNEL_WEIGHT, mMemKernelWeight);
-            args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_NATIVE_WEIGHT, mMemNativeWeight);
-            args.putDouble(ProcessStatsMemDetail.EXTRA_MEM_TOTAL_WEIGHT, mMemTotalWeight);
-            args.putBoolean(ProcessStatsMemDetail.EXTRA_USE_USS, mUseUss);
-            args.putLong(ProcessStatsMemDetail.EXTRA_TOTAL_TIME, mTotalTime);
-            ((SettingsActivity) getActivity()).startPreferencePanel(
-                    ProcessStatsMemDetail.class.getName(), args, R.string.mem_details_title,
-                    null, null, 0);
-            return true;
-        }
-
         if (!(preference instanceof ProcessStatsPreference)) {
             return false;
         }
@@ -242,9 +198,11 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
         Bundle args = new Bundle();
         args.putParcelable(ProcessStatsDetail.EXTRA_PACKAGE_ENTRY, pgp.getEntry());
         args.putBoolean(ProcessStatsDetail.EXTRA_USE_USS, mUseUss);
-        args.putDouble(ProcessStatsDetail.EXTRA_MAX_WEIGHT, mMaxWeight);
-        args.putDouble(ProcessStatsDetail.EXTRA_WEIGHT_TO_RAM, mWeightToRam);
+        args.putDouble(ProcessStatsDetail.EXTRA_WEIGHT_TO_RAM,
+                mStatsManager.getMemInfo().weightToRam);
         args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, mTotalTime);
+        args.putFloat(ProcessStatsDetail.EXTRA_MAX_MEMORY_USAGE, mMaxMemoryUsage);
+        args.putDouble(ProcessStatsDetail.EXTRA_TOTAL_SCALE, mStatsManager.getMemInfo().totalScale);
         ((SettingsActivity) getActivity()).startPreferencePanel(
                 ProcessStatsDetail.class.getName(), args, R.string.details_title, null, null, 0);
 
@@ -263,41 +221,37 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
             mDurationMenus[i] = subMenu.add(0, MENU_DURATION+i, 0, sDurationLabels[i])
                             .setCheckable(true);
         }
-        mShowSystemMenu = menu.add(0, MENU_SHOW_SYSTEM, 0, R.string.menu_show_system)
-                .setAlphabeticShortcut('s')
-                .setCheckable(true);
-        mUseUssMenu = menu.add(0, MENU_USE_USS, 0, R.string.menu_use_uss)
-                .setAlphabeticShortcut('u')
-                .setCheckable(true);
-        subMenu = menu.addSubMenu(R.string.menu_proc_stats_type);
-        mTypeBackgroundMenu = subMenu.add(0, MENU_TYPE_BACKGROUND, 0,
-                R.string.menu_proc_stats_type_background)
-                .setAlphabeticShortcut('b')
-                .setCheckable(true);
-        mTypeForegroundMenu = subMenu.add(0, MENU_TYPE_FOREGROUND, 0,
-                R.string.menu_proc_stats_type_foreground)
-                .setAlphabeticShortcut('f')
-                .setCheckable(true);
-        mTypeCachedMenu = subMenu.add(0, MENU_TYPE_CACHED, 0,
-                R.string.menu_proc_stats_type_cached)
-                .setCheckable(true);
+        // Hide these for now, until their need is determined.
+//        mShowPercentageMenu = menu.add(0, MENU_SHOW_PERCENTAGE, 0, R.string.menu_show_percentage)
+//                .setAlphabeticShortcut('p')
+//                .setCheckable(true);
+//        mShowSystemMenu = menu.add(0, MENU_SHOW_SYSTEM, 0, R.string.menu_show_system)
+//                .setAlphabeticShortcut('s')
+//                .setCheckable(true);
+//        mUseUssMenu = menu.add(0, MENU_USE_USS, 0, R.string.menu_use_uss)
+//                .setAlphabeticShortcut('u')
+//                .setCheckable(true);
+//        subMenu = menu.addSubMenu(R.string.menu_proc_stats_type);
+//        mTypeBackgroundMenu = subMenu.add(0, MENU_TYPE_BACKGROUND, 0,
+//                R.string.menu_proc_stats_type_background)
+//                .setAlphabeticShortcut('b')
+//                .setCheckable(true);
+//        mTypeForegroundMenu = subMenu.add(0, MENU_TYPE_FOREGROUND, 0,
+//                R.string.menu_proc_stats_type_foreground)
+//                .setAlphabeticShortcut('f')
+//                .setCheckable(true);
+//        mTypeCachedMenu = subMenu.add(0, MENU_TYPE_CACHED, 0,
+//                R.string.menu_proc_stats_type_cached)
+//                .setCheckable(true);
 
         updateMenus();
-
-        /*
-        String helpUrl;
-        if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_battery))) {
-            final MenuItem help = menu.add(0, MENU_HELP, 0, R.string.help_label);
-            HelpUtils.prepareHelpMenuItem(getActivity(), help, helpUrl);
-        }
-        */
     }
 
     void updateMenus() {
         int closestIndex = 0;
-        long closestDelta = Math.abs(sDurations[0]-mDuration);
-        for (int i=1; i<NUM_DURATIONS; i++) {
-            long delta = Math.abs(sDurations[i]-mDuration);
+        long closestDelta = Math.abs(sDurations[0] - mStatsManager.getDuration());
+        for (int i = 1; i < NUM_DURATIONS; i++) {
+            long delta = Math.abs(sDurations[i] - mStatsManager.getDuration());
             if (delta < closestDelta) {
                 closestDelta = delta;
                 closestIndex = i;
@@ -308,7 +262,10 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
                 mDurationMenus[i].setChecked(i == closestIndex);
             }
         }
-        mDuration = sDurations[closestIndex];
+        mStatsManager.setDuration(sDurations[closestIndex]);
+        if (mShowPercentageMenu != null) {
+            mShowPercentageMenu.setChecked(mShowPercentage);
+        }
         if (mShowSystemMenu != null) {
             mShowSystemMenu.setChecked(mShowSystem);
             mShowSystemMenu.setEnabled(mStatsType == MENU_TYPE_BACKGROUND);
@@ -332,46 +289,44 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
         final int id = item.getItemId();
         switch (id) {
             case MENU_STATS_REFRESH:
-                mStats = null;
-                refreshStats();
+                mStatsManager.refreshStats(false);
+                refreshUi();
+                return true;
+            case MENU_SHOW_PERCENTAGE:
+                mShowPercentage = !mShowPercentage;
+                refreshUi();
                 return true;
             case MENU_SHOW_SYSTEM:
                 mShowSystem = !mShowSystem;
-                refreshStats();
+                refreshUi();
                 return true;
             case MENU_USE_USS:
                 mUseUss = !mUseUss;
-                refreshStats();
+                refreshUi();
                 return true;
             case MENU_TYPE_BACKGROUND:
             case MENU_TYPE_FOREGROUND:
             case MENU_TYPE_CACHED:
                 mStatsType = item.getItemId();
-                refreshStats();
+                if (mStatsType == MENU_TYPE_FOREGROUND) {
+                    mStatsManager.setStats(FOREGROUND_PROC_STATES);
+                } else if (mStatsType == MENU_TYPE_CACHED) {
+                    mStatsManager.setStats(CACHED_PROC_STATES);
+                } else {
+                    mStatsManager.setStats(mShowSystem ? BACKGROUND_AND_SYSTEM_PROC_STATES
+                            : ProcessStats.BACKGROUND_PROC_STATES);
+                }
+                refreshUi();
                 return true;
             default:
-                if (id >= MENU_DURATION && id < (MENU_DURATION+NUM_DURATIONS)) {
-                    mDuration = sDurations[id-MENU_DURATION];
-                    refreshStats();
+                if (id >= MENU_DURATION && id < (MENU_DURATION + NUM_DURATIONS)) {
+                    mStatsManager.setDuration(sDurations[id - MENU_DURATION]);
+                    refreshUi();
                 }
                 return false;
         }
     }
 
-    @Override
-    public void onRegionTapped(int region) {
-        if (mMemRegion != region) {
-            mMemRegion = region;
-            refreshStats();
-        }
-    }
-
-    private void addNotAvailableMessage() {
-        Preference notAvailable = new Preference(getActivity());
-        notAvailable.setTitle(R.string.power_usage_not_available);
-        mAppListGroup.addPreference(notAvailable);
-    }
-
     /**
      * All states in which we consider a process to be actively running (rather than
      * something that can be freely killed to reclaim RAM).  Note this also includes
@@ -396,385 +351,75 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
             ProcessStats.STATE_CACHED_EMPTY
     };
 
-    public static final int[] RED_MEM_STATES = new int[] {
-            ProcessStats.ADJ_MEM_FACTOR_CRITICAL
-    };
-
-    public static final int[] YELLOW_MEM_STATES = new int[] {
-            ProcessStats.ADJ_MEM_FACTOR_CRITICAL, ProcessStats.ADJ_MEM_FACTOR_LOW,
-            ProcessStats.ADJ_MEM_FACTOR_MODERATE
-    };
-
-    private String makeDuration(long time) {
+    public static String makeDuration(long time) {
         StringBuilder sb = new StringBuilder(32);
         TimeUtils.formatDuration(time, sb);
         return sb.toString();
     }
 
-    private void refreshStats() {
+    private void refreshUi() {
         updateMenus();
 
-        if (mStats == null || mLastDuration != mDuration) {
-            load();
-        }
-
-        int[] stats;
-        int statsLabel;
-        if (mStatsType == MENU_TYPE_FOREGROUND) {
-            stats = FOREGROUND_PROC_STATES;
-            statsLabel = R.string.process_stats_type_foreground;
-        } else if (mStatsType == MENU_TYPE_CACHED) {
-            stats = CACHED_PROC_STATES;
-            statsLabel = R.string.process_stats_type_cached;
-        } else {
-            stats = mShowSystem ? BACKGROUND_AND_SYSTEM_PROC_STATES
-                    : ProcessStats.BACKGROUND_PROC_STATES;
-            statsLabel = R.string.process_stats_type_background;
-        }
 
         mAppListGroup.removeAll();
         mAppListGroup.setOrderingAsAdded(false);
+        mHeader.setOrder(-1);
+        mAppListGroup.addPreference(mHeader);
 
-        final long elapsedTime = mStats.mTimePeriodEndRealtime-mStats.mTimePeriodStartRealtime;
-
-        long now = SystemClock.uptimeMillis();
-
-        final PackageManager pm = getActivity().getPackageManager();
-
-        mTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations,
-                mStats.mMemFactor, mStats.mStartTime, now);
-        if (DEBUG) Log.d(TAG, "Total time of stats: " + makeDuration(mTotalTime));
-
-        for (int i=0; i<mMemTimes.length; i++) {
-            mMemTimes[i] = 0;
-        }
-        for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) {
-            for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) {
-                int state = imem+iscreen;
-                mMemTimes[imem] += mStats.mMemFactorDurations[state];
-            }
-        }
-
-        long memTotalTime;
-        int[] memStates;
+        final long elapsedTime = mStatsManager.getElapsedTime();
 
-        LinearColorPreference colors = new LinearColorPreference(getActivity());
-        colors.setOrder(-1);
-        switch (mMemRegion) {
-            case LinearColorBar.REGION_RED:
-                memTotalTime = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL];
-                memStates = RED_MEM_STATES;
-                break;
-            case LinearColorBar.REGION_YELLOW:
-                memTotalTime = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL]
-                        + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_LOW]
-                        + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE];
-                memStates = YELLOW_MEM_STATES;
-                break;
-            default:
-                memTotalTime = mTotalTime;
-                memStates = ProcessStats.ALL_MEM_ADJ;
-                break;
-        }
+        memTotalTime = mTotalTime;
         final Context context = getActivity();
-        colors.setColors(context.getColor(R.color.running_processes_apps_ram),
+        // TODO: More Colors.
+        mColors.setColors(context.getColor(R.color.running_processes_apps_ram),
                 context.getColor(R.color.running_processes_apps_ram),
                 context.getColor(R.color.running_processes_free_ram));
 
-        // Compute memory badness for chart color.
-        /*
-        int[] badColors = com.android.settings.Utils.BADNESS_COLORS;
-        long timeGood = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_NORMAL];
-        timeGood += (mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]*2)/3;
-        timeGood += mMemTimes[ProcessStats.ADJ_MEM_FACTOR_LOW]/3;
-        float memBadness = ((float)timeGood)/mTotalTime;
-        int badnessColor = badColors[1 + Math.round(memBadness*(badColors.length-2))];
-        colors.setColors(badnessColor, badnessColor, badnessColor);
-        */
-
-        // We are now going to scale the mMemTimes to match the total elapsed time.
-        // These are in uptime, so they will often be smaller than the elapsed time,
-        // but if the user taps on the bar we want to show the times to them.  It is confusing
-        // to see them be smaller than what we told them the measured duration is, so just
-        // scaling them up with make things look reasonable with them none the wiser.
-        for (int i=0; i<ProcessStats.ADJ_MEM_FACTOR_COUNT; i++) {
-            mMemTimes[i] = (long)((mMemTimes[i]*(double)elapsedTime)/mTotalTime);
-        }
-
-        ProcessStats.TotalMemoryUseCollection totalMem = new ProcessStats.TotalMemoryUseCollection(
-                ProcessStats.ALL_SCREEN_ADJ, memStates);
-        mStats.computeTotalMemoryUse(totalMem, now);
-        double freeWeight = totalMem.sysMemFreeWeight + totalMem.sysMemCachedWeight;
-        double usedWeight = totalMem.sysMemKernelWeight + totalMem.sysMemNativeWeight
-                + totalMem.sysMemZRamWeight;
-        double backgroundWeight = 0, persBackgroundWeight = 0;
-        mMemCachedWeight = totalMem.sysMemCachedWeight;
-        mMemFreeWeight = totalMem.sysMemFreeWeight;
-        mMemZRamWeight = totalMem.sysMemZRamWeight;
-        mMemKernelWeight = totalMem.sysMemKernelWeight;
-        mMemNativeWeight = totalMem.sysMemNativeWeight;
-        for (int i=0; i<ProcessStats.STATE_COUNT; i++) {
-            if (i == ProcessStats.STATE_SERVICE_RESTARTING) {
-                // These don't really run.
-                mMemStateWeights[i] = 0;
-            } else {
-                mMemStateWeights[i] = totalMem.processStateWeight[i];
-                if (i >= ProcessStats.STATE_HOME) {
-                    freeWeight += totalMem.processStateWeight[i];
-                } else {
-                    usedWeight += totalMem.processStateWeight[i];
-                }
-                if (i >= ProcessStats.STATE_IMPORTANT_FOREGROUND) {
-                    backgroundWeight += totalMem.processStateWeight[i];
-                    persBackgroundWeight += totalMem.processStateWeight[i];
-                }
-                if (i == ProcessStats.STATE_PERSISTENT) {
-                    persBackgroundWeight += totalMem.processStateWeight[i];
-                }
-            }
-        }
-        if (DEBUG) {
-            Log.i(TAG, "Used RAM: " + Formatter.formatShortFileSize(getActivity(),
-                    (long)((usedWeight * 1024) / memTotalTime)));
-            Log.i(TAG, "Free RAM: " + Formatter.formatShortFileSize(getActivity(),
-                    (long)((freeWeight * 1024) / memTotalTime)));
-            Log.i(TAG, "Total RAM: " + Formatter.formatShortFileSize(getActivity(),
-                    (long)(((freeWeight+usedWeight) * 1024) / memTotalTime)));
-            Log.i(TAG, "Background+Cached RAM: " + Formatter.formatShortFileSize(getActivity(),
-                    (long)((backgroundWeight * 1024) / memTotalTime)));
-        }
-        mMemTotalWeight = freeWeight + usedWeight;
-
         // For computing the ratio to show, we want to count the baseline cached RAM we
         // need (at which point we start killing processes) as used RAM, so that if we
         // reach the point of thrashing due to no RAM for any background processes we
         // report that as RAM being full.  To do this, we need to first convert the weights
         // back to actual RAM...  and since the RAM values we compute here won't exactly
         // match the real physical RAM, scale those to the actual physical RAM.  No problem!
-        double usedRam = (usedWeight*1024)/memTotalTime;
-        double freeRam = (freeWeight*1024)/memTotalTime;
-        double totalRam = usedRam + freeRam;
-        MemInfoReader memReader = new MemInfoReader();
-        memReader.readMemInfo();
-        double realTotalRam = memReader.getTotalSize();
-        double totalScale = realTotalRam / totalRam;
-        mWeightToRam = totalScale / memTotalTime * 1024;
-        mMaxWeight = totalRam / mWeightToRam;
-        double realUsedRam = usedRam * totalScale;
-        double realFreeRam = freeRam * totalScale;
-        if (DEBUG) {
-            Log.i(TAG, "Scaled Used RAM: " + Formatter.formatShortFileSize(getActivity(),
-                    (long)realUsedRam));
-            Log.i(TAG, "Scaled Free RAM: " + Formatter.formatShortFileSize(getActivity(),
-                    (long)realFreeRam));
-        }
-        ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
-        ((ActivityManager)getActivity().getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(
-                memInfo);
-        long baseCacheRam;
-        if (memInfo.hiddenAppThreshold >= realFreeRam) {
-            realUsedRam = realFreeRam;
-            realFreeRam = 0;
-            baseCacheRam = (long)realFreeRam;
-        } else {
-            realUsedRam += memInfo.hiddenAppThreshold;
-            realFreeRam -= memInfo.hiddenAppThreshold;
-            baseCacheRam = memInfo.hiddenAppThreshold;
-        }
-        if (DEBUG) {
-            Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(getActivity(),
-                    (long)realUsedRam));
-            Log.i(TAG, "Adj Scaled Free RAM: " + Formatter.formatShortFileSize(getActivity(),
-                    (long)realFreeRam));
-        }
+        MemInfo memInfo = mStatsManager.getMemInfo();
 
-        mMemStatusPref.setOrder(-2);
-        mAppListGroup.addPreference(mMemStatusPref);
-        String durationString = Utils.formatElapsedTime(getActivity(), elapsedTime, false);
-        String usedString = Formatter.formatShortFileSize(getActivity(), (long) realUsedRam);
-        String totalString = Formatter.formatShortFileSize(getActivity(), (long)realTotalRam);
+        String durationString = Utils.formatElapsedTime(context, elapsedTime, false);
+        String usedString = Formatter.formatShortFileSize(context, (long) memInfo.realUsedRam);
+        String totalString = Formatter.formatShortFileSize(context, (long) memInfo.realTotalRam);
         CharSequence memString;
         CharSequence[] memStatesStr = getResources().getTextArray(R.array.ram_states);
-        if (mMemState >= 0 && mMemState < memStatesStr.length) {
-            memString = memStatesStr[mMemState];
+        int memState = mStatsManager.getMemState();
+        if (memState >= 0 && memState < memStatesStr.length) {
+            memString = memStatesStr[memState];
         } else {
             memString = "?";
         }
-        mMemStatusPref.setTitle(getActivity().getString(R.string.process_stats_total_duration,
-                usedString, totalString, durationString));
-        mMemStatusPref.setSummary(getActivity().getString(R.string.process_stats_memory_status,
-                        memString)); 
-        float usedRatio = (float)(realUsedRam/(realFreeRam+realUsedRam));
-        colors.setRatios(usedRatio, 0, 1-usedRatio);
-
-        if (false) {
-            colors.setOnRegionTappedListener(this);
-            switch (mMemRegion) {
-                case LinearColorBar.REGION_RED:
-                    colors.setColoredRegions(LinearColorBar.REGION_RED);
-                    memTotalTime = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL];
-                    memStates = RED_MEM_STATES;
-                    break;
-                case LinearColorBar.REGION_YELLOW:
-                    colors.setColoredRegions(LinearColorBar.REGION_RED
-                            | LinearColorBar.REGION_YELLOW);
-                    memTotalTime = mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL]
-                            + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_LOW]
-                            + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE];
-                    memStates = YELLOW_MEM_STATES;
-                    break;
-                default:
-                    colors.setColoredRegions(LinearColorBar.REGION_ALL);
-                    memTotalTime = mTotalTime;
-                    memStates = ProcessStats.ALL_MEM_ADJ;
-                    break;
-            }
-            colors.setRatios(mMemTimes[ProcessStats.ADJ_MEM_FACTOR_CRITICAL] / (float)mTotalTime,
-                    (mMemTimes[ProcessStats.ADJ_MEM_FACTOR_LOW]
-                            + mMemTimes[ProcessStats.ADJ_MEM_FACTOR_MODERATE]) / (float)mTotalTime,
-                    mMemTimes[ProcessStats.ADJ_MEM_FACTOR_NORMAL] / (float)mTotalTime);
-        }
-
-        mAppListGroup.addPreference(colors);
-
-        ProcessStats.ProcessDataCollection bgTotals = new ProcessStats.ProcessDataCollection(
-                ProcessStats.ALL_SCREEN_ADJ, memStates, stats);
-        ProcessStats.ProcessDataCollection runTotals = new ProcessStats.ProcessDataCollection(
-                ProcessStats.ALL_SCREEN_ADJ, memStates, ProcessStats.NON_CACHED_PROC_STATES);
-
-        final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>();
-        final ArrayList<ProcStatsPackageEntry> pkgEntries = new ArrayList<>();
-
-        /*
-        ArrayList<ProcessStats.ProcessState> rawProcs = mStats.collectProcessesLocked(
-                ProcessStats.ALL_SCREEN_ADJ, ProcessStats.ALL_MEM_ADJ,
-                ProcessStats.BACKGROUND_PROC_STATES, now, null);
-        for (int i=0, N=(rawProcs != null ? rawProcs.size() : 0); i<N; i++) {
-            procs.add(new ProcStatsEntry(rawProcs.get(i), bgTotals));
-        }
-        */
-
-        if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES");
-
-        final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>();
-        for (int ipkg=0, N=mStats.mPackages.getMap().size(); ipkg<N; ipkg++) {
-            final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids
-                    = mStats.mPackages.getMap().valueAt(ipkg);
-            for (int iu=0; iu<pkgUids.size(); iu++) {
-                final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu);
-                for (int iv=0; iv<vpkgs.size(); iv++) {
-                    final ProcessStats.PackageState st = vpkgs.valueAt(iv);
-                    for (int iproc=0; iproc<st.mProcesses.size(); iproc++) {
-                        final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc);
-                        final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName,
-                                pkgProc.mUid);
-                        if (proc == null) {
-                            Log.w(TAG, "No process found for pkg " + st.mPackageName
-                                    + "/" + st.mUid + " proc name " + pkgProc.mName);
-                            continue;
-                        }
-                        ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid);
-                        if (ent == null) {
-                            ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals,
-                                    mUseUss);
-                            if (ent.mRunWeight > 0) {
-                                if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/"
-                                        + proc.mUid + ": time=" + makeDuration(ent.mRunDuration) + " ("
-                                        + ((((double)ent.mRunDuration) / memTotalTime) * 100) + "%)"
-                                        + " pss=" + ent.mAvgRunMem);
-                                entriesMap.put(proc.mName, proc.mUid, ent);
-                                procEntries.add(ent);
-                            }
-                        }  else {
-                            ent.addPackage(st.mPackageName);
-                        }
-                    }
-                }
-            }
-        }
-
-        if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES");
-
-        // Add in service info.
-        if (mStatsType == MENU_TYPE_BACKGROUND) {
-            for (int ip=0, N=mStats.mPackages.getMap().size(); ip<N; ip++) {
-                SparseArray<SparseArray<ProcessStats.PackageState>> uids
-                        = mStats.mPackages.getMap().valueAt(ip);
-                for (int iu=0; iu<uids.size(); iu++) {
-                    SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu);
-                    for (int iv=0; iv<vpkgs.size(); iv++) {
-                        ProcessStats.PackageState ps = vpkgs.valueAt(iv);
-                        for (int is=0, NS=ps.mServices.size(); is<NS; is++) {
-                            ProcessStats.ServiceState ss = ps.mServices.valueAt(is);
-                            if (ss.mProcessName != null) {
-                                ProcStatsEntry ent = entriesMap.get(ss.mProcessName,
-                                        uids.keyAt(iu));
-                                if (ent != null) {
-                                    if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName
-                                            + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc "
-                                            + ss.mProcessName);
-                                    ent.addService(ss);
-                                } else {
-                                    Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu)
-                                            + " for service " + ss.mName);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // Combine processes into packages.
-        HashMap<String, ProcStatsPackageEntry> pkgMap = new HashMap<>();
-        for (int i=procEntries.size()-1; i>=0; i--) {
-            ProcStatsEntry proc = procEntries.get(i);
-            proc.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
-            ProcStatsPackageEntry pkg = pkgMap.get(proc.mBestTargetPackage);
-            if (pkg == null) {
-                pkg = new ProcStatsPackageEntry(proc.mBestTargetPackage);
-                pkgMap.put(proc.mBestTargetPackage, pkg);
-                pkgEntries.add(pkg);
-            }
-            pkg.addEntry(proc);
+        if (mShowPercentage) {
+            mMemUsed.setText(context.getString(
+                    R.string.process_stats_total_duration_percentage,
+                    Utils.formatPercentage((long) memInfo.realUsedRam, (long) memInfo.realTotalRam),
+                    durationString));
+        } else {
+            mMemUsed.setText(context.getString(R.string.process_stats_total_duration,
+                    usedString, totalString, durationString));
         }
+        mMemStatus.setText(context.getString(R.string.process_stats_memory_status,
+                        memString));
+        float usedRatio = (float)(memInfo.realUsedRam
+                / (memInfo.realFreeRam + memInfo.realUsedRam));
+        mColors.setRatios(usedRatio, 0, 1-usedRatio);
 
-        // Add in fake entry representing the OS itself.
-        ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os");
-        pkgMap.put("os", osPkg);
-        pkgEntries.add(osPkg);
-        ProcStatsEntry osEntry;
-        if (totalMem.sysMemNativeWeight > 0) {
-            osEntry = new ProcStatsEntry("os", 0,
-                    getString(R.string.process_stats_os_native), memTotalTime,
-                    (long)(totalMem.sysMemNativeWeight/memTotalTime));
-            osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
-            osPkg.addEntry(osEntry);
-        }
-        if (totalMem.sysMemKernelWeight > 0) {
-            osEntry = new ProcStatsEntry("os", 0,
-                    getString(R.string.process_stats_os_kernel), memTotalTime,
-                    (long)(totalMem.sysMemKernelWeight/memTotalTime));
-            osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
-            osPkg.addEntry(osEntry);
-        }
-        if (totalMem.sysMemZRamWeight > 0) {
-            osEntry = new ProcStatsEntry("os", 0,
-                    getString(R.string.process_stats_os_zram), memTotalTime,
-                    (long)(totalMem.sysMemZRamWeight/memTotalTime));
-            osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
-            osPkg.addEntry(osEntry);
-        }
-        if (baseCacheRam > 0) {
-            osEntry = new ProcStatsEntry("os", 0,
-                    getString(R.string.process_stats_os_cache), memTotalTime, baseCacheRam/1024);
-            osEntry.evaluateTargetPackage(pm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
-            osPkg.addEntry(osEntry);
-        }
+        List<ProcStatsPackageEntry> pkgEntries = mStatsManager.getEntries();
 
+        // Update everything and get the absolute maximum of memory usage for scaling.
+        mMaxMemoryUsage = 0;
         for (int i=0, N=pkgEntries.size(); i<N; i++) {
             ProcStatsPackageEntry pkg = pkgEntries.get(i);
             pkg.updateMetrics();
+            float maxMem = Math.max(pkg.mMaxBgMem, pkg.mMaxRunMem);
+            if (maxMem > mMaxMemoryUsage) {
+                mMaxMemoryUsage = maxMem;
+            }
         }
 
         Collections.sort(pkgEntries, sPackageEntryCompare);
@@ -789,62 +434,25 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment
         int end = pkgEntries.size()-1;
         while (end >= 0) {
             ProcStatsPackageEntry pkg = pkgEntries.get(end);
-            final double percentOfWeight = (pkg.mRunWeight / mMaxWeight) * 100;
-            final double percentOfTime = (((double)pkg.mRunDuration) / memTotalTime) * 100;
+            final double percentOfWeight = (pkg.mRunWeight
+                    / (memInfo.totalRam / memInfo.weightToRam)) * 100;
+            final double percentOfTime = (((double) pkg.mRunDuration) / memTotalTime) * 100;
             if (percentOfWeight >= .01 || percentOfTime >= 25) {
                 break;
             }
             end--;
         }
-        for (int i=0; i<=end; i++) {
+        for (int i=0; i <= end; i++) {
             ProcStatsPackageEntry pkg = pkgEntries.get(i);
-            final double percentOfWeight = (pkg.mRunWeight / mMaxWeight) * 100;
-            final double percentOfTime = (((double)pkg.mRunDuration) / memTotalTime) * 100;
-            ProcessStatsPreference pref = new ProcessStatsPreference(getActivity());
-            pref.init(null, pkg);
-            pkg.retrieveUiData(getActivity(), pm);
-            pref.setTitle(pkg.mUiLabel);
-            if (pkg.mUiTargetApp != null) {
-                pref.setIcon(pkg.mUiTargetApp.loadIcon(pm));
-            }
+            ProcessStatsPreference pref = new ProcessStatsPreference(context);
+            pkg.retrieveUiData(context, mPm);
+            pref.init(pkg, mPm, mMaxMemoryUsage);
             pref.setOrder(i);
-            pref.setPercent(percentOfWeight, percentOfTime,
-                    (long)(pkg.mRunWeight * mWeightToRam));
             mAppListGroup.addPreference(pref);
-            if (mStatsType == MENU_TYPE_BACKGROUND) {
-                if (DEBUG) {
-                    Log.i(TAG, "App " + pkg.mUiLabel + ": weightedRam="
-                            + Formatter.formatShortFileSize(getActivity(),
-                                    (long)((pkg.mRunWeight * 1024) / memTotalTime))
-                            + ", avgRam=" + Formatter.formatShortFileSize(getActivity(),
-                                    (pkg.mAvgRunMem *1024)));
-                }
-
-            }
             if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) {
                 if (DEBUG) Log.d(TAG, "Done with UI, hit item limit!");
                 break;
             }
         }
     }
-
-    private void load() {
-        try {
-            mLastDuration = mDuration;
-            mMemState = mProcessStats.getCurrentMemoryState();
-            ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration);
-            mStats = new ProcessStats(false);
-            InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
-            mStats.read(is);
-            try {
-                is.close();
-            } catch (IOException e) {
-            }
-            if (mStats.mReadError != null) {
-                Log.w(TAG, "Failure reading process stats: " + mStats.mReadError);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException:", e);
-        }
-    }
 }