am 728ac353: New Running Services UI.

This commit is contained in:
Dianne Hackborn
2010-06-07 12:57:00 -07:00
committed by Android Git Automerger
21 changed files with 3359 additions and 1359 deletions

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="vertical"
android:gravity="fill" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingRight="6dip"
android:paddingLeft="6dip"
android:gravity="center_vertical" >
<ImageView android:id="@+id/icon"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:layout_marginRight="11dip"
android:layout_gravity="center_vertical"
android:scaleType="fitCenter"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:orientation="horizontal"
android:baselineAlignedChildIndex="0"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/name"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingRight="4dip"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="marquee"
android:layout_marginBottom="2dip" />
<TextView android:id="@+id/size"
android:layout_gravity="center_vertical|right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:baselineAlignedChildIndex="0"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/description"
android:layout_gravity="center_vertical|left"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingRight="4dip"
android:singleLine="true"
android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView android:id="@+id/uptime"
android:layout_gravity="center_vertical|right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
* Copyright (C) 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="false"
android:fastScrollEnabled="true" />
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/no_running_services"
android:textAppearance="?android:attr/textAppearanceLarge" />
</FrameLayout>
<view class="com.android.settings.applications.RunningProcessesView$LinearColorBar"
android:id="@+id/color_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dp">
<TextView android:id="@+id/foregroundText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:color="?android:attr/textColorPrimaryInverse"
android:singleLine="true" />
<TextView android:id="@+id/backgroundText"
android:layout_gravity="center_vertical|right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:color="?android:attr/textColorPrimaryInverse"
android:singleLine="true" />
</view>
</LinearLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/all_details"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Summary information as per previous screen -->
<include
layout="@layout/running_processes_item"
android:id="@+id/snippet"/>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Icon and label of the service. -->
<include
layout="@layout/running_processes_item"
android:id="@+id/service"/>
<TextView android:id="@+id/comp_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="14dip"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="?android:attr/listDivider"/>
-->
<!-- Icon and label of the service. -->
<include
layout="@layout/running_processes_item"
android:id="@+id/service"/>
<TextView android:id="@+id/comp_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingLeft="14dip"
android:textAppearance="?android:attr/textAppearanceMedium" />
<include
layout="@layout/two_buttons_panel"
android:id="@+id/control_buttons_panel"/>
</LinearLayout>

View File

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

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/listSeparatorTextViewStyle" />

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,548 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import com.android.settings.R;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
public class RunningProcessesView extends FrameLayout
implements AdapterView.OnItemClickListener {
/** Maximum number of services to retrieve */
static final int MAX_SERVICES = 100;
static final int MSG_UPDATE_TIMES = 1;
static final int MSG_UPDATE_CONTENTS = 2;
static final int MSG_REFRESH_UI = 3;
static final long TIME_UPDATE_DELAY = 1000;
static final long CONTENTS_UPDATE_DELAY = 2000;
// Memory pages are 4K.
static final long PAGE_SIZE = 4*1024;
long SECONDARY_SERVER_MEM;
final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
ActivityManager mAm;
RunningState mState;
StringBuilder mBuilder = new StringBuilder(128);
RunningState.BaseItem mCurSelected;
ListView mListView;
LinearColorBar mColorBar;
TextView mBackgroundProcessText;
TextView mForegroundProcessText;
int mLastNumBackgroundProcesses = -1;
int mLastNumForegroundProcesses = -1;
int mLastNumServiceProcesses = -1;
long mLastBackgroundProcessMemory = -1;
long mLastForegroundProcessMemory = -1;
long mLastServiceProcessMemory = -1;
long mLastAvailMemory = -1;
Dialog mCurDialog;
byte[] mBuffer = new byte[1024];
public static class ActiveItem {
View mRootView;
RunningState.BaseItem mItem;
ActivityManager.RunningServiceInfo mService;
ViewHolder mHolder;
long mFirstRunTime;
void updateTime(Context context, StringBuilder builder) {
TextView uptimeView = null;
if (mItem instanceof RunningState.ServiceItem) {
// If we are displaying a service, then the service
// uptime goes at the top.
uptimeView = mHolder.size;
} else {
String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
if (!size.equals(mItem.mCurSizeStr)) {
mItem.mCurSizeStr = size;
mHolder.size.setText(size);
}
if (mItem instanceof RunningState.MergedItem) {
// This item represents both services and proceses,
// so show the service uptime below.
uptimeView = mHolder.uptime;
}
}
if (uptimeView != null) {
if (mItem.mActiveSince >= 0) {
uptimeView.setText(DateUtils.formatElapsedTime(builder,
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
} else {
uptimeView.setText(context.getResources().getText(
R.string.service_restarting));
}
}
}
}
public static class ViewHolder {
public View rootView;
public ImageView icon;
public TextView name;
public TextView description;
public TextView size;
public TextView uptime;
public ViewHolder(View v) {
rootView = v;
icon = (ImageView)v.findViewById(R.id.icon);
name = (TextView)v.findViewById(R.id.name);
description = (TextView)v.findViewById(R.id.description);
size = (TextView)v.findViewById(R.id.size);
uptime = (TextView)v.findViewById(R.id.uptime);
v.setTag(this);
}
public ActiveItem bind(RunningState state, RunningState.BaseItem item,
StringBuilder builder) {
synchronized (state.mLock) {
name.setText(item.mDisplayLabel);
ActiveItem ai = new ActiveItem();
ai.mRootView = rootView;
ai.mItem = item;
ai.mHolder = this;
ai.mFirstRunTime = item.mActiveSince;
description.setText(item.mDescription);
item.mCurSizeStr = null;
icon.setImageDrawable(item.mPackageInfo.loadIcon(
rootView.getContext().getPackageManager()));
icon.setVisibility(View.VISIBLE);
ai.updateTime(rootView.getContext(), builder);
return ai;
}
}
}
static class TimeTicker extends TextView {
public TimeTicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
class ServiceListAdapter extends BaseAdapter {
final RunningState mState;
final LayoutInflater mInflater;
ArrayList<RunningState.MergedItem> mItems;
ServiceListAdapter(RunningState state) {
mState = state;
mInflater = (LayoutInflater)getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
refreshItems();
}
void refreshItems() {
ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
if (mItems != newItems) {
mItems = newItems;
}
if (mItems == null) {
mItems = new ArrayList<RunningState.MergedItem>();
}
}
public boolean hasStableIds() {
return true;
}
public int getCount() {
return mItems.size();
}
public Object getItem(int position) {
return mItems.get(position);
}
public long getItemId(int position) {
return mItems.get(position).hashCode();
}
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
return !mItems.get(position).mIsProcess;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
v = newView(parent);
} else {
v = convertView;
}
bindView(v, position);
return v;
}
public View newView(ViewGroup parent) {
View v = mInflater.inflate(R.layout.running_processes_item, parent, false);
new ViewHolder(v);
return v;
}
public void bindView(View view, int position) {
synchronized (mState.mLock) {
if (position >= mItems.size()) {
// List must have changed since we last reported its
// size... ignore here, we will be doing a data changed
// to refresh the entire list.
return;
}
ViewHolder vh = (ViewHolder) view.getTag();
RunningState.MergedItem item = mItems.get(position);
ActiveItem ai = vh.bind(mState, item, mBuilder);
mActiveItems.put(view, ai);
}
}
}
public static class LinearColorBar extends LinearLayout {
private float mRedRatio;
private float mYellowRatio;
private float mGreenRatio;
final Rect mRect = new Rect();
final Paint mPaint = new Paint();
public LinearColorBar(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
mPaint.setStyle(Paint.Style.FILL);
}
public void setRatios(float red, float yellow, float green) {
mRedRatio = red;
mYellowRatio = yellow;
mGreenRatio = green;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
mRect.top = 0;
mRect.bottom = getHeight();
int left = 0;
int right = left + (int)(width*mRedRatio);
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xffff8080);
canvas.drawRect(mRect, mPaint);
width -= (right-left);
left = right;
}
right = left + (int)(width*mYellowRatio);
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xffffff00);
canvas.drawRect(mRect, mPaint);
width -= (right-left);
left = right;
}
right = left + width;
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xff80ff80);
canvas.drawRect(mRect, mPaint);
}
}
}
HandlerThread mBackgroundThread;
final class BackgroundHandler extends Handler {
public BackgroundHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_CONTENTS:
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
mHandler.sendMessage(cmd);
removeMessages(MSG_UPDATE_CONTENTS);
msg = obtainMessage(MSG_UPDATE_CONTENTS);
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
break;
}
}
};
BackgroundHandler mBackgroundHandler;
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_TIMES:
Iterator<ActiveItem> it = mActiveItems.values().iterator();
while (it.hasNext()) {
ActiveItem ai = it.next();
if (ai.mRootView.getWindowToken() == null) {
// Clean out any dead views, just in case.
it.remove();
continue;
}
ai.updateTime(getContext(), mBuilder);
}
removeMessages(MSG_UPDATE_TIMES);
msg = obtainMessage(MSG_UPDATE_TIMES);
sendMessageDelayed(msg, TIME_UPDATE_DELAY);
break;
case MSG_REFRESH_UI:
refreshUi(msg.arg1 != 0);
break;
}
}
};
private boolean matchText(byte[] buffer, int index, String text) {
int N = text.length();
if ((index+N) >= buffer.length) {
return false;
}
for (int i=0; i<N; i++) {
if (buffer[index+i] != text.charAt(i)) {
return false;
}
}
return true;
}
private long extractMemValue(byte[] buffer, int index) {
while (index < buffer.length && buffer[index] != '\n') {
if (buffer[index] >= '0' && buffer[index] <= '9') {
int start = index;
index++;
while (index < buffer.length && buffer[index] >= '0'
&& buffer[index] <= '9') {
index++;
}
String str = new String(buffer, 0, start, index-start);
return ((long)Integer.parseInt(str)) * 1024;
}
index++;
}
return 0;
}
private long readAvailMem() {
try {
long memFree = 0;
long memCached = 0;
FileInputStream is = new FileInputStream("/proc/meminfo");
int len = is.read(mBuffer);
is.close();
final int BUFLEN = mBuffer.length;
for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
if (matchText(mBuffer, i, "MemFree")) {
i += 7;
memFree = extractMemValue(mBuffer, i);
} else if (matchText(mBuffer, i, "Cached")) {
i += 6;
memCached = extractMemValue(mBuffer, i);
}
while (i < BUFLEN && mBuffer[i] != '\n') {
i++;
}
}
return memFree + memCached;
} catch (java.io.FileNotFoundException e) {
} catch (java.io.IOException e) {
}
return 0;
}
void refreshUi(boolean dataChanged) {
if (dataChanged) {
ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
adapter.refreshItems();
adapter.notifyDataSetChanged();
}
// This is the amount of available memory until we start killing
// background services.
long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
if (availMem < 0) {
availMem = 0;
}
synchronized (mState.mLock) {
if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
|| mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
|| mLastAvailMemory != availMem) {
mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
mLastAvailMemory = availMem;
String sizeStr = Formatter.formatShortFileSize(getContext(),
mLastAvailMemory + mLastBackgroundProcessMemory);
mBackgroundProcessText.setText(getResources().getString(
R.string.service_background_processes, sizeStr));
}
if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
|| mLastForegroundProcessMemory != mState.mForegroundProcessMemory
|| mLastNumServiceProcesses != mState.mNumServiceProcesses
|| mLastServiceProcessMemory != mState.mServiceProcessMemory) {
mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
mLastNumServiceProcesses = mState.mNumServiceProcesses;
mLastServiceProcessMemory = mState.mServiceProcessMemory;
String sizeStr = Formatter.formatShortFileSize(getContext(),
mLastForegroundProcessMemory + mLastServiceProcessMemory);
mForegroundProcessText.setText(getResources().getString(
R.string.service_foreground_processes, sizeStr));
}
float totalMem = availMem + mLastBackgroundProcessMemory
+ mLastForegroundProcessMemory + mLastServiceProcessMemory;
mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
mLastServiceProcessMemory/totalMem,
(availMem+mLastBackgroundProcessMemory)/totalMem);
}
}
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
ListView l = (ListView)parent;
RunningState.MergedItem mi = (RunningState.MergedItem)l.getAdapter().getItem(position);
mCurSelected = mi;
Intent intent = new Intent();
intent.putExtra(RunningServiceDetails.KEY_UID, mi.mProcess.mUid);
intent.putExtra(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName);
intent.setClass(getContext(), RunningServiceDetails.class);
getContext().startActivity(intent);
}
public void onMovedToScrapHeap(View view) {
mActiveItems.remove(view);
}
public RunningProcessesView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
mState = (RunningState)nonConfigurationInstace;
if (mState == null) {
mState = new RunningState();
}
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.running_processes_view, this);
mListView = (ListView)findViewById(android.R.id.list);
View emptyView = findViewById(com.android.internal.R.id.empty);
if (emptyView != null) {
mListView.setEmptyView(emptyView);
}
mListView.setOnItemClickListener(this);
mListView.setAdapter(new ServiceListAdapter(mState));
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
// Magic! Implementation detail! Don't count on this!
SECONDARY_SERVER_MEM =
Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
}
public void doPause() {
mHandler.removeMessages(MSG_UPDATE_TIMES);
if (mBackgroundThread != null) {
mBackgroundThread.quit();
mBackgroundThread = null;
mBackgroundHandler = null;
}
}
public void doResume() {
refreshUi(mState.update(getContext(), mAm));
mBackgroundThread = new HandlerThread("RunningServices");
mBackgroundThread.start();
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
mHandler.removeMessages(MSG_UPDATE_TIMES);
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
}
public Object doRetainNonConfigurationInstance() {
return mState;
}
}

