summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/Activity.java20
-rw-r--r--core/java/android/app/ActivityManager.java34
-rw-r--r--core/java/android/app/ActivityManagerNative.java39
-rw-r--r--core/java/android/app/ActivityThread.java38
-rw-r--r--core/java/android/app/IActivityManager.java4
-rw-r--r--core/java/android/app/IAppTask.aidl5
-rw-r--r--core/java/android/app/Instrumentation.java34
-rw-r--r--core/java/android/provider/Settings.java3
-rw-r--r--core/java/com/android/internal/os/BinderInternal.java31
9 files changed, 195 insertions, 13 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2e66a4c..9b7cc1c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4704,6 +4704,26 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Ask that the local app instance of this activity be released to free up its memory.
+ * This is asking for the activity to be destroyed, but does <b>not</b> finish the activity --
+ * a new instance of the activity will later be re-created if needed due to the user
+ * navigating back to it.
+ *
+ * @return Returns true if the activity was in a state that it has started the process
+ * of destroying its current instance; returns false if for any reason this could not
+ * be done: it is currently visible to the user, it is already being destroyed, it is
+ * being finished, it hasn't yet saved its state, etc.
+ */
+ public boolean releaseInstance() {
+ try {
+ return ActivityManagerNative.getDefault().releaseActivityInstance(mToken);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ return false;
+ }
+
+ /**
* Called when an activity you launched exits, giving you the requestCode
* you started it with, the resultCode it returned, and any additional
* data from it. The <var>resultCode</var> will be
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2667bcd..58790f6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2617,6 +2617,40 @@ public class ActivityManager {
}
/**
+ * Bring this task to the foreground. If it contains activities, they will be
+ * brought to the foreground with it and their instances re-created if needed.
+ * If it doesn't contain activities, the root activity of the task will be
+ * re-launched.
+ */
+ public void moveToFront() {
+ try {
+ mAppTaskImpl.moveToFront();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invalid AppTask", e);
+ }
+ }
+
+ /**
+ * Start an activity in this task. Brings the task to the foreground. If this task
+ * is not currently active (that is, its id < 0), then the activity being started
+ * needs to be started as a new task and the Intent's ComponentName must match the
+ * base ComponenentName of the recent task entry. Otherwise, the activity being
+ * started must <b>not</b> be launched as a new task -- not through explicit intent
+ * flags nor implicitly as the singleTask or singleInstance launch modes.
+ *
+ * <p>See {@link Activity#startActivity(android.content.Intent, android.os.Bundle)
+ * Activity.startActivity} for more information.</p>
+ *
+ * @param intent The Intent describing the new activity to be launched on the task.
+ * @param options Optional launch options.
+ */
+ public void startActivity(Context context, Intent intent, Bundle options) {
+ ActivityThread thread = ActivityThread.currentActivityThread();
+ thread.getInstrumentation().execStartActivityFromAppTask(context,
+ thread.getApplicationThread(), mAppTaskImpl, intent, options);
+ }
+
+ /**
* Modify the {@link Intent#FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS} flag in the root
* Intent of this AppTask.
*
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 82af99b..36e8892 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -365,6 +365,23 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case RELEASE_ACTIVITY_INSTANCE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ boolean res = releaseActivityInstance(token);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+ case RELEASE_SOME_ACTIVITIES_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IApplicationThread app = ApplicationThreadNative.asInterface(data.readStrongBinder());
+ releaseSomeActivities(app);
+ reply.writeNoException();
+ return true;
+ }
+
case WILL_ACTIVITY_BE_VISIBLE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -2661,6 +2678,28 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
+ public boolean releaseActivityInstance(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(RELEASE_ACTIVITY_INSTANCE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ public void releaseSomeActivities(IApplicationThread app) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(app.asBinder());
+ mRemote.transact(RELEASE_SOME_ACTIVITIES_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
public boolean willActivityBeVisible(IBinder token) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2136209..0356093 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -102,8 +102,6 @@ import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
import com.google.android.collect.Lists;
-import dalvik.system.VMRuntime;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -201,6 +199,7 @@ public final class ActivityThread {
String mInstrumentedLibDir = null;
boolean mSystemThread = false;
boolean mJitEnabled = false;
+ boolean mSomeActivitiesChanged = false;
// These can be accessed by multiple threads; mPackages is the lock.
// XXX For now we keep around information about all packages we have
@@ -2353,6 +2352,7 @@ public final class ActivityThread {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
+ mSomeActivitiesChanged = true;
if (r.profileFd != null) {
mProfiler.setProfiler(r.profileFile, r.profileFd);
@@ -2495,6 +2495,7 @@ public final class ActivityThread {
public void handleCancelVisibleBehind(IBinder token) {
ActivityClientRecord r = mActivities.get(token);
if (r != null) {
+ mSomeActivitiesChanged = true;
final Activity activity = r.activity;
if (activity.mVisibleBehind) {
activity.mCalled = false;
@@ -2984,6 +2985,7 @@ public final class ActivityThread {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
+ mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
@@ -3175,6 +3177,7 @@ public final class ActivityThread {
ActivityManagerNative.getDefault().activityPaused(token, r.persistentState);
} catch (RemoteException ex) {
}
+ mSomeActivitiesChanged = true;
}
}
@@ -3413,6 +3416,7 @@ public final class ActivityThread {
info.state = r.state;
info.persistentState = r.persistentState;
mH.post(info);
+ mSomeActivitiesChanged = true;
}
final void performRestartActivity(IBinder token) {
@@ -3446,6 +3450,7 @@ public final class ActivityThread {
TAG, "Handle window " + r + " visibility: " + show);
updateVisibility(r, show);
}
+ mSomeActivitiesChanged = true;
}
private void handleSleeping(IBinder token, boolean sleeping) {
@@ -3743,6 +3748,7 @@ public final class ActivityThread {
// If the system process has died, it's game over for everyone.
}
}
+ mSomeActivitiesChanged = true;
}
public final void requestRelaunchActivity(IBinder token,
@@ -3805,6 +3811,7 @@ public final class ActivityThread {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
+ mSomeActivitiesChanged = true;
Configuration changedConfig = null;
int configChanges = 0;
@@ -4107,6 +4114,8 @@ public final class ActivityThread {
performConfigurationChanged(r.activity, mCompatConfiguration);
freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(mCompatConfiguration));
+
+ mSomeActivitiesChanged = true;
}
final void handleProfilerControl(boolean start, ProfilerControlData pcd, int profileType) {
@@ -5045,17 +5054,38 @@ public final class ActivityThread {
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
- IActivityManager mgr = ActivityManagerNative.getDefault();
+ final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
+ // Watch for getting close to heap limit.
+ BinderInternal.addGcWatcher(new Runnable() {
+ @Override public void run() {
+ if (!mSomeActivitiesChanged) {
+ return;
+ }
+ Runtime runtime = Runtime.getRuntime();
+ long dalvikMax = runtime.maxMemory();
+ long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
+ if (dalvikUsed > ((3*dalvikMax)/4)) {
+ if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
+ + " total=" + (runtime.totalMemory()/1024)
+ + " used=" + (dalvikUsed/1024));
+ mSomeActivitiesChanged = false;
+ try {
+ mgr.releaseSomeActivities(mAppThread);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ });
} else {
// Don't set application object here -- if the system crashes,
// we can't display an alert, we just want to die die die.
android.ddm.DdmHandleAppName.setAppName("system_process",
- UserHandle.myUserId());
+ UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 69e1710..57c4b71 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -97,6 +97,8 @@ public interface IActivityManager extends IInterface {
public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
public boolean finishActivityAffinity(IBinder token) throws RemoteException;
public void finishVoiceTask(IVoiceInteractionSession session) throws RemoteException;
+ public boolean releaseActivityInstance(IBinder token) throws RemoteException;
+ public void releaseSomeActivities(IApplicationThread app) throws RemoteException;
public boolean willActivityBeVisible(IBinder token) throws RemoteException;
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter,
@@ -771,4 +773,6 @@ public interface IActivityManager extends IInterface {
int START_ACTIVITY_AS_CALLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+232;
int ADD_APP_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+233;
int GET_APP_TASK_THUMBNAIL_SIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+234;
+ int RELEASE_ACTIVITY_INSTANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+235;
+ int RELEASE_SOME_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+236;
}
diff --git a/core/java/android/app/IAppTask.aidl b/core/java/android/app/IAppTask.aidl
index 4e38c36..37fead9 100644
--- a/core/java/android/app/IAppTask.aidl
+++ b/core/java/android/app/IAppTask.aidl
@@ -17,10 +17,15 @@
package android.app;
import android.app.ActivityManager;
+import android.content.Intent;
+import android.os.Bundle;
/** @hide */
interface IAppTask {
void finishAndRemoveTask();
ActivityManager.RecentTaskInfo getTaskInfo();
+ void moveToFront();
+ int startActivity(IBinder whoThread, String callingPackage,
+ in Intent intent, String resolvedType, in Bundle options);
void setExcludeFromRecents(boolean exclude);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b28d7cc..bc71bad 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1702,6 +1702,40 @@ public class Instrumentation {
return null;
}
+ /**
+ * Special version!
+ * @hide
+ */
+ public void execStartActivityFromAppTask(
+ Context who, IBinder contextThread, IAppTask appTask,
+ Intent intent, Bundle options) {
+ IApplicationThread whoThread = (IApplicationThread) contextThread;
+ if (mActivityMonitors != null) {
+ synchronized (mSync) {
+ final int N = mActivityMonitors.size();
+ for (int i=0; i<N; i++) {
+ final ActivityMonitor am = mActivityMonitors.get(i);
+ if (am.match(who, null, intent)) {
+ am.mHits++;
+ if (am.isBlocking()) {
+ return;
+ }
+ break;
+ }
+ }
+ }
+ }
+ try {
+ intent.migrateExtraStreamToClipData();
+ intent.prepareToLeaveProcess();
+ int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
+ intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options);
+ checkStartActivityResult(result, intent);
+ } catch (RemoteException e) {
+ }
+ return;
+ }
+
/*package*/ final void init(ActivityThread thread,
Context instrContext, Context appContext, ComponentName component,
IInstrumentationWatcher watcher, IUiAutomationConnection uiAutomationConnection) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2241716..95d1351 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6320,8 +6320,7 @@ public final class Settings {
* processes as soon as they are no longer needed. If 0, the normal
* extended lifetime is used.
*/
- public static final String ALWAYS_FINISH_ACTIVITIES =
- "always_finish_activities";
+ public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
/**
* Use Dock audio output for media:
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 3b0f0f4..240d9df 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -21,6 +21,7 @@ import android.os.SystemClock;
import android.util.EventLog;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
/**
* Private and debugging Binder APIs.
@@ -28,19 +29,35 @@ import java.lang.ref.WeakReference;
* @see IBinder
*/
public class BinderInternal {
- static WeakReference<GcWatcher> mGcWatcher
+ static WeakReference<GcWatcher> sGcWatcher
= new WeakReference<GcWatcher>(new GcWatcher());
- static long mLastGcTime;
-
+ static ArrayList<Runnable> sGcWatchers = new ArrayList<>();
+ static Runnable[] sTmpWatchers = new Runnable[1];
+ static long sLastGcTime;
+
static final class GcWatcher {
@Override
protected void finalize() throws Throwable {
handleGc();
- mLastGcTime = SystemClock.uptimeMillis();
- mGcWatcher = new WeakReference<GcWatcher>(new GcWatcher());
+ sLastGcTime = SystemClock.uptimeMillis();
+ synchronized (sGcWatchers) {
+ sTmpWatchers = sGcWatchers.toArray(sTmpWatchers);
+ }
+ for (int i=0; i<sTmpWatchers.length; i++) {
+ if (sTmpWatchers[i] != null) {
+ sTmpWatchers[i].run();
+ }
+ }
+ sGcWatcher = new WeakReference<GcWatcher>(new GcWatcher());
}
}
-
+
+ public static void addGcWatcher(Runnable watcher) {
+ synchronized (sGcWatchers) {
+ sGcWatchers.add(watcher);
+ }
+ }
+
/**
* Add the calling thread to the IPC thread pool. This function does
* not return until the current process is exiting.
@@ -58,7 +75,7 @@ public class BinderInternal {
* SystemClock.uptimeMillis()} of the last garbage collection.
*/
public static long getLastGcTime() {
- return mLastGcTime;
+ return sLastGcTime;
}
/**