diff options
author | Dianne Hackborn <hackbod@google.com> | 2010-06-03 18:47:52 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2010-06-04 10:09:13 -0700 |
commit | 860755faa6bdd3c2aeae49c05b87b5bc080ae60c (patch) | |
tree | 49cb794a0de5b8f505048533ac5daf02e95c94b1 | |
parent | f8acea6ccbdd7e7283b1dc439f49c72a937f746d (diff) | |
download | frameworks_base-860755faa6bdd3c2aeae49c05b87b5bc080ae60c.zip frameworks_base-860755faa6bdd3c2aeae49c05b87b5bc080ae60c.tar.gz frameworks_base-860755faa6bdd3c2aeae49c05b87b5bc080ae60c.tar.bz2 |
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
-rw-r--r-- | api/current.xml | 55 | ||||
-rw-r--r-- | core/java/android/app/ActivityManager.java | 6 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 17 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 3 | ||||
-rw-r--r-- | core/java/android/content/IntentSender.java | 20 | ||||
-rw-r--r-- | core/java/android/content/pm/ApplicationInfo.java | 11 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 38 | ||||
-rw-r--r-- | core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java | 156 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 6 | ||||
-rw-r--r-- | core/res/res/layout/heavy_weight_switcher.xml | 145 | ||||
-rw-r--r-- | core/res/res/values/attrs_manifest.xml | 9 | ||||
-rw-r--r-- | core/res/res/values/public.xml | 3 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 23 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 397 | ||||
-rw-r--r-- | services/java/com/android/server/am/ProcessRecord.java | 15 |
15 files changed, 796 insertions, 108 deletions
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 class ActivityManager { 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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ class ActivityManagerProxy implements IActivityManager 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 interface IActivityManager extends IInterface { public boolean isUserAMonkey() throws RemoteException; + public void finishHeavyWeightApp() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -513,4 +515,5 @@ public interface IActivityManager extends IInterface { 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 @@ public class IntentSender implements Parcelable { } /** + * 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 class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class PackageParser { 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 @@ public class PackageParser { 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 @@ public class PackageParser { 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 @@ public class PackageParser { 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.IActivityManager; 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.PathPermission; 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 class ActivityManagerService extends ActivityManagerNative implemen } } + 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + "/" + 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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; } + activity = (HistoryRecord)mHistory.get(index); + if (activity.finishing) { + 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); + 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; + 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); } - 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); - } + 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } // 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen "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 final class ActivityManagerService extends ActivityManagerNative implemen */ 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen " 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } //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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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 @@ public final class ActivityManagerService extends ActivityManagerNative implemen 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.content.pm.ApplicationInfo; 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 @@ class ProcessRecord implements Watchdog.PssRequestor { 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 @@ class ProcessRecord implements Watchdog.PssRequestor { 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 @@ class ProcessRecord implements Watchdog.PssRequestor { // 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 @@ class ProcessRecord implements Watchdog.PssRequestor { 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 @@ class ProcessRecord implements Watchdog.PssRequestor { 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 @@ class ProcessRecord implements Watchdog.PssRequestor { int i = activities.size(); while (i > 0) { i--; - ((HistoryRecord)activities.get(i)).stopFreezingScreenLocked(true); + activities.get(i).stopFreezingScreenLocked(true); } } |