View File

@@ -0,0 +1,538 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import com.android.settings.R;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
public class RunningProcessesViewOld extends FrameLayout
implements AdapterView.OnItemClickListener {
static final String TAG = "RunningServices";
/** Maximum number of services to retrieve */
static final int MAX_SERVICES = 100;
static final int MSG_UPDATE_TIMES = 1;
static final int MSG_UPDATE_CONTENTS = 2;
static final int MSG_REFRESH_UI = 3;
static final long TIME_UPDATE_DELAY = 1000;
static final long CONTENTS_UPDATE_DELAY = 2000;
// Memory pages are 4K.
static final long PAGE_SIZE = 4*1024;
long SECONDARY_SERVER_MEM;
final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
ActivityManager mAm;
RunningState mState;
StringBuilder mBuilder = new StringBuilder(128);
RunningState.BaseItem mCurSelected;
int mProcessBgColor;
ListView mListView;
LinearColorBar mColorBar;
TextView mBackgroundProcessText;
TextView mForegroundProcessText;
int mLastNumBackgroundProcesses = -1;
int mLastNumForegroundProcesses = -1;
int mLastNumServiceProcesses = -1;
long mLastBackgroundProcessMemory = -1;
long mLastForegroundProcessMemory = -1;
long mLastServiceProcessMemory = -1;
long mLastAvailMemory = -1;
Dialog mCurDialog;
byte[] mBuffer = new byte[1024];
class ActiveItem {
View mRootView;
RunningState.BaseItem mItem;
ActivityManager.RunningServiceInfo mService;
ViewHolder mHolder;
long mFirstRunTime;
void updateTime(Context context) {
if (mItem.mIsProcess) {
String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
if (!size.equals(mItem.mCurSizeStr)) {
mItem.mCurSizeStr = size;
mHolder.size.setText(size);
}
} else {
if (mItem.mActiveSince >= 0) {
mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
} else {
mHolder.size.setText(context.getResources().getText(
R.string.service_restarting));
}
}
}
}
static class ViewHolder {
ImageView separator;
ImageView icon;
TextView name;
TextView description;
TextView size;
}
static class TimeTicker extends TextView {
public TimeTicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
class ServiceListAdapter extends BaseAdapter {
final RunningState mState;
final LayoutInflater mInflater;
ArrayList<RunningState.BaseItem> mItems;
ServiceListAdapter(RunningState state) {
mState = state;
mInflater = (LayoutInflater)getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
refreshItems();
}
void refreshItems() {
ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
if (mItems != newItems) {
mItems = newItems;
}
if (mItems == null) {
mItems = new ArrayList<RunningState.BaseItem>();
}
}
public boolean hasStableIds() {
return true;
}
public int getCount() {
return mItems.size();
}
public Object getItem(int position) {
return mItems.get(position);
}
public long getItemId(int position) {
return mItems.get(position).hashCode();
}
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
return !mItems.get(position).mIsProcess;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
v = newView(parent);
} else {
v = convertView;
}
bindView(v, position);
return v;
}
public View newView(ViewGroup parent) {
View v = mInflater.inflate(R.layout.running_services_item, parent, false);
ViewHolder h = new ViewHolder();
h.separator = (ImageView)v.findViewById(R.id.separator);
h.icon = (ImageView)v.findViewById(R.id.icon);
h.name = (TextView)v.findViewById(R.id.name);
h.description = (TextView)v.findViewById(R.id.description);
h.size = (TextView)v.findViewById(R.id.size);
v.setTag(h);
return v;
}
public void bindView(View view, int position) {
synchronized (mState.mLock) {
ViewHolder vh = (ViewHolder) view.getTag();
if (position >= mItems.size()) {
// List must have changed since we last reported its
// size... ignore here, we will be doing a data changed
// to refresh the entire list.
return;
}
RunningState.BaseItem item = mItems.get(position);
vh.name.setText(item.mDisplayLabel);
vh.separator.setVisibility(item.mNeedDivider
? View.VISIBLE : View.INVISIBLE);
ActiveItem ai = new ActiveItem();
ai.mRootView = view;
ai.mItem = item;
ai.mHolder = vh;
ai.mFirstRunTime = item.mActiveSince;
vh.description.setText(item.mDescription);
if (item.mIsProcess) {
view.setBackgroundColor(mProcessBgColor);
vh.icon.setImageDrawable(null);
vh.icon.setVisibility(View.GONE);
vh.description.setText(item.mDescription);
item.mCurSizeStr = null;
} else {
view.setBackgroundDrawable(null);
vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(
getContext().getPackageManager()));
vh.icon.setVisibility(View.VISIBLE);
vh.description.setText(item.mDescription);
ai.mFirstRunTime = item.mActiveSince;
}
ai.updateTime(getContext());
mActiveItems.put(view, ai);
}
}
}
public static class LinearColorBar extends LinearLayout {
private float mRedRatio;
private float mYellowRatio;
private float mGreenRatio;
final Rect mRect = new Rect();
final Paint mPaint = new Paint();
public LinearColorBar(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
mPaint.setStyle(Paint.Style.FILL);
}
public void setRatios(float red, float yellow, float green) {
mRedRatio = red;
mYellowRatio = yellow;
mGreenRatio = green;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
mRect.top = 0;
mRect.bottom = getHeight();
int left = 0;
int right = left + (int)(width*mRedRatio);
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xffff8080);
canvas.drawRect(mRect, mPaint);
width -= (right-left);
left = right;
}
right = left + (int)(width*mYellowRatio);
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xffffff00);
canvas.drawRect(mRect, mPaint);
width -= (right-left);
left = right;
}
right = left + width;
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xff80ff80);
canvas.drawRect(mRect, mPaint);
}
}
}
HandlerThread mBackgroundThread;
final class BackgroundHandler extends Handler {
public BackgroundHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_CONTENTS:
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
mHandler.sendMessage(cmd);
removeMessages(MSG_UPDATE_CONTENTS);
msg = obtainMessage(MSG_UPDATE_CONTENTS);
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
break;
}
}
};
BackgroundHandler mBackgroundHandler;
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_TIMES:
Iterator<ActiveItem> it = mActiveItems.values().iterator();
while (it.hasNext()) {
ActiveItem ai = it.next();
if (ai.mRootView.getWindowToken() == null) {
// Clean out any dead views, just in case.
it.remove();
continue;
}
ai.updateTime(getContext());
}
removeMessages(MSG_UPDATE_TIMES);
msg = obtainMessage(MSG_UPDATE_TIMES);
sendMessageDelayed(msg, TIME_UPDATE_DELAY);
break;
case MSG_REFRESH_UI:
refreshUi(msg.arg1 != 0);
break;
}
}
};
private boolean matchText(byte[] buffer, int index, String text) {
int N = text.length();
if ((index+N) >= buffer.length) {
return false;
}
for (int i=0; i<N; i++) {
if (buffer[index+i] != text.charAt(i)) {
return false;
}
}
return true;
}
private long extractMemValue(byte[] buffer, int index) {
while (index < buffer.length && buffer[index] != '\n') {
if (buffer[index] >= '0' && buffer[index] <= '9') {
int start = index;
index++;
while (index < buffer.length && buffer[index] >= '0'
&& buffer[index] <= '9') {
index++;
}
String str = new String(buffer, 0, start, index-start);
return ((long)Integer.parseInt(str)) * 1024;
}
index++;
}
return 0;
}
private long readAvailMem() {
try {
long memFree = 0;
long memCached = 0;
FileInputStream is = new FileInputStream("/proc/meminfo");
int len = is.read(mBuffer);
is.close();
final int BUFLEN = mBuffer.length;
for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
if (matchText(mBuffer, i, "MemFree")) {
i += 7;
memFree = extractMemValue(mBuffer, i);
} else if (matchText(mBuffer, i, "Cached")) {
i += 6;
memCached = extractMemValue(mBuffer, i);
}
while (i < BUFLEN && mBuffer[i] != '\n') {
i++;
}
}
return memFree + memCached;
} catch (java.io.FileNotFoundException e) {
} catch (java.io.IOException e) {
}
return 0;
}
void refreshUi(boolean dataChanged) {
if (dataChanged) {
ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
adapter.refreshItems();
adapter.notifyDataSetChanged();
}
// This is the amount of available memory until we start killing
// background services.
long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
if (availMem < 0) {
availMem = 0;
}
synchronized (mState.mLock) {
if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
|| mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
|| mLastAvailMemory != availMem) {
mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
mLastAvailMemory = availMem;
String sizeStr = Formatter.formatShortFileSize(getContext(),
mLastAvailMemory + mLastBackgroundProcessMemory);
mBackgroundProcessText.setText(getResources().getString(
R.string.service_background_processes, sizeStr));
}
if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
|| mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
String sizeStr = Formatter.formatShortFileSize(getContext(),
mLastForegroundProcessMemory);
mForegroundProcessText.setText(getResources().getString(
R.string.service_foreground_processes, sizeStr));
}
mLastNumServiceProcesses = mState.mNumServiceProcesses;
mLastServiceProcessMemory = mState.mServiceProcessMemory;
float totalMem = availMem + mLastBackgroundProcessMemory
+ mLastForegroundProcessMemory + mLastServiceProcessMemory;
mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
mLastServiceProcessMemory/totalMem,
(availMem+mLastBackgroundProcessMemory)/totalMem);
}
}
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
ListView l = (ListView)parent;
RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
mCurSelected = bi;
}
public void onMovedToScrapHeap(View view) {
mActiveItems.remove(view);
}
public RunningProcessesViewOld(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
mState = (RunningState)nonConfigurationInstace;
if (mState == null) {
mState = new RunningState();
}
mProcessBgColor = 0xff505050;
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.running_processes_view, this);
mListView = (ListView)findViewById(android.R.id.list);
View emptyView = findViewById(com.android.internal.R.id.empty);
if (emptyView != null) {
mListView.setEmptyView(emptyView);
}
mListView.setOnItemClickListener(this);
mListView.setDivider(null);
mListView.setAdapter(new ServiceListAdapter(mState));
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
// Magic! Implementation detail! Don't count on this!
SECONDARY_SERVER_MEM =
Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
}
public void doPause() {
mHandler.removeMessages(MSG_UPDATE_TIMES);
if (mBackgroundThread != null) {
mBackgroundThread.quit();
mBackgroundThread = null;
mBackgroundHandler = null;
}
}
public void doResume() {
refreshUi(mState.update(getContext(), mAm));
mBackgroundThread = new HandlerThread("RunningServices");
mBackgroundThread.start();
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
mHandler.removeMessages(MSG_UPDATE_TIMES);
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
}
public Object doRetainNonConfigurationInstance() {
return mState;
}
}

