Add support for heavy-weight applications.
Only one can be running at a time, their process can not be killed,
and a notification is posted while it is running.
Change-Id: I843015723947e0c934ae63a1aeee139327c0bc01
diff --git a/api/current.xml b/api/current.xml
index e6780be..100f5bc 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -4508,6 +4508,17 @@
visibility="public"
>
</field>
+<field name="heavyWeight"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843456"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="height"
type="int"
transient="false"
@@ -5916,17 +5927,6 @@
visibility="public"
>
</field>
-<field name="kraken_resource_pad65"
- type="int"
- transient="false"
- volatile="false"
- value="16843456"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="kraken_resource_pad7"
type="int"
transient="false"
@@ -21759,6 +21759,17 @@
visibility="public"
>
</field>
+<field name="IMPORTANCE_HEAVY_WEIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="150"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="IMPORTANCE_SERVICE"
type="int"
transient="false"
@@ -44092,6 +44103,17 @@
visibility="public"
>
</method>
+<method name="getTargetPackage"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="readIntentSenderOrNullFromParcel"
return="android.content.IntentSender"
abstract="false"
@@ -46286,6 +46308,17 @@
visibility="public"
>
</field>
+<field name="FLAG_HEAVY_WEIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1048576"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_KILL_AFTER_RESTORE"
type="int"
transient="false"
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c9096cf..793b9d2 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -726,6 +726,12 @@
public static final int IMPORTANCE_FOREGROUND = 100;
/**
+ * Constant for {@link #importance}: this process is running a
+ * heavy-weight application and thus should not be killed.
+ */
+ public static final int IMPORTANCE_HEAVY_WEIGHT = 150;
+
+ /**
* Constant for {@link #importance}: this process is running something
* that is considered to be actively visible to the user.
*/
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f694285..b78d22f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1251,6 +1251,13 @@
reply.writeNoException();
return true;
}
+
+ case FINISH_HEAVY_WEIGHT_APP_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ finishHeavyWeightApp();
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -2758,5 +2765,15 @@
return res;
}
+ public void finishHeavyWeightApp() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(FINISH_HEAVY_WEIGHT_APP_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 31f0a63..cd24fa6 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -303,6 +303,8 @@
public boolean isUserAMonkey() throws RemoteException;
+ public void finishHeavyWeightApp() throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -513,4 +515,5 @@
int WILL_ACTIVITY_BE_VISIBLE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+105;
int START_ACTIVITY_WITH_CONFIG_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+106;
int GET_RUNNING_EXTERNAL_APPLICATIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+107;
+ int FINISH_HEAVY_WEIGHT_APP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+108;
}
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index e182021..007a715 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -16,6 +16,7 @@
package android.content;
+import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.Intent;
import android.content.IIntentSender;
@@ -170,6 +171,25 @@
}
/**
+ * Return the package name of the application that created this
+ * IntentSender, that is the identity under which you will actually be
+ * sending the Intent. The returned string is supplied by the system, so
+ * that an application can not spoof its package.
+ *
+ * @return The package name of the PendingIntent, or null if there is
+ * none associated with it.
+ */
+ public String getTargetPackage() {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getPackageForIntentSender(mTarget);
+ } catch (RemoteException e) {
+ // Should never happen.
+ return null;
+ }
+ }
+
+ /**
* Comparison operator on two IntentSender objects, such that true
* is returned then they both represent the same operation from the
* same package.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 7047113..7901b155 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -273,6 +273,17 @@
public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 1<<19;
/**
+ * Value for {@link #flags}: set to <code>true</code> if the application
+ * has reported that it is heavy-weight, and thus can not participate in
+ * the normal application lifecycle.
+ *
+ * <p>Comes from the
+ * {@link android.R.styleable#AndroidManifestApplication_heavyWeight android:heavyWeight}
+ * attribute of the <application> tag.
+ */
+ public static final int FLAG_HEAVY_WEIGHT = 1<<20;
+
+ /**
* Value for {@link #flags}: this is true if the application has set
* its android:neverEncrypt to true, false otherwise. It is used to specify
* that this package specifically "opts-out" of a secured file system solution,
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ddc124..a5f5acc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1600,6 +1600,18 @@
ai.enabled = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_heavyWeight,
+ false)) {
+ ai.flags |= ApplicationInfo.FLAG_HEAVY_WEIGHT;
+
+ // A heavy-weight application can not be in a custom process.
+ // We can do direct compare because we intern all strings.
+ if (ai.processName != null && ai.processName != ai.packageName) {
+ outError[0] = "Heavy-weight applications can not use custom processes";
+ }
+ }
}
sa.recycle();
@@ -1889,6 +1901,14 @@
sa.recycle();
+ if (receiver && (owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // A heavy-weight application can not have receives in its main process
+ // We can do direct compare because we intern all strings.
+ if (a.info.processName == owner.packageName) {
+ outError[0] = "Heavy-weight applications can not have receivers in main process";
+ }
+ }
+
if (outError[0] != null) {
return null;
}
@@ -2171,6 +2191,15 @@
sa.recycle();
+ if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // A heavy-weight application can not have providers in its main process
+ // We can do direct compare because we intern all strings.
+ if (p.info.processName == owner.packageName) {
+ outError[0] = "Heavy-weight applications can not have providers in main process";
+ return null;
+ }
+ }
+
if (cpname == null) {
outError[0] = "<provider> does not incude authorities attribute";
return null;
@@ -2403,6 +2432,15 @@
sa.recycle();
+ if ((owner.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // A heavy-weight application can not have services in its main process
+ // We can do direct compare because we intern all strings.
+ if (s.info.processName == owner.packageName) {
+ outError[0] = "Heavy-weight applications can not have services in main process";
+ return null;
+ }
+ }
+
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
new file mode 100644
index 0000000..ada7f36
--- /dev/null
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -0,0 +1,156 @@
+/*
+ * 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.internal.app;
+
+import com.android.internal.R;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * This activity is displayed when the system attempts to start an Intent for
+ * which there is more than one matching activity, allowing the user to decide
+ * which to go to. It is not normally used directly by application developers.
+ */
+public class HeavyWeightSwitcherActivity extends Activity {
+ /** The PendingIntent of the new activity being launched. */
+ public static final String KEY_INTENT = "intent";
+ /** Set if the caller is requesting a result. */
+ public static final String KEY_HAS_RESULT = "has_result";
+ /** Package of current heavy-weight app. */
+ public static final String KEY_CUR_APP = "cur_app";
+ /** Task that current heavy-weight activity is running in. */
+ public static final String KEY_CUR_TASK = "cur_task";
+ /** Package of newly requested heavy-weight app. */
+ public static final String KEY_NEW_APP = "new_app";
+
+ IntentSender mStartIntent;
+ boolean mHasResult;
+ String mCurApp;
+ int mCurTask;
+ String mNewApp;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_LEFT_ICON);
+
+ mStartIntent = (IntentSender)getIntent().getParcelableExtra(KEY_INTENT);
+ mHasResult = getIntent().getBooleanExtra(KEY_HAS_RESULT, false);
+ mCurApp = getIntent().getStringExtra(KEY_CUR_APP);
+ mCurTask = getIntent().getIntExtra(KEY_CUR_TASK, 0);
+ mNewApp = getIntent().getStringExtra(KEY_NEW_APP);
+
+ setContentView(com.android.internal.R.layout.heavy_weight_switcher);
+
+ setIconAndText(R.id.old_app_icon, R.id.old_app_action, R.id.old_app_description,
+ mCurApp, R.string.old_app_action, R.string.old_app_description);
+ setIconAndText(R.id.new_app_icon, R.id.new_app_action, R.id.new_app_description,
+ mNewApp, R.string.new_app_action, R.string.new_app_description);
+
+ View button = findViewById((R.id.switch_old));
+ button.setOnClickListener(mSwitchOldListener);
+ button = findViewById((R.id.switch_new));
+ button.setOnClickListener(mSwitchNewListener);
+ button = findViewById((R.id.cancel));
+ button.setOnClickListener(mCancelListener);
+
+ getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
+ android.R.drawable.ic_dialog_alert);
+ }
+
+ void setText(int id, CharSequence text) {
+ ((TextView)findViewById(id)).setText(text);
+ }
+
+ void setDrawable(int id, Drawable dr) {
+ if (dr != null) {
+ ((ImageView)findViewById(id)).setImageDrawable(dr);
+ }
+ }
+
+ void setIconAndText(int iconId, int actionId, int descriptionId,
+ String packageName, int actionStr, int descriptionStr) {
+ CharSequence appName = "";
+ Drawable appIcon = null;
+ if (mCurApp != null) {
+ try {
+ ApplicationInfo info = getPackageManager().getApplicationInfo(
+ packageName, 0);
+ appName = info.loadLabel(getPackageManager());
+ appIcon = info.loadIcon(getPackageManager());
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ setDrawable(iconId, appIcon);
+ setText(actionId, getString(actionStr, appName));
+ setText(descriptionId, getText(descriptionStr));
+ }
+
+ private OnClickListener mSwitchOldListener = new OnClickListener() {
+ public void onClick(View v) {
+ try {
+ ActivityManagerNative.getDefault().moveTaskToFront(mCurTask);
+ } catch (RemoteException e) {
+ }
+ finish();
+ }
+ };
+
+ private OnClickListener mSwitchNewListener = new OnClickListener() {
+ public void onClick(View v) {
+ try {
+ ActivityManagerNative.getDefault().finishHeavyWeightApp();
+ } catch (RemoteException e) {
+ }
+ try {
+ if (mHasResult) {
+ startIntentSenderForResult(mStartIntent, -1, null,
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT,
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0);
+ } else {
+ startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0);
+ }
+ } catch (IntentSender.SendIntentException ex) {
+ Log.w("HeavyWeightSwitcherActivity", "Failure starting", ex);
+ }
+ finish();
+ }
+ };
+
+ private OnClickListener mCancelListener = new OnClickListener() {
+ public void onClick(View v) {
+ finish();
+ }
+ };
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2a2208f..1fcf186 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1250,6 +1250,12 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
+ android:theme="@style/Theme.Dialog"
+ android:label="@string/heavy_weight_switcher_title"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
android:theme="@style/Theme.NoDisplay"
android:excludeFromRecents="true">
diff --git a/core/res/res/layout/heavy_weight_switcher.xml b/core/res/res/layout/heavy_weight_switcher.xml
new file mode 100644
index 0000000..9acf009
--- /dev/null
+++ b/core/res/res/layout/heavy_weight_switcher.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:padding="4dp"
+ android:gravity="center_horizontal"
+ android:layout_width="wrap_content" android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:paddingBottom="8dp"
+ android:text="@string/heavy_weight_switcher_text"/>
+
+ <ImageView android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"
+ android:src="?android:listDivider" />
+
+ <LinearLayout android:id="@+id/switch_old"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:background="@android:drawable/list_selector_background"
+ android:paddingRight="3dip"
+ android:paddingLeft="3dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="14dip"
+ android:gravity="center_vertical"
+ android:focusable="true" >
+
+ <ImageView android:id="@+id/old_app_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"
+ android:duplicateParentState="true" >
+ <TextView android:id="@+id/old_app_action"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:layout_marginBottom="2dip"
+ android:duplicateParentState="true" />
+ <TextView android:id="@+id/old_app_description"
+ android:layout_marginTop="-4dip"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:duplicateParentState="true" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"
+ android:src="?android:listDivider" />
+
+ <LinearLayout android:id="@+id/switch_new"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:background="@android:drawable/list_selector_background"
+ android:paddingRight="3dip"
+ android:paddingLeft="3dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="8dip"
+ android:gravity="center_vertical"
+ android:focusable="true" >
+
+ <ImageView android:id="@+id/new_app_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"
+ android:duplicateParentState="true" >
+ <TextView android:id="@+id/new_app_action"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:layout_marginBottom="2dip"
+ android:duplicateParentState="true" />
+ <TextView android:id="@+id/new_app_description"
+ android:layout_marginTop="-4dip"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:duplicateParentState="true" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"
+ android:src="?android:listDivider" />
+
+ <TextView android:id="@+id/cancel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@android:drawable/list_selector_background"
+ android:paddingRight="6dip"
+ android:paddingLeft="6dip"
+ android:paddingTop="5dip"
+ android:paddingBottom="8dip"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:gravity="center"
+ android:focusable="true"
+ android:text="@string/cancel" />
+
+</LinearLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b4c4811..5ca7b28 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -740,6 +740,15 @@
<attr name="restoreNeedsApplication" />
<attr name="restoreAnyVersion" />
<attr name="neverEncrypt" />
+ <!-- Declare that this is a heavy-weight application. This kind of
+ application is not able to save and restore its state on demand,
+ so can not participate in the normal activity lifecycle. It will
+ not be killed while in the background; the user must explicitly
+ quit it. Only one such app can be running at a time; if the user
+ tries to launch a second heavy-weight app, they will be prompted
+ to quit the first before doing so. While a heavy-weight
+ application is running, the user will be informed of this. -->
+ <attr name="heavyWeight" format="boolean" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5d18e9e..1932771 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1245,8 +1245,9 @@
<public type="attr" name="logo" id="0x010102be" />
<public type="attr" name="xlargeScreens" id="0x010102bf" />
-
+ <public type="attr" name="heavyWeight" id="0x010102c0" />
<public-padding type="attr" name="kraken_resource_pad" end="0x01010300" />
+
<public-padding type="id" name="kraken_resource_pad" end="0x01020040" />
<public-padding type="anim" name="kraken_resource_pad" end="0x010a0020" />
<public-padding type="drawable" name="kraken_resource_pad" end="0x01080100" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 613a9a2..e7f892a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1935,8 +1935,29 @@
<!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
<string name="wait">Wait</string>
+ <!-- Notification text to tell the user that a heavy-weight application is running. -->
+ <string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string>
+
+ <!-- Notification details to tell the user that a heavy-weight application is running. -->
+ <string name="heavy_weight_notification_detail">Select to switch to application</string>
+
+ <!-- Title of dialog prompting whether user wants to switch between heavy-weight apps. -->
+ <string name="heavy_weight_switcher_title">Switch applications?</string>
+
+ <!-- Descriptive text for switching to a new heavy-weight application. -->
+ <string name="heavy_weight_switcher_text">Another application is already running
+ that must be stopped before you can start a new one.</string>
+
+ <string name="old_app_action">Return to <xliff:g id="old_app">%1$s</xliff:g></string>
+ <string name="old_app_description">Don\'t start the new application.</string>
+
+ <string name="new_app_action">Start <xliff:g id="old_app">%1$s</xliff:g></string>
+ <string name="new_app_description">Stop the old application without saving.</string>
+
<!-- Displayed in the title of the chooser for things to do with text that
- is to be sent to another application. For example, I can send text through SMS or IM. A dialog with those choices would be shown, and this would be the title. -->
+ is to be sent to another application. For example, I can send
+ text through SMS or IM. A dialog with those choices would be shown,
+ and this would be the title. -->
<string name="sendText">Select an action for text</string>
<!-- Title of the dialog where the user is adjusting the phone ringer volume -->
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8857c5f..38fda09 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.am;
+import com.android.internal.R;
+import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.AttributeCache;
import com.android.server.IntentResolver;
@@ -39,10 +41,12 @@
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
+import android.app.INotificationManager;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ResultInfo;
import android.app.Service;
@@ -69,6 +73,7 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
@@ -473,7 +478,8 @@
* to become visible before completing whatever operation they are
* supposed to do.
*/
- final ArrayList mWaitingVisibleActivities = new ArrayList();
+ final ArrayList<HistoryRecord> mWaitingVisibleActivities
+ = new ArrayList<HistoryRecord>();
/**
* List of activities that are ready to be stopped, but waiting
@@ -501,7 +507,8 @@
* for the previous activity to settle down before doing so. It contains
* HistoryRecord objects.
*/
- final ArrayList mFinishingActivities = new ArrayList();
+ final ArrayList<HistoryRecord> mFinishingActivities
+ = new ArrayList<HistoryRecord>();
/**
* All of the applications we currently have running organized by name.
@@ -513,6 +520,11 @@
= new ProcessMap<ProcessRecord>();
/**
+ * The currently running heavy-weight process, if any.
+ */
+ ProcessRecord mHeavyWeightProcess = null;
+
+ /**
* The last time that various processes have crashed.
*/
final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>();
@@ -726,21 +738,24 @@
* that a single provider may be published under multiple names, so
* there may be multiple entries here for a single one in mProvidersByClass.
*/
- final HashMap mProvidersByName = new HashMap();
+ final HashMap<String, ContentProviderRecord> mProvidersByName
+ = new HashMap<String, ContentProviderRecord>();
/**
* All of the currently running global content providers. Keys are a
* string containing the provider's implementation class and values are a
* ContentProviderRecord object containing the data about it.
*/
- final HashMap mProvidersByClass = new HashMap();
+ final HashMap<String, ContentProviderRecord> mProvidersByClass
+ = new HashMap<String, ContentProviderRecord>();
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
* removed from this list once it is published.
*/
- final ArrayList mLaunchingProviders = new ArrayList();
+ final ArrayList<ContentProviderRecord> mLaunchingProviders
+ = new ArrayList<ContentProviderRecord>();
/**
* Global set of specific Uri permissions that have been granted.
@@ -1006,6 +1021,8 @@
static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
static final int KILL_APPLICATION_MSG = 22;
static final int FINALIZE_PENDING_INTENT_MSG = 23;
+ static final int POST_HEAVY_NOTIFICATION_MSG = 24;
+ static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25;
AlertDialog mUidAlert;
@@ -1235,6 +1252,62 @@
case FINALIZE_PENDING_INTENT_MSG: {
((PendingIntentRecord)msg.obj).completeFinalize();
} break;
+ case POST_HEAVY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ HistoryRecord root = (HistoryRecord)msg.obj;
+ ProcessRecord process = root.app;
+ if (process == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(process.info.packageName, 0);
+ String text = mContext.getString(R.string.heavy_weight_notification,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_sys_adb; //context.getApplicationInfo().icon;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.tickerText = text;
+ notification.defaults = 0; // please be quiet
+ notification.sound = null;
+ notification.vibrate = null;
+ notification.setLatestEventInfo(context, text,
+ mContext.getText(R.string.heavy_weight_notification_detail),
+ PendingIntent.getActivity(mContext, 0, root.intent,
+ PendingIntent.FLAG_CANCEL_CURRENT));
+
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotification("android", R.string.heavy_weight_notification,
+ notification, outId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for heavy-weight app", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Unable to create context for heavy notification", e);
+ }
+ } break;
+ case CANCEL_HEAVY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotification("android",
+ R.string.heavy_weight_notification);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
}
}
};
@@ -1822,6 +1895,24 @@
System.identityHashCode(r),
r.info, r.icicle, results, newIntents, !andResume,
isNextTransitionForward());
+
+ if ((app.info.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // This may be a heavy-weight process! Note that the package
+ // manager will ensure that only activity can run in the main
+ // process of the .apk, which is the only thing that will be
+ // considered heavy-weight.
+ if (app.processName.equals(app.info.packageName)) {
+ if (mHeavyWeightProcess != null && mHeavyWeightProcess != app) {
+ Log.w(TAG, "Starting new heavy weight process " + app
+ + " when already running " + mHeavyWeightProcess);
+ }
+ mHeavyWeightProcess = app;
+ Message msg = mHandler.obtainMessage(POST_HEAVY_NOTIFICATION_MSG);
+ msg.obj = r;
+ mHandler.sendMessage(msg);
+ }
+ }
+
} catch (RemoteException e) {
if (r.launchFailed) {
// This is the second time we failed -- finish activity
@@ -3678,7 +3769,7 @@
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- final boolean componentSpecified = intent.getComponent() != null;
+ boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
intent = new Intent(intent);
@@ -3728,6 +3819,74 @@
final long origId = Binder.clearCallingIdentity();
+ if (aInfo != null &&
+ (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_HEAVY_WEIGHT) != 0) {
+ // This may be a heavy-weight process! Check to see if we already
+ // have another, different heavy-weight process running.
+ if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
+ if (mHeavyWeightProcess != null &&
+ (mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
+ !mHeavyWeightProcess.processName.equals(aInfo.processName))) {
+ int realCallingPid = callingPid;
+ int realCallingUid = callingUid;
+ if (caller != null) {
+ ProcessRecord callerApp = getRecordForAppLocked(caller);
+ if (callerApp != null) {
+ realCallingPid = callerApp.pid;
+ realCallingUid = callerApp.info.uid;
+ } else {
+ Slog.w(TAG, "Unable to find app for caller " + caller
+ + " (pid=" + realCallingPid + ") when starting: "
+ + intent.toString());
+ return START_PERMISSION_DENIED;
+ }
+ }
+
+ IIntentSender target = getIntentSenderLocked(
+ IActivityManager.INTENT_SENDER_ACTIVITY, "android",
+ realCallingUid, null, null, 0, intent,
+ resolvedType, PendingIntent.FLAG_CANCEL_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT);
+
+ Intent newIntent = new Intent();
+ if (requestCode >= 0) {
+ // Caller is requesting a result.
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
+ new IntentSender(target));
+ if (mHeavyWeightProcess.activities.size() > 0) {
+ HistoryRecord hist = mHeavyWeightProcess.activities.get(0);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
+ hist.packageName);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
+ hist.task.taskId);
+ }
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
+ aInfo.packageName);
+ newIntent.setFlags(intent.getFlags());
+ newIntent.setClassName("android",
+ HeavyWeightSwitcherActivity.class.getName());
+ intent = newIntent;
+ resolvedType = null;
+ caller = null;
+ callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
+ componentSpecified = true;
+ try {
+ ResolveInfo rInfo =
+ ActivityThread.getPackageManager().resolveIntent(
+ intent, null,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | STOCK_PM_FLAGS);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ } catch (RemoteException e) {
+ aInfo = null;
+ }
+ }
+ }
+ }
+
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, callingPid, callingUid,
@@ -4316,6 +4475,40 @@
}
}
+ public final void finishHeavyWeightApp() {
+ if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
+ != PackageManager.PERMISSION_GRANTED) {
+ String msg = "Permission Denial: finishHeavyWeightApp() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + android.Manifest.permission.FORCE_STOP_PACKAGES;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ synchronized(this) {
+ if (mHeavyWeightProcess == null) {
+ return;
+ }
+
+ ArrayList<HistoryRecord> activities = new ArrayList<HistoryRecord>(
+ mHeavyWeightProcess.activities);
+ for (int i=0; i<activities.size(); i++) {
+ HistoryRecord r = activities.get(i);
+ if (!r.finishing) {
+ int index = indexOfTokenLocked(r);
+ if (index >= 0) {
+ finishActivityLocked(r, index, Activity.RESULT_CANCELED,
+ null, "finish-heavy");
+ }
+ }
+ }
+
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
+ }
+
void sendActivityResultLocked(int callingUid, HistoryRecord r,
String resultWho, int requestCode, int resultCode, Intent data) {
@@ -4513,6 +4706,10 @@
if (idx >= 0) {
r.app.activities.remove(idx);
}
+ if (mHeavyWeightProcess == r.app && r.app.activities.size() <= 0) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
if (r.persistent) {
decPersistentCountLocked(r.app);
}
@@ -5375,6 +5572,10 @@
+ "/" + uid + ")");
mProcessNames.remove(name, uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
boolean needRestart = false;
if (app.pid > 0 && app.pid != MY_PID) {
int pid = app.pid;
@@ -5416,6 +5617,10 @@
EventLog.writeEvent(EventLogTags.AM_PROCESS_START_TIMEOUT, pid, app.info.uid,
app.processName);
mProcessNames.remove(app.processName, app.info.uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
// Take care of any launching providers waiting for this process.
checkAppInLaunchingProvidersLocked(app, true);
// Take care of any services that are waiting for the process.
@@ -6101,57 +6306,66 @@
throw new SecurityException(msg);
}
}
+
+ return getIntentSenderLocked(type, packageName, callingUid,
+ token, resultWho, requestCode, intent, resolvedType, flags);
+
} catch (RemoteException e) {
throw new SecurityException(e);
}
- HistoryRecord activity = null;
- if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- int index = indexOfTokenLocked(token);
- if (index < 0) {
- return null;
- }
- activity = (HistoryRecord)mHistory.get(index);
- if (activity.finishing) {
- return null;
- }
+ }
+ }
+
+ IIntentSender getIntentSenderLocked(int type,
+ String packageName, int callingUid, IBinder token, String resultWho,
+ int requestCode, Intent intent, String resolvedType, int flags) {
+ HistoryRecord activity = null;
+ if (type == INTENT_SENDER_ACTIVITY_RESULT) {
+ int index = indexOfTokenLocked(token);
+ if (index < 0) {
+ return null;
}
-
- final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
- final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
- final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
- flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
- |PendingIntent.FLAG_UPDATE_CURRENT);
-
- PendingIntentRecord.Key key = new PendingIntentRecord.Key(
- type, packageName, activity, resultWho,
- requestCode, intent, resolvedType, flags);
- WeakReference<PendingIntentRecord> ref;
- ref = mIntentSenderRecords.get(key);
- PendingIntentRecord rec = ref != null ? ref.get() : null;
- if (rec != null) {
- if (!cancelCurrent) {
- if (updateCurrent) {
- rec.key.requestIntent.replaceExtras(intent);
- }
- return rec;
- }
- rec.canceled = true;
- mIntentSenderRecords.remove(key);
+ activity = (HistoryRecord)mHistory.get(index);
+ if (activity.finishing) {
+ return null;
}
- if (noCreate) {
+ }
+
+ final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
+ final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
+ final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
+ flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
+ |PendingIntent.FLAG_UPDATE_CURRENT);
+
+ PendingIntentRecord.Key key = new PendingIntentRecord.Key(
+ type, packageName, activity, resultWho,
+ requestCode, intent, resolvedType, flags);
+ WeakReference<PendingIntentRecord> ref;
+ ref = mIntentSenderRecords.get(key);
+ PendingIntentRecord rec = ref != null ? ref.get() : null;
+ if (rec != null) {
+ if (!cancelCurrent) {
+ if (updateCurrent) {
+ rec.key.requestIntent.replaceExtras(intent);
+ }
return rec;
}
- rec = new PendingIntentRecord(this, key, callingUid);
- mIntentSenderRecords.put(key, rec.ref);
- if (type == INTENT_SENDER_ACTIVITY_RESULT) {
- if (activity.pendingResults == null) {
- activity.pendingResults
- = new HashSet<WeakReference<PendingIntentRecord>>();
- }
- activity.pendingResults.add(rec.ref);
- }
+ rec.canceled = true;
+ mIntentSenderRecords.remove(key);
+ }
+ if (noCreate) {
return rec;
}
+ rec = new PendingIntentRecord(this, key, callingUid);
+ mIntentSenderRecords.put(key, rec.ref);
+ if (type == INTENT_SENDER_ACTIVITY_RESULT) {
+ if (activity.pendingResults == null) {
+ activity.pendingResults
+ = new HashSet<WeakReference<PendingIntentRecord>>();
+ }
+ activity.pendingResults.add(rec.ref);
+ }
+ return rec;
}
public void cancelIntentSender(IIntentSender sender) {
@@ -6450,8 +6664,7 @@
String name = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6648,8 +6861,7 @@
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProvidersByName.get(authority);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6743,8 +6955,7 @@
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr
- = (ContentProviderRecord)mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProvidersByName.get(authority);
if (cpr != null) {
pi = cpr.info;
} else {
@@ -7752,8 +7963,7 @@
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
- ContentProviderRecord cpr =
- (ContentProviderRecord)mProvidersByClass.get(cpi.name);
+ ContentProviderRecord cpr = mProvidersByClass.get(cpi.name);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info);
mProvidersByClass.put(cpi.name, cpr);
@@ -7828,7 +8038,7 @@
}
// First check if this content provider has been published...
- cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ cpr = mProvidersByName.get(name);
if (cpr != null) {
cpi = cpr.info;
if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
@@ -7909,7 +8119,7 @@
"Attempt to launch content provider before system ready");
}
- cpr = (ContentProviderRecord)mProvidersByClass.get(cpi.name);
+ cpr = mProvidersByClass.get(cpi.name);
final boolean firstClass = cpr == null;
if (firstClass) {
try {
@@ -8043,7 +8253,7 @@
*/
public void removeContentProvider(IApplicationThread caller, String name) {
synchronized (this) {
- ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if(cpr == null) {
// remove from mProvidersByClass
if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -8057,8 +8267,7 @@
" when removing content provider " + name);
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord)
- mProvidersByClass.get(cpr.info.name);
+ ContentProviderRecord localCpr = mProvidersByClass.get(cpr.info.name);
if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+ r.info.processName + " from process "
+ localCpr.appInfo.processName);
@@ -8082,7 +8291,7 @@
private void removeContentProviderExternal(String name) {
synchronized (this) {
- ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProvidersByName.get(name);
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -8090,7 +8299,7 @@
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord) mProvidersByClass.get(cpr.info.name);
+ ContentProviderRecord localCpr = mProvidersByClass.get(cpr.info.name);
localCpr.externals--;
if (localCpr.externals < 0) {
Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -8122,8 +8331,7 @@
if (src == null || src.info == null || src.provider == null) {
continue;
}
- ContentProviderRecord dst =
- (ContentProviderRecord)r.pubProviders.get(src.info.name);
+ ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
mProvidersByClass.put(dst.info.name, dst);
String names[] = dst.info.authority.split(";");
@@ -9038,9 +9246,9 @@
if (app.services.size() != 0) {
// Any services running in the application need to be placed
// back in the pending list.
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord sr = (ServiceRecord)it.next();
+ ServiceRecord sr = it.next();
sr.crashCount++;
}
}
@@ -9485,6 +9693,8 @@
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
} else if (adj >= VISIBLE_APP_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+ } else if (app == mHeavyWeightProcess) {
+ currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_HEAVY_WEIGHT;
} else {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
}
@@ -9860,6 +10070,9 @@
pw.println(" ");
pw.println(" mHomeProcess: " + mHomeProcess);
+ if (mHeavyWeightProcess != null) {
+ pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess);
+ }
pw.println(" mConfiguration: " + mConfiguration);
pw.println(" mConfigWillChange: " + mConfigWillChange);
pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown);
@@ -10121,10 +10334,11 @@
if (mProvidersByClass.size() > 0) {
if (needSep) pw.println(" ");
pw.println(" Published content providers (by class):");
- Iterator it = mProvidersByClass.entrySet().iterator();
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByClass.entrySet().iterator();
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
pw.print(" * "); pw.println(r);
r.dump(pw, " ");
}
@@ -10134,10 +10348,11 @@
if (mProvidersByName.size() > 0) {
pw.println(" ");
pw.println(" Authority to provider mappings:");
- Iterator it = mProvidersByName.entrySet().iterator();
+ Iterator<Map.Entry<String, ContentProviderRecord>> it
+ = mProvidersByName.entrySet().iterator();
while (it.hasNext()) {
- Map.Entry e = (Map.Entry)it.next();
- ContentProviderRecord r = (ContentProviderRecord)e.getValue();
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
pw.print(" "); pw.print(e.getKey()); pw.print(": ");
pw.println(r);
}
@@ -10370,9 +10585,9 @@
// XXX we are letting the client link to the service for
// death notifications.
if (app.services.size() > 0) {
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord r = (ServiceRecord)it.next();
+ ServiceRecord r = it.next();
if (r.connections.size() > 0) {
Iterator<ConnectionRecord> jt
= r.connections.values().iterator();
@@ -10407,9 +10622,9 @@
if (app.services.size() != 0) {
// Any services running in the application need to be placed
// back in the pending list.
- Iterator it = app.services.iterator();
+ Iterator<ServiceRecord> it = app.services.iterator();
while (it.hasNext()) {
- ServiceRecord sr = (ServiceRecord)it.next();
+ ServiceRecord sr = it.next();
synchronized (sr.stats.getBatteryStats()) {
sr.stats.stopLaunchedLocked();
}
@@ -10548,9 +10763,9 @@
// Remove published content providers.
if (!app.pubProviders.isEmpty()) {
- Iterator it = app.pubProviders.values().iterator();
+ Iterator<ContentProviderRecord> it = app.pubProviders.values().iterator();
while (it.hasNext()) {
- ContentProviderRecord cpr = (ContentProviderRecord)it.next();
+ ContentProviderRecord cpr = it.next();
cpr.provider = null;
cpr.app = null;
@@ -10642,6 +10857,10 @@
if (DEBUG_PROCESSES) Slog.v(TAG,
"Removing non-persistent process during cleanup: " + app);
mProcessNames.remove(app.processName, app.info.uid);
+ if (mHeavyWeightProcess == app) {
+ mHeavyWeightProcess = null;
+ mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
+ }
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
// If it is not already on the pending app list, add it there
@@ -10683,8 +10902,7 @@
int NL = mLaunchingProviders.size();
boolean restart = false;
for (int i=0; i<NL; i++) {
- ContentProviderRecord cpr = (ContentProviderRecord)
- mLaunchingProviders.get(i);
+ ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.launchingApp == app) {
if (!alwaysBad && !app.bad) {
restart = true;
@@ -11601,7 +11819,7 @@
public void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
boolean anyForeground = false;
- for (ServiceRecord sr : (HashSet<ServiceRecord>)proc.services) {
+ for (ServiceRecord sr : proc.services) {
if (sr.isForeground) {
anyForeground = true;
break;
@@ -13824,6 +14042,11 @@
adj = FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "instrumentation";
+ } else if (app == mHeavyWeightProcess) {
+ // We don't want to kill the current heavy-weight process.
+ adj = FOREGROUND_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.adjType = "heavy";
} else if (app.persistentActivities > 0) {
// Special persistent activities... shouldn't be used these days.
adj = FOREGROUND_APP_ADJ;
@@ -13867,7 +14090,7 @@
app.adjType = "bg-activities";
N = app.activities.size();
for (int j=0; j<N; j++) {
- if (((HistoryRecord)app.activities.get(j)).visible) {
+ if (app.activities.get(j).visible) {
// This app has a visible activity!
app.hidden = false;
adj = VISIBLE_APP_ADJ;
@@ -13910,9 +14133,9 @@
final long now = SystemClock.uptimeMillis();
// This process is more important if the top activity is
// bound to the service.
- Iterator jt = app.services.iterator();
+ Iterator<ServiceRecord> jt = app.services.iterator();
while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) {
- ServiceRecord s = (ServiceRecord)jt.next();
+ ServiceRecord s = jt.next();
if (s.startRequested) {
if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
// This service has seen some activity within
@@ -14007,10 +14230,10 @@
if (app.pubProviders.size() != 0 && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- Iterator jt = app.pubProviders.values().iterator();
+ Iterator<ContentProviderRecord> jt = app.pubProviders.values().iterator();
while (jt.hasNext() && (adj > FOREGROUND_APP_ADJ
|| schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
- ContentProviderRecord cpr = (ContentProviderRecord)jt.next();
+ ContentProviderRecord cpr = jt.next();
if (cpr.clients.size() != 0) {
Iterator<ProcessRecord> kt = cpr.clients.iterator();
while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) {
@@ -14491,7 +14714,7 @@
if (Config.LOGV) Slog.v(
TAG, "Looking to quit " + app.processName);
for (j=0; j<NUMA && canQuit; j++) {
- HistoryRecord r = (HistoryRecord)app.activities.get(j);
+ HistoryRecord r = app.activities.get(j);
if (Config.LOGV) Slog.v(
TAG, " " + r.intent.getComponent().flattenToShortString()
+ ": frozen=" + r.haveState + ", visible=" + r.visible);
@@ -14501,7 +14724,7 @@
if (canQuit) {
// Finish all of the activities, and then the app itself.
for (j=0; j<NUMA; j++) {
- HistoryRecord r = (HistoryRecord)app.activities.get(j);
+ HistoryRecord r = app.activities.get(j);
if (!r.finishing) {
destroyActivityLocked(r, false);
}
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index f49a182..9dda1df 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -28,7 +28,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.PrintWriterPrinter;
import java.io.PrintWriter;
@@ -45,7 +44,7 @@
final ApplicationInfo info; // all about the first app in the process
final String processName; // name of the process
// List of packages running in the process
- final HashSet<String> pkgList = new HashSet();
+ final HashSet<String> pkgList = new HashSet<String>();
IApplicationThread thread; // the actual proc... may be null only if
// 'persistent' is true (in which case we
// are in the process of launching the app)
@@ -87,9 +86,9 @@
Object adjTarget; // Debugging: target component impacting oom_adj.
// contains HistoryRecord objects
- final ArrayList activities = new ArrayList();
+ final ArrayList<HistoryRecord> activities = new ArrayList<HistoryRecord>();
// all ServiceRecord running in this process
- final HashSet services = new HashSet();
+ final HashSet<ServiceRecord> services = new HashSet<ServiceRecord>();
// services that are currently executing code (need to remain foreground).
final HashSet<ServiceRecord> executingServices
= new HashSet<ServiceRecord>();
@@ -99,7 +98,8 @@
// all IIntentReceivers that are registered from this process.
final HashSet<ReceiverList> receivers = new HashSet<ReceiverList>();
// class (String) -> ContentProviderRecord
- final HashMap pubProviders = new HashMap();
+ final HashMap<String, ContentProviderRecord> pubProviders
+ = new HashMap<String, ContentProviderRecord>();
// All ContentProviderRecord process is using
final HashMap<ContentProviderRecord, Integer> conProviders
= new HashMap<ContentProviderRecord, Integer>();
@@ -128,7 +128,6 @@
ComponentName errorReportReceiver;
void dump(PrintWriter pw, String prefix) {
- long now = SystemClock.uptimeMillis();
if (info.className != null) {
pw.print(prefix); pw.print("class="); pw.println(info.className);
}
@@ -249,7 +248,7 @@
public boolean isInterestingToUserLocked() {
final int size = activities.size();
for (int i = 0 ; i < size ; i++) {
- HistoryRecord r = (HistoryRecord) activities.get(i);
+ HistoryRecord r = activities.get(i);
if (r.isInterestingToUserLocked()) {
return true;
}
@@ -261,7 +260,7 @@
int i = activities.size();
while (i > 0) {
i--;
- ((HistoryRecord)activities.get(i)).stopFreezingScreenLocked(true);
+ activities.get(i).stopFreezingScreenLocked(true);
}
}