View File

@@ -0,0 +1,390 @@
package com.android.settings.applications;
import com.android.settings.R;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class RunningServiceDetails extends Activity {
static final String TAG = "RunningServicesDetails";
static final String KEY_UID = "uid";
static final String KEY_PROCESS = "process";
static final int MSG_UPDATE_TIMES = 1;
static final int MSG_UPDATE_CONTENTS = 2;
static final int MSG_REFRESH_UI = 3;
ActivityManager mAm;
LayoutInflater mInflater;
RunningState mState;
int mUid;
String mProcessName;
RunningState.MergedItem mMergedItem;
ViewGroup mAllDetails;
ViewGroup mSnippet;
RunningProcessesView.ActiveItem mSnippetActiveItem;
RunningProcessesView.ViewHolder mSnippetViewHolder;
TextView mServicesHeader;
TextView mProcessesHeader;
final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>();
class ActiveDetail implements View.OnClickListener {
View mRootView;
RunningProcessesView.ActiveItem mActiveItem;
RunningProcessesView.ViewHolder mViewHolder;
PendingIntent mManageIntent;
public void onClick(View v) {
if (mManageIntent != null) {
try {
startIntentSender(mManageIntent.getIntentSender(), null,
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
} catch (IntentSender.SendIntentException e) {
Log.w(TAG, e);
} catch (IllegalArgumentException e) {
Log.w(TAG, e);
} catch (ActivityNotFoundException e) {
Log.w(TAG, e);
}
} else {
RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem;
stopService(new Intent().setComponent(si.mRunningService.service));
if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
// If there was only one service, we are finishing it,
// so no reason for the UI to stick around.
finish();
} else {
if (mBackgroundHandler != null) {
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
}
}
}
}
StringBuilder mBuilder = new StringBuilder(128);
HandlerThread mBackgroundThread;
final class BackgroundHandler extends Handler {
public BackgroundHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_CONTENTS:
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0;
mHandler.sendMessage(cmd);
removeMessages(MSG_UPDATE_CONTENTS);
msg = obtainMessage(MSG_UPDATE_CONTENTS);
sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
break;
}
}
};
BackgroundHandler mBackgroundHandler;
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_TIMES:
if (mSnippetActiveItem != null) {
mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
}
for (int i=0; i<mActiveDetails.size(); i++) {
mActiveDetails.get(i).mActiveItem.updateTime(
RunningServiceDetails.this, mBuilder);
}
removeMessages(MSG_UPDATE_TIMES);
msg = obtainMessage(MSG_UPDATE_TIMES);
sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
break;
case MSG_REFRESH_UI:
refreshUi(msg.arg1 != 0);
break;
}
}
};
boolean findMergedItem() {
RunningState.MergedItem item = null;
ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
if (newItems != null) {
for (int i=0; i<newItems.size(); i++) {
RunningState.MergedItem mi = newItems.get(i);
if (mi.mProcess.mUid == mUid
&& mi.mProcess.mProcessName.equals(mProcessName)) {
item = mi;
break;
}
}
}
if (mMergedItem != item) {
mMergedItem = item;
return true;
}
return false;
}
void addDetailViews() {
for (int i=mActiveDetails.size()-1; i>=0; i--) {
mAllDetails.removeView(mActiveDetails.get(i).mRootView);
}
mActiveDetails.clear();
if (mServicesHeader != null) {
mAllDetails.removeView(mServicesHeader);
mServicesHeader = null;
}
if (mProcessesHeader != null) {
mAllDetails.removeView(mProcessesHeader);
mProcessesHeader = null;
}
if (mMergedItem != null) {
for (int i=0; i<mMergedItem.mServices.size(); i++) {
if (i == 0) {
mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
mAllDetails, false);
mServicesHeader.setText(R.string.runningservicedetails_services_title);
mAllDetails.addView(mServicesHeader);
}
RunningState.ServiceItem si = mMergedItem.mServices.get(i);
ActiveDetail detail = new ActiveDetail();
View root = mInflater.inflate(R.layout.running_service_details_service,
mAllDetails, false);
mAllDetails.addView(root);
detail.mRootView = root;
detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
detail.mActiveItem = detail.mViewHolder.bind(mState, si, mBuilder);
if (si.mRunningService.clientLabel != 0) {
detail.mManageIntent = mAm.getRunningServiceControlPanel(
si.mRunningService.service);
}
TextView description = (TextView)root.findViewById(R.id.comp_description);
if (si.mServiceInfo.descriptionRes != 0) {
description.setText(getPackageManager().getText(
si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
si.mServiceInfo.applicationInfo));
} else {
if (detail.mManageIntent != null) {
try {
Resources clientr = getPackageManager().getResourcesForApplication(
si.mRunningService.clientPackage);
String label = clientr.getString(si.mRunningService.clientLabel);
description.setText(getString(R.string.service_manage_description,
label));
} catch (PackageManager.NameNotFoundException e) {
}
} else {
description.setText(getText(R.string.service_stop_description));
}
}
View button = root.findViewById(R.id.right_button);
button.setOnClickListener(detail);
((TextView)button).setText(getText(detail.mManageIntent != null
? R.string.service_manage : R.string.service_stop));
root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
mActiveDetails.add(detail);
}
boolean didProcess = false;
for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) {
RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess
: mMergedItem.mOtherProcesses.get(i);
if (pi.mPid <= 0) {
continue;
}
if (!didProcess) {
mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label,
mAllDetails, false);
mProcessesHeader.setText(R.string.runningservicedetails_processes_title);
mAllDetails.addView(mProcessesHeader);
didProcess = true;
}
ActiveDetail detail = new ActiveDetail();
View root = mInflater.inflate(R.layout.running_service_details_process,
mAllDetails, false);
mAllDetails.addView(root);
detail.mRootView = root;
detail.mViewHolder = new RunningProcessesView.ViewHolder(root);
detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder);
TextView description = (TextView)root.findViewById(R.id.comp_description);
if (i < 0) {
description.setText(R.string.main_running_process_description);
} else {
int textid = 0;
CharSequence label = null;
ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo;
final ComponentName comp = rpi.importanceReasonComponent;
//Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode
// + " pid=" + rpi.importanceReasonPid + " comp=" + comp);
switch (rpi.importanceReasonCode) {
case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE:
textid = R.string.process_provider_in_use_description;
List<ProviderInfo> providers = null;
if (comp != null) {
providers = getPackageManager()
.queryContentProviders(comp.getPackageName(),
rpi.uid, 0);
}
if (providers != null) {
for (int j=0; j<providers.size(); j++) {
ProviderInfo prov = providers.get(i);
if (comp.getClassName().equals(prov.name)) {
label = RunningState.makeLabel(getPackageManager(),
prov.name, prov);
break;
}
}
}
break;
case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE:
textid = R.string.process_service_in_use_description;
if (rpi.importanceReasonComponent != null) {
try {
ServiceInfo serv = getPackageManager().getServiceInfo(
rpi.importanceReasonComponent, 0);
label = RunningState.makeLabel(getPackageManager(),
serv.name, serv);
} catch (NameNotFoundException e) {
}
}
break;
}
if (textid != 0 && label != null) {
description.setText(getString(textid, label));
}
}
mActiveDetails.add(detail);
}
}
}
void refreshUi(boolean dataChanged) {
if (findMergedItem()) {
dataChanged = true;
}
if (dataChanged) {
if (mMergedItem != null) {
mSnippetActiveItem = mSnippetViewHolder.bind(mState,
mMergedItem, mBuilder);
} else if (mSnippetActiveItem != null) {
// Clear whatever is currently being shown.
mSnippetActiveItem.mHolder.size.setText("");
mSnippetActiveItem.mHolder.uptime.setText("");
mSnippetActiveItem.mHolder.description.setText(R.string.no_services);
} else {
// No merged item, never had one. Nothing to do.
finish();
return;
}
addDetailViews();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mUid = getIntent().getIntExtra(KEY_UID, 0);
mProcessName = getIntent().getStringExtra(KEY_PROCESS);
mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mState = (RunningState)getLastNonConfigurationInstance();
if (mState == null) {
mState = new RunningState();
}
setContentView(R.layout.running_service_details);
mAllDetails = (ViewGroup)findViewById(R.id.all_details);
mSnippet = (ViewGroup)findViewById(R.id.snippet);
mSnippet.setBackgroundResource(com.android.internal.R.drawable.title_bar_medium);
mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom());
mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet);
}
@Override
protected void onPause() {
super.onPause();
mHandler.removeMessages(MSG_UPDATE_TIMES);
if (mBackgroundThread != null) {
mBackgroundThread.quit();
mBackgroundThread = null;
mBackgroundHandler = null;
}
}
@Override
protected void onResume() {
super.onResume();
refreshUi(mState.update(this, mAm));
mBackgroundThread = new HandlerThread("RunningServices");
mBackgroundThread.start();
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
mHandler.removeMessages(MSG_UPDATE_TIMES);
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
mHandler.sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
}
@Override
public Object onRetainNonConfigurationInstance() {
return mState;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
}

View File

@@ -0,0 +1,582 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import com.android.settings.R;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
public class RunningServices extends ListActivity
implements AbsListView.RecyclerListener,
DialogInterface.OnClickListener {
static final String TAG = "RunningServices";
/** Maximum number of services to retrieve */
static final int MAX_SERVICES = 100;
static final int MSG_UPDATE_TIMES = 1;
static final int MSG_UPDATE_CONTENTS = 2;
static final int MSG_REFRESH_UI = 3;
static final long TIME_UPDATE_DELAY = 1000;
static final long CONTENTS_UPDATE_DELAY = 2000;
// Memory pages are 4K.
static final long PAGE_SIZE = 4*1024;
long SECONDARY_SERVER_MEM;
final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
ActivityManager mAm;
RunningState mState;
StringBuilder mBuilder = new StringBuilder(128);
RunningState.BaseItem mCurSelected;
int mProcessBgColor;
LinearColorBar mColorBar;
TextView mBackgroundProcessText;
TextView mForegroundProcessText;
int mLastNumBackgroundProcesses = -1;
int mLastNumForegroundProcesses = -1;
int mLastNumServiceProcesses = -1;
long mLastBackgroundProcessMemory = -1;
long mLastForegroundProcessMemory = -1;
long mLastServiceProcessMemory = -1;
long mLastAvailMemory = -1;
Dialog mCurDialog;
byte[] mBuffer = new byte[1024];
class ActiveItem {
View mRootView;
RunningState.BaseItem mItem;
ActivityManager.RunningServiceInfo mService;
ViewHolder mHolder;
long mFirstRunTime;
void updateTime(Context context) {
if (mItem.mIsProcess) {
String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
if (!size.equals(mItem.mCurSizeStr)) {
mItem.mCurSizeStr = size;
mHolder.size.setText(size);
}
} else {
if (mItem.mActiveSince >= 0) {
mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
} else {
mHolder.size.setText(context.getResources().getText(
R.string.service_restarting));
}
}
}
}
static class ViewHolder {
ImageView separator;
ImageView icon;
TextView name;
TextView description;
TextView size;
}
static class TimeTicker extends TextView {
public TimeTicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
class ServiceListAdapter extends BaseAdapter {
final RunningState mState;
final LayoutInflater mInflater;
ArrayList<RunningState.BaseItem> mItems;
ServiceListAdapter(RunningState state) {
mState = state;
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
refreshItems();
}
void refreshItems() {
ArrayList<RunningState.BaseItem> newItems = mState.getCurrentItems();
if (mItems != newItems) {
mItems = newItems;
}
if (mItems == null) {
mItems = new ArrayList<RunningState.BaseItem>();
}
}
public boolean hasStableIds() {
return true;
}
public int getCount() {
return mItems.size();
}
public Object getItem(int position) {
return mItems.get(position);
}
public long getItemId(int position) {
return mItems.get(position).hashCode();
}
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
return !mItems.get(position).mIsProcess;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
v = newView(parent);
} else {
v = convertView;
}
bindView(v, position);
return v;
}
public View newView(ViewGroup parent) {
View v = mInflater.inflate(R.layout.running_services_item, parent, false);
ViewHolder h = new ViewHolder();
h.separator = (ImageView)v.findViewById(R.id.separator);
h.icon = (ImageView)v.findViewById(R.id.icon);
h.name = (TextView)v.findViewById(R.id.name);
h.description = (TextView)v.findViewById(R.id.description);
h.size = (TextView)v.findViewById(R.id.size);
v.setTag(h);
return v;
}
public void bindView(View view, int position) {
synchronized (mState.mLock) {
ViewHolder vh = (ViewHolder) view.getTag();
if (position >= mItems.size()) {
// List must have changed since we last reported its
// size... ignore here, we will be doing a data changed
// to refresh the entire list.
return;
}
RunningState.BaseItem item = mItems.get(position);
vh.name.setText(item.mDisplayLabel);
vh.separator.setVisibility(item.mNeedDivider
? View.VISIBLE : View.INVISIBLE);
ActiveItem ai = new ActiveItem();
ai.mRootView = view;
ai.mItem = item;
ai.mHolder = vh;
ai.mFirstRunTime = item.mActiveSince;
vh.description.setText(item.mDescription);
if (item.mIsProcess) {
view.setBackgroundColor(mProcessBgColor);
vh.icon.setImageDrawable(null);
vh.icon.setVisibility(View.GONE);
vh.description.setText(item.mDescription);
item.mCurSizeStr = null;
} else {
view.setBackgroundDrawable(null);
vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager()));
vh.icon.setVisibility(View.VISIBLE);
vh.description.setText(item.mDescription);
ai.mFirstRunTime = item.mActiveSince;
}
ai.updateTime(RunningServices.this);
mActiveItems.put(view, ai);
}
}
}
public static class LinearColorBar extends LinearLayout {
private float mRedRatio;
private float mYellowRatio;
private float mGreenRatio;
final Rect mRect = new Rect();
final Paint mPaint = new Paint();
public LinearColorBar(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
mPaint.setStyle(Paint.Style.FILL);
}
public void setRatios(float red, float yellow, float green) {
mRedRatio = red;
mYellowRatio = yellow;
mGreenRatio = green;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
mRect.top = 0;
mRect.bottom = getHeight();
int left = 0;
int right = left + (int)(width*mRedRatio);
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xffff8080);
canvas.drawRect(mRect, mPaint);
width -= (right-left);
left = right;
}
right = left + (int)(width*mYellowRatio);
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xffffff00);
canvas.drawRect(mRect, mPaint);
width -= (right-left);
left = right;
}
right = left + width;
if (left < right) {
mRect.left = left;
mRect.right = right;
mPaint.setColor(0xff80ff80);
canvas.drawRect(mRect, mPaint);
}
}
}
HandlerThread mBackgroundThread;
final class BackgroundHandler extends Handler {
public BackgroundHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_CONTENTS:
Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
cmd.arg1 = mState.update(RunningServices.this, mAm) ? 1 : 0;
mHandler.sendMessage(cmd);
removeMessages(MSG_UPDATE_CONTENTS);
msg = obtainMessage(MSG_UPDATE_CONTENTS);
sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
break;
}
}
};
BackgroundHandler mBackgroundHandler;
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_TIMES:
Iterator<ActiveItem> it = mActiveItems.values().iterator();
while (it.hasNext()) {
ActiveItem ai = it.next();
if (ai.mRootView.getWindowToken() == null) {
// Clean out any dead views, just in case.
it.remove();
continue;
}
ai.updateTime(RunningServices.this);
}
removeMessages(MSG_UPDATE_TIMES);
msg = obtainMessage(MSG_UPDATE_TIMES);
sendMessageDelayed(msg, TIME_UPDATE_DELAY);
break;
case MSG_REFRESH_UI:
refreshUi(msg.arg1 != 0);
break;
}
}
};
private boolean matchText(byte[] buffer, int index, String text) {
int N = text.length();
if ((index+N) >= buffer.length) {
return false;
}
for (int i=0; i<N; i++) {
if (buffer[index+i] != text.charAt(i)) {
return false;
}
}
return true;
}
private long extractMemValue(byte[] buffer, int index) {
while (index < buffer.length && buffer[index] != '\n') {
if (buffer[index] >= '0' && buffer[index] <= '9') {
int start = index;
index++;
while (index < buffer.length && buffer[index] >= '0'
&& buffer[index] <= '9') {
index++;
}
String str = new String(buffer, 0, start, index-start);
return ((long)Integer.parseInt(str)) * 1024;
}
index++;
}
return 0;
}
private long readAvailMem() {
try {
long memFree = 0;
long memCached = 0;
FileInputStream is = new FileInputStream("/proc/meminfo");
int len = is.read(mBuffer);
is.close();
final int BUFLEN = mBuffer.length;
for (int i=0; i<len && (memFree == 0 || memCached == 0); i++) {
if (matchText(mBuffer, i, "MemFree")) {
i += 7;
memFree = extractMemValue(mBuffer, i);
} else if (matchText(mBuffer, i, "Cached")) {
i += 6;
memCached = extractMemValue(mBuffer, i);
}
while (i < BUFLEN && mBuffer[i] != '\n') {
i++;
}
}
return memFree + memCached;
} catch (java.io.FileNotFoundException e) {
} catch (java.io.IOException e) {
}
return 0;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
mState = (RunningState)getLastNonConfigurationInstance();
if (mState == null) {
mState = new RunningState();
}
mProcessBgColor = 0xff505050;
setContentView(R.layout.running_services);
getListView().setDivider(null);
getListView().setAdapter(new ServiceListAdapter(mState));
mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
// Magic! Implementation detail! Don't count on this!
SECONDARY_SERVER_MEM =
Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE;
}
void refreshUi(boolean dataChanged) {
if (dataChanged) {
ServiceListAdapter adapter = (ServiceListAdapter)(getListView().getAdapter());
adapter.refreshItems();
adapter.notifyDataSetChanged();
}
// This is the amount of available memory until we start killing
// background services.
long availMem = readAvailMem() - SECONDARY_SERVER_MEM;
if (availMem < 0) {
availMem = 0;
}
synchronized (mState.mLock) {
if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
|| mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
|| mLastAvailMemory != availMem) {
mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
mLastAvailMemory = availMem;
String availStr = availMem != 0
? Formatter.formatShortFileSize(this, availMem) : "0";
String sizeStr = Formatter.formatShortFileSize(this, mLastBackgroundProcessMemory);
mBackgroundProcessText.setText(getResources().getString(
R.string.service_background_processes,
mLastNumBackgroundProcesses, availStr, sizeStr));
}
if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
|| mLastForegroundProcessMemory != mState.mForegroundProcessMemory) {
mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
String sizeStr = Formatter.formatShortFileSize(this, mLastForegroundProcessMemory);
mForegroundProcessText.setText(getResources().getString(
R.string.service_foreground_processes, mLastNumForegroundProcesses, sizeStr));
}
mLastNumServiceProcesses = mState.mNumServiceProcesses;
mLastServiceProcessMemory = mState.mServiceProcessMemory;
float totalMem = availMem + mLastBackgroundProcessMemory
+ mLastForegroundProcessMemory + mLastServiceProcessMemory;
mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
mLastServiceProcessMemory/totalMem,
(availMem+mLastBackgroundProcessMemory)/totalMem);
}
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position);
if (!bi.mIsProcess) {
RunningState.ServiceItem si = (RunningState.ServiceItem)bi;
if (si.mRunningService.clientLabel != 0) {
mCurSelected = null;
PendingIntent pi = mAm.getRunningServiceControlPanel(
si.mRunningService.service);
if (pi != null) {
try {
this.startIntentSender(pi.getIntentSender(), null,
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET,
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0);
} catch (IntentSender.SendIntentException e) {
Log.w(TAG, e);
} catch (IllegalArgumentException e) {
Log.w(TAG, e);
} catch (ActivityNotFoundException e) {
Log.w(TAG, e);
}
}
} else {
mCurSelected = bi;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.confirm_stop_service);
String msg = getResources().getString(
R.string.confirm_stop_service_msg,
si.mPackageInfo.loadLabel(getPackageManager()));
builder.setMessage(msg);
builder.setPositiveButton(R.string.confirm_stop_stop, this);
builder.setNegativeButton(R.string.confirm_stop_cancel, null);
builder.setCancelable(true);
mCurDialog = builder.show();
}
} else {
mCurSelected = null;
}
}
public void onClick(DialogInterface dialog, int which) {
if (mCurSelected != null) {
stopService(new Intent().setComponent(
((RunningState.ServiceItem)mCurSelected).mRunningService.service));
if (mBackgroundHandler != null) {
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
}
}
@Override
protected void onPause() {
super.onPause();
mHandler.removeMessages(MSG_UPDATE_TIMES);
if (mBackgroundThread != null) {
mBackgroundThread.quit();
mBackgroundThread = null;
mBackgroundHandler = null;
}
}
@Override
protected void onResume() {
super.onResume();
refreshUi(mState.update(this, mAm));
mBackgroundThread = new HandlerThread("RunningServices");
mBackgroundThread.start();
mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
mHandler.removeMessages(MSG_UPDATE_TIMES);
Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
}
@Override
public Object onRetainNonConfigurationInstance() {
return mState;
}
public void onMovedToScrapHeap(View view) {
mActiveItems.remove(view);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mCurDialog != null) {
mCurDialog.dismiss();
}
}
}

View File

@@ -0,0 +1,736 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.applications;
import com.android.settings.R;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.Debug;
import android.os.RemoteException;
import android.text.format.Formatter;
import android.util.Log;
import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* Singleton for retrieving and monitoring the state about all running
* applications/processes/services.
*/
public class RunningState {
final SparseArray<HashMap<String, ProcessItem>> mProcesses
= new SparseArray<HashMap<String, ProcessItem>>();
final SparseArray<ProcessItem> mActiveProcesses
= new SparseArray<ProcessItem>();
final ServiceProcessComparator mServiceProcessComparator
= new ServiceProcessComparator();
// Temporary for finding process dependencies.
final SparseArray<ProcessItem> mRunningProcesses
= new SparseArray<ProcessItem>();
final ArrayList<ProcessItem> mProcessItems = new ArrayList<ProcessItem>();
final ArrayList<ProcessItem> mAllProcessItems = new ArrayList<ProcessItem>();
int mSequence = 0;
// ----- following protected by mLock -----
// Lock for protecting the state that will be shared between the
// background update thread and the UI thread.
final Object mLock = new Object();
ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
int mNumBackgroundProcesses;
long mBackgroundProcessMemory;
int mNumForegroundProcesses;
long mForegroundProcessMemory;
int mNumServiceProcesses;
long mServiceProcessMemory;
static class BaseItem {
final boolean mIsProcess;
PackageItemInfo mPackageInfo;
CharSequence mDisplayLabel;
String mLabel;
String mDescription;
int mCurSeq;
long mActiveSince;
long mSize;
String mSizeStr;
String mCurSizeStr;
boolean mNeedDivider;
public BaseItem(boolean isProcess) {
mIsProcess = isProcess;
}
}
static class ServiceItem extends BaseItem {
ActivityManager.RunningServiceInfo mRunningService;
ServiceInfo mServiceInfo;
boolean mShownAsStarted;
MergedItem mMergedItem;
public ServiceItem() {
super(false);
}
}
static class ProcessItem extends BaseItem {
final HashMap<ComponentName, ServiceItem> mServices
= new HashMap<ComponentName, ServiceItem>();
final SparseArray<ProcessItem> mDependentProcesses
= new SparseArray<ProcessItem>();
final int mUid;
final String mProcessName;
int mPid;
ProcessItem mClient;
int mLastNumDependentProcesses;
int mRunningSeq;
ActivityManager.RunningAppProcessInfo mRunningProcessInfo;
// Purely for sorting.
boolean mIsSystem;
boolean mIsStarted;
long mActiveSince;
public ProcessItem(Context context, int uid, String processName) {
super(true);
mDescription = context.getResources().getString(
R.string.service_process_name, processName);
mUid = uid;
mProcessName = processName;
}
void ensureLabel(PackageManager pm) {
if (mLabel != null) {
return;
}
try {
ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0);
if (ai.uid == mUid) {
mDisplayLabel = ai.loadLabel(pm);
mLabel = mDisplayLabel.toString();
mPackageInfo = ai;
return;
}
} catch (PackageManager.NameNotFoundException e) {
}
// If we couldn't get information about the overall
// process, try to find something about the uid.
String[] pkgs = pm.getPackagesForUid(mUid);
// If there is one package with this uid, that is what we want.
if (pkgs.length == 1) {
try {
ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
mDisplayLabel = ai.loadLabel(pm);
mLabel = mDisplayLabel.toString();
mPackageInfo = ai;
return;
} catch (PackageManager.NameNotFoundException e) {
}
}
// If there are multiple, see if one gives us the official name
// for this uid.
for (String name : pkgs) {
try {
PackageInfo pi = pm.getPackageInfo(name, 0);
if (pi.sharedUserLabel != 0) {
CharSequence nm = pm.getText(name,
pi.sharedUserLabel, pi.applicationInfo);
if (nm != null) {
mDisplayLabel = nm;
mLabel = nm.toString();
mPackageInfo = pi.applicationInfo;
return;
}
}
} catch (PackageManager.NameNotFoundException e) {
}
}
// If still don't have anything to display, just use the
// service info.
if (mServices.size() > 0) {
mPackageInfo = mServices.values().iterator().next()
.mServiceInfo.applicationInfo;
mDisplayLabel = mPackageInfo.loadLabel(pm);
mLabel = mDisplayLabel.toString();
return;
}
// Finally... whatever, just pick the first package's name.
try {
ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0);
mDisplayLabel = ai.loadLabel(pm);
mLabel = mDisplayLabel.toString();
mPackageInfo = ai;
return;
} catch (PackageManager.NameNotFoundException e) {
}
}
boolean updateService(Context context,
ActivityManager.RunningServiceInfo service) {
final PackageManager pm = context.getPackageManager();
boolean changed = false;
ServiceItem si = mServices.get(service.service);
if (si == null) {
changed = true;
si = new ServiceItem();
si.mRunningService = service;
try {
si.mServiceInfo = pm.getServiceInfo(service.service, 0);
} catch (PackageManager.NameNotFoundException e) {
}
si.mDisplayLabel = makeLabel(pm,
si.mRunningService.service.getClassName(), si.mServiceInfo);
mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null;
si.mPackageInfo = si.mServiceInfo.applicationInfo;
mServices.put(service.service, si);
}
si.mCurSeq = mCurSeq;
si.mRunningService = service;
long activeSince = service.restarting == 0 ? service.activeSince : -1;
if (si.mActiveSince != activeSince) {
si.mActiveSince = activeSince;
changed = true;
}
if (service.clientPackage != null && service.clientLabel != 0) {
if (si.mShownAsStarted) {
si.mShownAsStarted = false;
changed = true;
}
try {
Resources clientr = pm.getResourcesForApplication(service.clientPackage);
String label = clientr.getString(service.clientLabel);
si.mDescription = context.getResources().getString(
R.string.service_client_name, label);
} catch (PackageManager.NameNotFoundException e) {
si.mDescription = null;
}
} else {
if (!si.mShownAsStarted) {
si.mShownAsStarted = true;
changed = true;
}
si.mDescription = context.getResources().getString(
R.string.service_started_by_app);
}
return changed;
}
boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) {
mSize = ((long)mem.getTotalPss()) * 1024;
if (mCurSeq == curSeq) {
String sizeStr = Formatter.formatShortFileSize(
context, mSize);
if (!sizeStr.equals(mSizeStr)){
mSizeStr = sizeStr;
// We update this on the second tick where we update just
// the text in the current items, so no need to say we
// changed here.
return false;
}
}
return false;
}
boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) {
final int NP = mDependentProcesses.size();
boolean changed = false;
for (int i=0; i<NP; i++) {
ProcessItem proc = mDependentProcesses.valueAt(i);
if (proc.mClient != this) {
changed = true;
proc.mClient = this;
}
proc.mCurSeq = curSeq;
proc.ensureLabel(pm);
changed |= proc.buildDependencyChain(context, pm, curSeq);
}
if (mLastNumDependentProcesses != mDependentProcesses.size()) {
changed = true;
mLastNumDependentProcesses = mDependentProcesses.size();
}
return changed;
}
void addDependentProcesses(ArrayList<BaseItem> dest,
ArrayList<ProcessItem> destProc) {
final int NP = mDependentProcesses.size();
for (int i=0; i<NP; i++) {
ProcessItem proc = mDependentProcesses.valueAt(i);
proc.addDependentProcesses(dest, destProc);
dest.add(proc);
if (proc.mPid > 0) {
destProc.add(proc);
}
}
}
}
static class MergedItem extends BaseItem {
ProcessItem mProcess;
final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
MergedItem() {
super(false);
}
boolean update(Context context) {
mPackageInfo = mProcess.mPackageInfo;
mDisplayLabel = mProcess.mDisplayLabel;
mLabel = mProcess.mLabel;
int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size();
int numServices = mServices.size();
int resid = R.string.running_processes_item_description_s_s;
if (numProcesses != 1) {
resid = numServices != 1
? R.string.running_processes_item_description_p_p
: R.string.running_processes_item_description_p_s;
} else if (numServices != 1) {
resid = R.string.running_processes_item_description_s_p;
}
mDescription = context.getResources().getString(resid, numProcesses, numServices);
mActiveSince = -1;
for (int i=0; i<mServices.size(); i++) {
ServiceItem si = mServices.get(i);
if (si.mActiveSince >= 0 && mActiveSince < si.mActiveSince) {
mActiveSince = si.mActiveSince;
}
}
return false;
}
boolean updateSize(Context context) {
mSize = mProcess.mSize;
for (int i=0; i<mOtherProcesses.size(); i++) {
mSize += mOtherProcesses.get(i).mSize;
}
String sizeStr = Formatter.formatShortFileSize(
context, mSize);
if (!sizeStr.equals(mSizeStr)){
mSizeStr = sizeStr;
// We update this on the second tick where we update just
// the text in the current items, so no need to say we
// changed here.
return false;
}
return false;
}
}
static class ServiceProcessComparator implements Comparator<ProcessItem> {
public int compare(ProcessItem object1, ProcessItem object2) {
if (object1.mIsStarted != object2.mIsStarted) {
// Non-started processes go last.
return object1.mIsStarted ? -1 : 1;
}
if (object1.mIsSystem != object2.mIsSystem) {
// System processes go below non-system.
return object1.mIsSystem ? 1 : -1;
}
if (object1.mActiveSince != object2.mActiveSince) {
// Remaining ones are sorted with the longest running
// services last.
return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1;
}
return 0;
}
}
static CharSequence makeLabel(PackageManager pm,
String className, PackageItemInfo item) {
if (item != null && (item.labelRes != 0
|| item.nonLocalizedLabel != null)) {
CharSequence label = item.loadLabel(pm);
if (label != null) {
return label;
}
}
String label = className;
int tail = label.lastIndexOf('.');
if (tail >= 0) {
label = label.substring(tail+1, label.length());
}
return label;
}
boolean update(Context context, ActivityManager am) {
final PackageManager pm = context.getPackageManager();
mSequence++;
boolean changed = false;
List<ActivityManager.RunningServiceInfo> services
= am.getRunningServices(RunningServices.MAX_SERVICES);
final int NS = services != null ? services.size() : 0;
for (int i=0; i<NS; i++) {
ActivityManager.RunningServiceInfo si = services.get(i);
// We are not interested in services that have not been started
// and don't have a known client, because
// there is nothing the user can do about them.
if (!si.started && si.clientLabel == 0) {
continue;
}
// We likewise don't care about services running in a
// persistent process like the system or phone.
if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
!= 0) {
continue;
}
HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
if (procs == null) {
procs = new HashMap<String, ProcessItem>();
mProcesses.put(si.uid, procs);
}
ProcessItem proc = procs.get(si.process);
if (proc == null) {
changed = true;
proc = new ProcessItem(context, si.uid, si.process);
procs.put(si.process, proc);
}
if (proc.mCurSeq != mSequence) {
int pid = si.restarting == 0 ? si.pid : 0;
if (pid != proc.mPid) {
changed = true;
if (proc.mPid != pid) {
if (proc.mPid != 0) {
mActiveProcesses.remove(proc.mPid);
}
if (pid != 0) {
mActiveProcesses.put(pid, proc);
}
proc.mPid = pid;
}
}
proc.mDependentProcesses.clear();
proc.mCurSeq = mSequence;
}
changed |= proc.updateService(context, si);
}
// Now update the map of other processes that are running (but
// don't have services actively running inside them).
List<ActivityManager.RunningAppProcessInfo> processes
= am.getRunningAppProcesses();
final int NP = processes != null ? processes.size() : 0;
for (int i=0; i<NP; i++) {
ActivityManager.RunningAppProcessInfo pi = processes.get(i);
ProcessItem proc = mActiveProcesses.get(pi.pid);
if (proc == null) {
// This process is not one that is a direct container
// of a service, so look for it in the secondary
// running list.
proc = mRunningProcesses.get(pi.pid);
if (proc == null) {
proc = new ProcessItem(context, pi.uid, pi.processName);
proc.mPid = pi.pid;
mRunningProcesses.put(pi.pid, proc);
}
proc.mDependentProcesses.clear();
}
proc.mRunningSeq = mSequence;
proc.mRunningProcessInfo = pi;
}
// Build the chains from client processes to the process they are
// dependent on; also remove any old running processes.
int NRP = mRunningProcesses.size();
for (int i=0; i<NRP; i++) {
ProcessItem proc = mRunningProcesses.valueAt(i);
if (proc.mRunningSeq == mSequence) {
int clientPid = proc.mRunningProcessInfo.importanceReasonPid;
if (clientPid != 0) {
ProcessItem client = mActiveProcesses.get(clientPid);
if (client == null) {
client = mRunningProcesses.get(clientPid);
}
if (client != null) {
client.mDependentProcesses.put(proc.mPid, proc);
}
} else {
// In this pass the process doesn't have a client.
// Clear to make sure if it later gets the same one
// that we will detect the change.
proc.mClient = null;
}
} else {
mRunningProcesses.remove(mRunningProcesses.keyAt(i));
}
}
// Follow the tree from all primary service processes to all
// processes they are dependent on, marking these processes as
// still being active and determining if anything has changed.
final int NAP = mActiveProcesses.size();
for (int i=0; i<NAP; i++) {
ProcessItem proc = mActiveProcesses.valueAt(i);
if (proc.mCurSeq == mSequence) {
changed |= proc.buildDependencyChain(context, pm, mSequence);
}
}
// Look for services and their primary processes that no longer exist...
for (int i=0; i<mProcesses.size(); i++) {
HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
Iterator<ProcessItem> pit = procs.values().iterator();
while (pit.hasNext()) {
ProcessItem pi = pit.next();
if (pi.mCurSeq == mSequence) {
pi.ensureLabel(pm);
if (pi.mPid == 0) {
// Sanity: a non-process can't be dependent on
// anything.
pi.mDependentProcesses.clear();
}
} else {
changed = true;
pit.remove();
if (procs.size() == 0) {
mProcesses.remove(mProcesses.keyAt(i));
}
if (pi.mPid != 0) {
mActiveProcesses.remove(pi.mPid);
}
continue;
}
Iterator<ServiceItem> sit = pi.mServices.values().iterator();
while (sit.hasNext()) {
ServiceItem si = sit.next();
if (si.mCurSeq != mSequence) {
changed = true;
sit.remove();
}
}
}
}
if (changed) {
// First determine an order for the services.
ArrayList<ProcessItem> sortedProcesses = new ArrayList<ProcessItem>();
for (int i=0; i<mProcesses.size(); i++) {
for (ProcessItem pi : mProcesses.valueAt(i).values()) {
pi.mIsSystem = false;
pi.mIsStarted = true;
pi.mActiveSince = Long.MAX_VALUE;
for (ServiceItem si : pi.mServices.values()) {
if (si.mServiceInfo != null
&& (si.mServiceInfo.applicationInfo.flags
& ApplicationInfo.FLAG_SYSTEM) != 0) {
pi.mIsSystem = true;
}
if (si.mRunningService != null
&& si.mRunningService.clientLabel != 0) {
pi.mIsStarted = false;
if (pi.mActiveSince > si.mRunningService.activeSince) {
pi.mActiveSince = si.mRunningService.activeSince;
}
}
}
sortedProcesses.add(pi);
}
}
Collections.sort(sortedProcesses, mServiceProcessComparator);
ArrayList<BaseItem> newItems = new ArrayList<BaseItem>();
ArrayList<MergedItem> newMergedItems = new ArrayList<MergedItem>();
mProcessItems.clear();
for (int i=0; i<sortedProcesses.size(); i++) {
ProcessItem pi = sortedProcesses.get(i);
pi.mNeedDivider = false;
int firstProc = mProcessItems.size();
// First add processes we are dependent on.
pi.addDependentProcesses(newItems, mProcessItems);
// And add the process itself.
newItems.add(pi);
if (pi.mPid > 0) {
mProcessItems.add(pi);
}
// Now add the services running in it.
MergedItem mergedItem = null;
boolean haveAllMerged = false;
boolean needDivider = false;
for (ServiceItem si : pi.mServices.values()) {
si.mNeedDivider = needDivider;
needDivider = true;
newItems.add(si);
if (si.mMergedItem != null) {
if (mergedItem != null && mergedItem != si.mMergedItem) {
haveAllMerged = false;
}
mergedItem = si.mMergedItem;
} else {
haveAllMerged = false;
}
}
if (!haveAllMerged || mergedItem == null
|| mergedItem.mServices.size() != pi.mServices.size()) {
// Whoops, we need to build a new MergedItem!
mergedItem = new MergedItem();
for (ServiceItem si : pi.mServices.values()) {
mergedItem.mServices.add(si);
si.mMergedItem = mergedItem;
}
mergedItem.mProcess = pi;
mergedItem.mOtherProcesses.clear();
for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) {
mergedItem.mOtherProcesses.add(mProcessItems.get(mpi));
}
}
mergedItem.update(context);
newMergedItems.add(mergedItem);
}
synchronized (mLock) {
mItems = newItems;
mMergedItems = newMergedItems;
}
}
// Count number of interesting other (non-active) processes, and
// build a list of all processes we will retrieve memory for.
mAllProcessItems.clear();
mAllProcessItems.addAll(mProcessItems);
int numBackgroundProcesses = 0;
int numForegroundProcesses = 0;
int numServiceProcesses = 0;
NRP = mRunningProcesses.size();
for (int i=0; i<NRP; i++) {
ProcessItem proc = mRunningProcesses.valueAt(i);
if (proc.mCurSeq != mSequence) {
// We didn't hit this process as a dependency on one
// of our active ones, so add it up if needed.
if (proc.mRunningProcessInfo.importance >=
ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
numBackgroundProcesses++;
mAllProcessItems.add(proc);
} else if (proc.mRunningProcessInfo.importance <=
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
numForegroundProcesses++;
mAllProcessItems.add(proc);
} else {
Log.i(RunningServices.TAG, "Unknown non-service process: "
+ proc.mProcessName + " #" + proc.mPid);
}
} else {
numServiceProcesses++;
}
}
long backgroundProcessMemory = 0;
long foregroundProcessMemory = 0;
long serviceProcessMemory = 0;
try {
final int numProc = mAllProcessItems.size();
int[] pids = new int[numProc];
for (int i=0; i<numProc; i++) {
pids[i] = mAllProcessItems.get(i).mPid;
}
Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault()
.getProcessMemoryInfo(pids);
for (int i=pids.length-1; i>=0; i--) {
ProcessItem proc = mAllProcessItems.get(i);
changed |= proc.updateSize(context, mem[i], mSequence);
if (proc.mCurSeq == mSequence) {
serviceProcessMemory += proc.mSize;
} else if (proc.mRunningProcessInfo.importance >=
ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
backgroundProcessMemory += proc.mSize;
} else if (proc.mRunningProcessInfo.importance <=
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
foregroundProcessMemory += proc.mSize;
}
}
} catch (RemoteException e) {
}
for (int i=0; i<mMergedItems.size(); i++) {
mMergedItems.get(i).updateSize(context);
}
synchronized (mLock) {
mNumBackgroundProcesses = numBackgroundProcesses;
mNumForegroundProcesses = numForegroundProcesses;
mNumServiceProcesses = numServiceProcesses;
mBackgroundProcessMemory = backgroundProcessMemory;
mForegroundProcessMemory = foregroundProcessMemory;
mServiceProcessMemory = serviceProcessMemory;
}
return changed;
}
ArrayList<BaseItem> getCurrentItems() {
synchronized (mLock) {
return mItems;
}
}
ArrayList<MergedItem> getCurrentMergedItems() {
synchronized (mLock) {
return mMergedItems;
}
}
}

View File

@@ -40,9 +40,9 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.settings.InstalledAppDetails;
import com.android.settings.ManageApplications;
import com.android.settings.R;
import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.applications.ManageApplications;
public class PowerUsageDetail extends Activity implements Button.OnClickListener {
@@ -248,9 +248,9 @@ public class PowerUsageDetail extends Activity implements Button.OnClickListener
startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
break;
case ACTION_APP_DETAILS:
Intent intent = new Intent(Intent.ACTION_VIEW);
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.fromParts("package", mPackages[0], null));
intent.setClass(this, InstalledAppDetails.class);
intent.putExtra(ManageApplications.APP_PKG_NAME, mPackages[0]);
startActivity(intent);
break;
case ACTION_SECURITY_SETTINGS: