summaryrefslogtreecommitdiffstats
path: root/core/java/android/app
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-11-12 18:45:53 -0800
committerJean-Baptiste Queru <jbq@google.com>2009-11-13 13:53:39 -0800
commit9db3d07b9620b4269ab33f78604a36327e536ce1 (patch)
tree41e294f34b9695187af098cd42167489fb0c8fb0 /core/java/android/app
parent6c63ee4fc4acae4bbbbd2a49e0a68206221f0de0 (diff)
downloadframeworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.zip
frameworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.tar.gz
frameworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.tar.bz2
eclair snapshot
Diffstat (limited to 'core/java/android/app')
-rw-r--r--core/java/android/app/Activity.java254
-rw-r--r--core/java/android/app/ActivityManager.java167
-rw-r--r--core/java/android/app/ActivityManagerNative.java212
-rw-r--r--core/java/android/app/ActivityThread.java406
-rw-r--r--core/java/android/app/ApplicationContext.java290
-rw-r--r--core/java/android/app/ApplicationErrorReport.java8
-rw-r--r--core/java/android/app/ApplicationThreadNative.java71
-rw-r--r--core/java/android/app/BackupAgent.java5
-rw-r--r--core/java/android/app/Dialog.java67
-rw-r--r--core/java/android/app/IActivityManager.java39
-rw-r--r--core/java/android/app/IApplicationThread.java10
-rw-r--r--core/java/android/app/INotificationManager.aidl4
-rw-r--r--core/java/android/app/ISearchManager.aidl10
-rw-r--r--core/java/android/app/IWallpaperManager.aidl (renamed from core/java/android/app/IWallpaperService.aidl)22
-rw-r--r--core/java/android/app/IWallpaperManagerCallback.aidl (renamed from core/java/android/app/IWallpaperServiceCallback.aidl)4
-rw-r--r--core/java/android/app/Instrumentation.java95
-rw-r--r--core/java/android/app/IntentService.java21
-rw-r--r--core/java/android/app/LauncherActivity.java56
-rw-r--r--core/java/android/app/Notification.java11
-rw-r--r--core/java/android/app/NotificationManager.java32
-rw-r--r--core/java/android/app/PendingIntent.java5
-rw-r--r--core/java/android/app/SearchDialog.java290
-rw-r--r--core/java/android/app/SearchManager.java156
-rw-r--r--core/java/android/app/Service.java216
-rw-r--r--core/java/android/app/SuggestionsAdapter.java146
-rw-r--r--core/java/android/app/WallpaperInfo.aidl19
-rw-r--r--core/java/android/app/WallpaperInfo.java277
-rw-r--r--core/java/android/app/WallpaperManager.java709
28 files changed, 2897 insertions, 705 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f2905a7..49ebce3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -24,6 +24,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IIntentSender;
+import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -34,6 +35,7 @@ import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -788,8 +790,8 @@ public class Activity extends ContextThemeWrapper
* @see #onPostCreate
*/
protected void onCreate(Bundle savedInstanceState) {
- mVisibleFromClient = mWindow.getWindowStyle().getBoolean(
- com.android.internal.R.styleable.Window_windowNoDisplay, true);
+ mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
+ com.android.internal.R.styleable.Window_windowNoDisplay, false);
mCalled = true;
}
@@ -1752,8 +1754,17 @@ public class Activity extends ContextThemeWrapper
*
* <p>If the focused view didn't want this event, this method is called.
*
- * <p>The default implementation handles KEYCODE_BACK to stop the activity
- * and go back, and other default key handling if configured with {@link #setDefaultKeyMode}.
+ * <p>The default implementation takes care of {@link KeyEvent#KEYCODE_BACK}
+ * by calling {@link #onBackPressed()}, though the behavior varies based
+ * on the application compatibility mode: for
+ * {@link android.os.Build.VERSION_CODES#ECLAIR} or later applications,
+ * it will set up the dispatch to call {@link #onKeyUp} where the action
+ * will be performed; for earlier applications, it will perform the
+ * action immediately in on-down, as those versions of the platform
+ * behaved.
+ *
+ * <p>Other additional default key handling may be performed
+ * if configured with {@link #setDefaultKeyMode}.
*
* @return Return <code>true</code> to prevent this event from being propagated
* further, or <code>false</code> to indicate that you have not handled
@@ -1762,16 +1773,24 @@ public class Activity extends ContextThemeWrapper
* @see android.view.KeyEvent
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
- finish();
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (getApplicationInfo().targetSdkVersion
+ >= Build.VERSION_CODES.ECLAIR) {
+ event.startTracking();
+ } else {
+ onBackPressed();
+ }
return true;
}
if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
return false;
} else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
- return getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
- keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE);
+ if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
+ keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
+ return true;
+ }
+ return false;
} else {
// Common code for DEFAULT_KEYS_DIALER & DEFAULT_KEYS_SEARCH_*
boolean clearSpannable = false;
@@ -1780,8 +1799,8 @@ public class Activity extends ContextThemeWrapper
clearSpannable = true;
handled = false;
} else {
- handled = TextKeyListener.getInstance().onKeyDown(null, mDefaultKeySsb,
- keyCode, event);
+ handled = TextKeyListener.getInstance().onKeyDown(
+ null, mDefaultKeySsb, keyCode, event);
if (handled && mDefaultKeySsb.length() > 0) {
// something useable has been typed - dispatch it now.
@@ -1813,11 +1832,23 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
+ * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
+ * the event).
+ */
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
* Called when a key was released and not handled by any of the views
* inside of the activity. So, for example, key presses while the cursor
* is inside a TextView will not trigger the event (unless it is a navigation
* to another object) because TextView handles its own key presses.
*
+ * <p>The default implementation handles KEYCODE_BACK to stop the activity
+ * and go back.
+ *
* @return Return <code>true</code> to prevent this event from being propagated
* further, or <code>false</code> to indicate that you have not handled
* this event and it should continue to be propagated.
@@ -1825,6 +1856,14 @@ public class Activity extends ContextThemeWrapper
* @see KeyEvent
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (getApplicationInfo().targetSdkVersion
+ >= Build.VERSION_CODES.ECLAIR) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+ && !event.isCanceled()) {
+ onBackPressed();
+ return true;
+ }
+ }
return false;
}
@@ -1838,6 +1877,15 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Called when the activity has detected the user's press of the back
+ * key. The default implementation simply finishes the current activity,
+ * but you can override this to do whatever you want.
+ */
+ public void onBackPressed() {
+ finish();
+ }
+
+ /**
* Called when a touch screen event was not handled by any of the views
* under it. This is most useful to process touch events that happen
* outside of your window bounds, where there is no view to receive it.
@@ -1909,9 +1957,10 @@ public class Activity extends ContextThemeWrapper
/**
* Called when the current {@link Window} of the activity gains or loses
* focus. This is the best indicator of whether this activity is visible
- * to the user.
+ * to the user. The default implementation clears the key tracking
+ * state, so should always be called.
*
- * <p>Note that this provides information what global focus state, which
+ * <p>Note that this provides information about global focus state, which
* is managed independently of activity lifecycles. As such, while focus
* changes will generally have some relation to lifecycle changes (an
* activity that is stopped will not generally get window focus), you
@@ -1930,11 +1979,32 @@ public class Activity extends ContextThemeWrapper
*
* @see #hasWindowFocus()
* @see #onResume
+ * @see View#onWindowFocusChanged(boolean)
*/
public void onWindowFocusChanged(boolean hasFocus) {
}
/**
+ * Called when the main window associated with the activity has been
+ * attached to the window manager.
+ * See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
+ * for more information.
+ * @see View#onAttachedToWindow
+ */
+ public void onAttachedToWindow() {
+ }
+
+ /**
+ * Called when the main window associated with the activity has been
+ * detached from the window manager.
+ * See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
+ * for more information.
+ * @see View#onDetachedFromWindow
+ */
+ public void onDetachedFromWindow() {
+ }
+
+ /**
* Returns true if this activity's <em>main</em> window currently has window focus.
* Note that this is not the same as the view itself having focus.
*
@@ -1964,10 +2034,14 @@ public class Activity extends ContextThemeWrapper
*/
public boolean dispatchKeyEvent(KeyEvent event) {
onUserInteraction();
- if (getWindow().superDispatchKeyEvent(event)) {
+ Window win = getWindow();
+ if (win.superDispatchKeyEvent(event)) {
return true;
}
- return event.dispatch(this);
+ View decor = mDecor;
+ if (decor == null) decor = win.getDecorView();
+ return event.dispatch(this, decor != null
+ ? decor.getKeyDispatcherState() : null, this);
}
/**
@@ -2394,6 +2468,7 @@ public class Activity extends ContextThemeWrapper
*
* @param id The id of the managed dialog.
*
+ * @see Dialog
* @see #onCreateDialog(int)
* @see #onPrepareDialog(int, Dialog)
* @see #dismissDialog(int)
@@ -2479,16 +2554,17 @@ public class Activity extends ContextThemeWrapper
/**
* This hook is called when the user signals the desire to start a search.
*
- * <p>You can use this function as a simple way to launch the search UI, in response to a
- * menu item, search button, or other widgets within your activity. Unless overidden,
- * calling this function is the same as calling:
- * <p>The default implementation simply calls
- * {@link #startSearch startSearch(null, false, null, false)}, launching a local search.
+ * <p>You can use this function as a simple way to launch the search UI, in response to a
+ * menu item, search button, or other widgets within your activity. Unless overidden,
+ * calling this function is the same as calling
+ * {@link #startSearch startSearch(null, false, null, false)}, which launches
+ * search for the current activity as specified in its manifest, see {@link SearchManager}.
*
* <p>You can override this function to force global search, e.g. in response to a dedicated
* search key, or to block search entirely (by simply returning false).
*
- * @return Returns true if search launched, false if activity blocks it
+ * @return Returns {@code true} if search launched, and {@code false} if activity blocks it.
+ * The default implementation always returns {@code true}.
*
* @see android.app.SearchManager
*/
@@ -2535,6 +2611,21 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Similar to {@link #startSearch}, but actually fires off the search query after invoking
+ * the search dialog. Made available for testing purposes.
+ *
+ * @param query The query to trigger. If empty, the request will be ignored.
+ * @param appSearchData An application can insert application-specific
+ * context here, in order to improve quality or specificity of its own
+ * searches. This data will be returned with SEARCH intent(s). Null if
+ * no extra data is required.
+ */
+ public void triggerSearch(String query, Bundle appSearchData) {
+ ensureSearchManager();
+ mSearchManager.triggerSearch(query, getComponentName(), appSearchData);
+ }
+
+ /**
* Request that key events come to this activity. Use this if your
* activity has no views with focus, but the activity still wants
* a chance to process key events.
@@ -2608,10 +2699,8 @@ public class Activity extends ContextThemeWrapper
}
@Override
- protected void onApplyThemeResource(Resources.Theme theme,
- int resid,
- boolean first)
- {
+ protected void onApplyThemeResource(Resources.Theme theme, int resid,
+ boolean first) {
if (mParent == null) {
super.onApplyThemeResource(theme, resid, first);
} else {
@@ -2682,6 +2771,68 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Like {@link #startActivityForResult(Intent, int)}, but allowing you
+ * to use a IntentSender to describe the activity to be started. If
+ * the IntentSender is for an activity, that activity will be started
+ * as if you had called the regular {@link #startActivityForResult(Intent, int)}
+ * here; otherwise, its associated action will be executed (such as
+ * sending a broadcast) as if you had called
+ * {@link IntentSender#sendIntent IntentSender.sendIntent} on it.
+ *
+ * @param intent The IntentSender to launch.
+ * @param requestCode If >= 0, this code will be returned in
+ * onActivityResult() when the activity exits.
+ * @param fillInIntent If non-null, this will be provided as the
+ * intent parameter to {@link IntentSender#sendIntent}.
+ * @param flagsMask Intent flags in the original IntentSender that you
+ * would like to change.
+ * @param flagsValues Desired values for any bits set in
+ * <var>flagsMask</var>
+ * @param extraFlags Always set to 0.
+ */
+ public void startIntentSenderForResult(IntentSender intent, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
+ throws IntentSender.SendIntentException {
+ if (mParent == null) {
+ startIntentSenderForResultInner(intent, requestCode, fillInIntent,
+ flagsMask, flagsValues, this);
+ } else {
+ mParent.startIntentSenderFromChild(this, intent, requestCode,
+ fillInIntent, flagsMask, flagsValues, extraFlags);
+ }
+ }
+
+ private void startIntentSenderForResultInner(IntentSender intent, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues, Activity activity)
+ throws IntentSender.SendIntentException {
+ try {
+ String resolvedType = null;
+ if (fillInIntent != null) {
+ resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
+ }
+ int result = ActivityManagerNative.getDefault()
+ .startActivityIntentSender(mMainThread.getApplicationThread(), intent,
+ fillInIntent, resolvedType, mToken, activity.mEmbeddedID,
+ requestCode, flagsMask, flagsValues);
+ if (result == IActivityManager.START_CANCELED) {
+ throw new IntentSender.SendIntentException();
+ }
+ Instrumentation.checkStartActivityResult(result, null);
+ } catch (RemoteException e) {
+ }
+ if (requestCode >= 0) {
+ // If this start is requesting a result, we can avoid making
+ // the activity visible until the result is received. Setting
+ // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
+ // activity hidden during this time, to avoid flickering.
+ // This can only be done when a result is requested because
+ // that guarantees we will get information back when the
+ // activity is finished, no matter what happens to it.
+ mStartedActivity = true;
+ }
+ }
+
+ /**
* Launch a new activity. You will not receive any information about when
* the activity exits. This implementation overrides the base version,
* providing information about
@@ -2705,6 +2856,28 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Like {@link #startActivity(Intent)}, but taking a IntentSender
+ * to start; see
+ * {@link #startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}
+ * for more information.
+ *
+ * @param intent The IntentSender to launch.
+ * @param fillInIntent If non-null, this will be provided as the
+ * intent parameter to {@link IntentSender#sendIntent}.
+ * @param flagsMask Intent flags in the original IntentSender that you
+ * would like to change.
+ * @param flagsValues Desired values for any bits set in
+ * <var>flagsMask</var>
+ * @param extraFlags Always set to 0.
+ */
+ public void startIntentSender(IntentSender intent,
+ Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
+ throws IntentSender.SendIntentException {
+ startIntentSenderForResult(intent, -1, fillInIntent, flagsMask,
+ flagsValues, extraFlags);
+ }
+
+ /**
* A special variation to launch an activity only if a new activity
* instance is needed to handle the given Intent. In other words, this is
* just like {@link #startActivityForResult(Intent, int)} except: if you are
@@ -2825,6 +2998,37 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Like {@link #startActivityFromChild(Activity, Intent, int)}, but
+ * taking a IntentSender; see
+ * {@link #startIntentSenderForResult(IntentSender, int, Intent, int, int, int)}
+ * for more information.
+ */
+ public void startIntentSenderFromChild(Activity child, IntentSender intent,
+ int requestCode, Intent fillInIntent, int flagsMask, int flagsValues,
+ int extraFlags)
+ throws IntentSender.SendIntentException {
+ startIntentSenderForResultInner(intent, requestCode, fillInIntent,
+ flagsMask, flagsValues, child);
+ }
+
+ /**
+ * Call immediately after one of the flavors of {@link #startActivity(Intent)}
+ * or {@link #finish} to specify an explicit transition animation to
+ * perform next.
+ * @param enterAnim A resource ID of the animation resource to use for
+ * the incoming activity. Use 0 for no animation.
+ * @param exitAnim A resource ID of the animation resource to use for
+ * the outgoing activity. Use 0 for no animation.
+ */
+ public void overridePendingTransition(int enterAnim, int exitAnim) {
+ try {
+ ActivityManagerNative.getDefault().overridePendingTransition(
+ mToken, getPackageName(), enterAnim, exitAnim);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Call this to set the result that your activity will return to its
* caller.
*
@@ -3255,7 +3459,7 @@ public class Activity extends ContextThemeWrapper
throw new IllegalArgumentException("no ident");
}
}
- mSearchManager.setIdent(ident);
+ mSearchManager.setIdent(ident, getComponentName());
}
@Override
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 07520c9d..d709deb 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -22,10 +22,12 @@ import android.content.Intent;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.graphics.Bitmap;
+import android.os.Debug;
import android.os.RemoteException;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemProperties;
import android.text.TextUtils;
import java.util.List;
@@ -46,6 +48,26 @@ public class ActivityManager {
}
/**
+ * Return the approximate per-application memory class of the current
+ * device. This gives you an idea of how hard a memory limit you should
+ * impose on your application to let the overall system work best. The
+ * returned value is in megabytes; the baseline Android memory class is
+ * 16 (which happens to be the Java heap limit of those devices); some
+ * device with more memory may return 24 or even higher numbers.
+ */
+ public int getMemoryClass() {
+ return staticGetMemoryClass();
+ }
+
+ /** @hide */
+ static public int staticGetMemoryClass() {
+ // Really brain dead right now -- just take this from the configured
+ // vm heap size, and assume it is in megabytes and thus ends with "m".
+ String vmHeapSize = SystemProperties.get("dalvik.vm.heapsize", "16m");
+ return Integer.parseInt(vmHeapSize.substring(0, vmHeapSize.length()-1));
+ }
+
+ /**
* Information you can retrieve about tasks that the user has most recently
* started or visited.
*/
@@ -289,6 +311,11 @@ public class ActivityManager {
public int pid;
/**
+ * The UID that owns this service.
+ */
+ public int uid;
+
+ /**
* The name of the process this service runs in.
*/
public String process;
@@ -299,7 +326,7 @@ public class ActivityManager {
public boolean foreground;
/**
- * The time when the service was first made activity, either by someone
+ * The time when the service was first made active, either by someone
* starting or binding to it.
*/
public long activeSince;
@@ -332,6 +359,48 @@ public class ActivityManager {
*/
public long restarting;
+ /**
+ * Bit for {@link #flags}: set if this service has been
+ * explicitly started.
+ */
+ public static final int FLAG_STARTED = 1<<0;
+
+ /**
+ * Bit for {@link #flags}: set if the service has asked to
+ * run as a foreground process.
+ */
+ public static final int FLAG_FOREGROUND = 1<<1;
+
+ /**
+ * Bit for {@link #flags): set if the service is running in a
+ * core system process.
+ */
+ public static final int FLAG_SYSTEM_PROCESS = 1<<2;
+
+ /**
+ * Bit for {@link #flags): set if the service is running in a
+ * persistent process.
+ */
+ public static final int FLAG_PERSISTENT_PROCESS = 1<<3;
+
+ /**
+ * Running flags.
+ */
+ public int flags;
+
+ /**
+ * For special services that are bound to by system code, this is
+ * the package that holds the binding.
+ */
+ public String clientPackage;
+
+ /**
+ * For special services that are bound to by system code, this is
+ * a string resource providing a user-visible label for who the
+ * client is.
+ */
+ public int clientLabel;
+
public RunningServiceInfo() {
}
@@ -342,6 +411,7 @@ public class ActivityManager {
public void writeToParcel(Parcel dest, int flags) {
ComponentName.writeToParcel(service, dest);
dest.writeInt(pid);
+ dest.writeInt(uid);
dest.writeString(process);
dest.writeInt(foreground ? 1 : 0);
dest.writeLong(activeSince);
@@ -350,11 +420,15 @@ public class ActivityManager {
dest.writeInt(crashCount);
dest.writeLong(lastActivityTime);
dest.writeLong(restarting);
+ dest.writeInt(this.flags);
+ dest.writeString(clientPackage);
+ dest.writeInt(clientLabel);
}
public void readFromParcel(Parcel source) {
service = ComponentName.readFromParcel(source);
pid = source.readInt();
+ uid = source.readInt();
process = source.readString();
foreground = source.readInt() != 0;
activeSince = source.readLong();
@@ -363,6 +437,9 @@ public class ActivityManager {
crashCount = source.readInt();
lastActivityTime = source.readLong();
restarting = source.readLong();
+ flags = source.readInt();
+ clientPackage = source.readString();
+ clientLabel = source.readInt();
}
public static final Creator<RunningServiceInfo> CREATOR = new Creator<RunningServiceInfo>() {
@@ -401,6 +478,22 @@ public class ActivityManager {
}
/**
+ * Returns a PendingIntent you can start to show a control panel for the
+ * given running service. If the service does not have a control panel,
+ * null is returned.
+ */
+ public PendingIntent getRunningServiceControlPanel(ComponentName service)
+ throws SecurityException {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getRunningServiceControlPanel(service);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return null;
+ }
+ }
+
+ /**
* Information you can retrieve about the available memory through
* {@link ActivityManager#getMemoryInfo}.
*/
@@ -613,6 +706,11 @@ public class ActivityManager {
*/
public int pid;
+ /**
+ * The user id of this process.
+ */
+ public int uid;
+
public String pkgList[];
/**
@@ -666,8 +764,51 @@ public class ActivityManager {
*/
public int lru;
+ /**
+ * Constant for {@link #importanceReasonCode}: nothing special has
+ * been specified for the reason for this level.
+ */
+ public static final int REASON_UNKNOWN = 0;
+
+ /**
+ * Constant for {@link #importanceReasonCode}: one of the application's
+ * content providers is being used by another process. The pid of
+ * the client process is in {@link #importanceReasonPid} and the
+ * target provider in this process is in
+ * {@link #importanceReasonComponent}.
+ */
+ public static final int REASON_PROVIDER_IN_USE = 1;
+
+ /**
+ * Constant for {@link #importanceReasonCode}: one of the application's
+ * content providers is being used by another process. The pid of
+ * the client process is in {@link #importanceReasonPid} and the
+ * target provider in this process is in
+ * {@link #importanceReasonComponent}.
+ */
+ public static final int REASON_SERVICE_IN_USE = 2;
+
+ /**
+ * The reason for {@link #importance}, if any.
+ */
+ public int importanceReasonCode;
+
+ /**
+ * For the specified values of {@link #importanceReasonCode}, this
+ * is the process ID of the other process that is a client of this
+ * process. This will be 0 if no other process is using this one.
+ */
+ public int importanceReasonPid;
+
+ /**
+ * For the specified values of {@link #importanceReasonCode}, this
+ * is the name of the component that is being used in this process.
+ */
+ public ComponentName importanceReasonComponent;
+
public RunningAppProcessInfo() {
importance = IMPORTANCE_FOREGROUND;
+ importanceReasonCode = REASON_UNKNOWN;
}
public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
@@ -683,17 +824,25 @@ public class ActivityManager {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(processName);
dest.writeInt(pid);
+ dest.writeInt(uid);
dest.writeStringArray(pkgList);
dest.writeInt(importance);
dest.writeInt(lru);
+ dest.writeInt(importanceReasonCode);
+ dest.writeInt(importanceReasonPid);
+ ComponentName.writeToParcel(importanceReasonComponent, dest);
}
public void readFromParcel(Parcel source) {
processName = source.readString();
pid = source.readInt();
+ uid = source.readInt();
pkgList = source.readStringArray();
importance = source.readInt();
lru = source.readInt();
+ importanceReasonCode = source.readInt();
+ importanceReasonPid = source.readInt();
+ importanceReasonComponent = ComponentName.readFromParcel(source);
}
public static final Creator<RunningAppProcessInfo> CREATOR =
@@ -727,6 +876,22 @@ public class ActivityManager {
}
/**
+ * Return information about the memory usage of one or more processes.
+ *
+ * @param pids The pids of the processes whose memory usage is to be
+ * retrieved.
+ * @return Returns an array of memory information, one for each
+ * requested pid.
+ */
+ public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids) {
+ try {
+ return ActivityManagerNative.getDefault().getProcessMemoryInfo(pids);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Have the system perform a force stop of everything associated with
* the given application package. All processes that share its uid
* will be killed, all services it has running stopped, all activities
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 447512a..3b8aee9 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IIntentSender;
import android.content.IIntentReceiver;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
@@ -29,6 +30,7 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -39,9 +41,6 @@ import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
-import java.io.FileNotFoundException;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -146,6 +145,30 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeInt(result);
return true;
}
+
+ case START_ACTIVITY_INTENT_SENDER_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ IntentSender intent = IntentSender.CREATOR.createFromParcel(data);
+ Intent fillInIntent = null;
+ if (data.readInt() != 0) {
+ fillInIntent = Intent.CREATOR.createFromParcel(data);
+ }
+ String resolvedType = data.readString();
+ IBinder resultTo = data.readStrongBinder();
+ String resultWho = data.readString();
+ int requestCode = data.readInt();
+ int flagsMask = data.readInt();
+ int flagsValues = data.readInt();
+ int result = startActivityIntentSender(app, intent,
+ fillInIntent, resolvedType, resultTo, resultWho,
+ requestCode, flagsMask, flagsValues);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
case START_NEXT_MATCHING_ACTIVITY_TRANSACTION:
{
@@ -292,8 +315,12 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case ACTIVITY_IDLE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
+ Configuration config = null;
+ if (data.readInt() != 0) {
+ config = Configuration.CREATOR.createFromParcel(data);
+ }
if (token != null) {
- activityIdle(token);
+ activityIdle(token, config);
}
reply.writeNoException();
return true;
@@ -512,6 +539,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ComponentName comp = ComponentName.CREATOR.createFromParcel(data);
+ PendingIntent pi = getRunningServiceControlPanel(comp);
+ reply.writeNoException();
+ PendingIntent.writePendingIntentOrNullToParcel(pi, reply);
+ return true;
+ }
+
case START_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -551,8 +587,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
data.enforceInterface(IActivityManager.descriptor);
ComponentName className = ComponentName.readFromParcel(data);
IBinder token = data.readStrongBinder();
- boolean isForeground = data.readInt() != 0;
- setServiceForeground(className, token, isForeground);
+ int id = data.readInt();
+ Notification notification = null;
+ if (data.readInt() != 0) {
+ notification = Notification.CREATOR.createFromParcel(data);
+ }
+ boolean removeNotification = data.readInt() != 0;
+ setServiceForeground(className, token, id, notification, removeNotification);
reply.writeNoException();
return true;
}
@@ -606,7 +647,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case SERVICE_DONE_EXECUTING_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- serviceDoneExecuting(token);
+ int type = data.readInt();
+ int startId = data.readInt();
+ int res = data.readInt();
+ serviceDoneExecuting(token, type, startId, res);
reply.writeNoException();
return true;
}
@@ -935,13 +979,6 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
- case SYSTEM_READY_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- systemReady();
- reply.writeNoException();
- return true;
- }
-
case HANDLE_APPLICATION_ERROR_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder app = data.readStrongBinder();
@@ -1102,6 +1139,34 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
+
+ case GET_PROCESS_MEMORY_INFO_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int[] pids = data.createIntArray();
+ Debug.MemoryInfo[] res = getProcessMemoryInfo(pids);
+ reply.writeNoException();
+ reply.writeTypedArray(res, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ return true;
+ }
+
+ case KILL_APPLICATION_PROCESS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String processName = data.readString();
+ int uid = data.readInt();
+ killApplicationProcess(processName, uid);
+ reply.writeNoException();
+ return true;
+ }
+
+ case OVERRIDE_PENDING_TRANSITION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ String packageName = data.readString();
+ int enterAnim = data.readInt();
+ int exitAnim = data.readInt();
+ overridePendingTransition(token, packageName, enterAnim, exitAnim);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -1152,6 +1217,34 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
return result;
}
+ public int startActivityIntentSender(IApplicationThread caller,
+ IntentSender intent, Intent fillInIntent, String resolvedType,
+ IBinder resultTo, String resultWho, int requestCode,
+ int flagsMask, int flagsValues) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ intent.writeToParcel(data, 0);
+ if (fillInIntent != null) {
+ data.writeInt(1);
+ fillInIntent.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ data.writeString(resolvedType);
+ data.writeStrongBinder(resultTo);
+ data.writeString(resultWho);
+ data.writeInt(requestCode);
+ data.writeInt(flagsMask);
+ data.writeInt(flagsValues);
+ mRemote.transact(START_ACTIVITY_INTENT_SENDER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent) throws RemoteException {
Parcel data = Parcel.obtain();
@@ -1308,12 +1401,18 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
- public void activityIdle(IBinder token) throws RemoteException
+ public void activityIdle(IBinder token, Configuration config) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
+ if (config != null) {
+ data.writeInt(1);
+ config.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(ACTIVITY_IDLE_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
reply.readException();
data.recycle();
@@ -1616,6 +1715,21 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public PendingIntent getRunningServiceControlPanel(ComponentName service)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ service.writeToParcel(data, 0);
+ mRemote.transact(GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION, data, reply, 0);
+ reply.readException();
+ PendingIntent res = PendingIntent.readPendingIntentOrNullFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType) throws RemoteException
{
@@ -1664,13 +1778,20 @@ class ActivityManagerProxy implements IActivityManager
return res;
}
public void setServiceForeground(ComponentName className, IBinder token,
- boolean isForeground) throws RemoteException {
+ int id, Notification notification, boolean removeNotification) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
ComponentName.writeToParcel(className, data);
data.writeStrongBinder(token);
- data.writeInt(isForeground ? 1 : 0);
+ data.writeInt(id);
+ if (notification != null) {
+ data.writeInt(1);
+ notification.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ data.writeInt(removeNotification ? 1 : 0);
mRemote.transact(SET_SERVICE_FOREGROUND_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -1737,11 +1858,15 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
- public void serviceDoneExecuting(IBinder token) throws RemoteException {
+ public void serviceDoneExecuting(IBinder token, int type, int startId,
+ int res) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
+ data.writeInt(type);
+ data.writeInt(startId);
+ data.writeInt(res);
mRemote.transact(SERVICE_DONE_EXECUTING_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
reply.readException();
data.recycle();
@@ -2212,16 +2337,6 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
- public void systemReady() throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- mRemote.transact(SYSTEM_READY_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
public boolean testIsSystemReady()
{
/* this base class version is never called */
@@ -2408,6 +2523,47 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
+
+ public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeIntArray(pids);
+ mRemote.transact(GET_PROCESS_MEMORY_INFO_TRANSACTION, data, reply, 0);
+ reply.readException();
+ Debug.MemoryInfo[] res = reply.createTypedArray(Debug.MemoryInfo.CREATOR);
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
+ public void killApplicationProcess(String processName, int uid) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(processName);
+ data.writeInt(uid);
+ mRemote.transact(KILL_APPLICATION_PROCESS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ public void overridePendingTransition(IBinder token, String packageName,
+ int enterAnim, int exitAnim) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ data.writeString(packageName);
+ data.writeInt(enterAnim);
+ data.writeInt(exitAnim);
+ mRemote.transact(OVERRIDE_PENDING_TRANSITION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e045105..b116bf8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -68,6 +68,7 @@ import android.view.WindowManagerImpl;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
+import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.util.ArrayUtils;
import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
@@ -87,6 +88,8 @@ import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Pattern;
+import dalvik.system.SamplingProfiler;
+
final class IntentReceiverLeaked extends AndroidRuntimeException {
public IntentReceiverLeaked(String msg) {
super(msg);
@@ -119,14 +122,15 @@ public final class ActivityThread {
private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
private static final boolean DEBUG_BROADCAST = false;
private static final boolean DEBUG_RESULTS = false;
- private static final boolean DEBUG_BACKUP = true;
+ private static final boolean DEBUG_BACKUP = false;
+ private static final boolean DEBUG_CONFIGURATION = false;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
private static final int LOG_ON_PAUSE_CALLED = 30021;
private static final int LOG_ON_RESUME_CALLED = 30022;
-
+
public static final ActivityThread currentActivityThread() {
return (ActivityThread)sThreadLocal.get();
}
@@ -289,9 +293,9 @@ public final class ActivityThread {
}
public PackageInfo(ActivityThread activityThread, String name,
- Context systemContext) {
+ Context systemContext, ApplicationInfo info) {
mActivityThread = activityThread;
- mApplicationInfo = new ApplicationInfo();
+ mApplicationInfo = info != null ? info : new ApplicationInfo();
mApplicationInfo.packageName = name;
mPackageName = name;
mAppDir = null;
@@ -314,7 +318,7 @@ public final class ActivityThread {
public ApplicationInfo getApplicationInfo() {
return mApplicationInfo;
}
-
+
public boolean isSecurityViolation() {
return mSecurityViolation;
}
@@ -322,7 +326,7 @@ public final class ActivityThread {
/**
* Gets the array of shared libraries that are listed as
* used by the given package.
- *
+ *
* @param packageName the name of the package (note: not its
* file name)
* @return null-ok; the array of shared libraries, each one
@@ -350,7 +354,7 @@ public final class ActivityThread {
* result is a single string with the names of the libraries
* separated by colons, or <code>null</code> if both lists
* were <code>null</code> or empty.
- *
+ *
* @param list1 null-ok; the first list
* @param list2 null-ok; the second list
* @return null-ok; the combination
@@ -378,7 +382,7 @@ public final class ActivityThread {
if (dupCheck && ArrayUtils.contains(list1, s)) {
continue;
}
-
+
if (first) {
first = false;
} else {
@@ -390,7 +394,7 @@ public final class ActivityThread {
return result.toString();
}
-
+
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader != null) {
@@ -428,7 +432,7 @@ public final class ActivityThread {
if ((mSharedLibraries != null) ||
(instrumentationLibs != null)) {
- zip =
+ zip =
combineLibs(mSharedLibraries, instrumentationLibs)
+ ':' + zip;
}
@@ -481,13 +485,14 @@ public final class ActivityThread {
return mResources;
}
- public Application makeApplication(boolean forceDefaultAppClass) {
+ public Application makeApplication(boolean forceDefaultAppClass,
+ Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
-
+
Application app = null;
-
+
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
@@ -508,9 +513,23 @@ public final class ActivityThread {
}
}
mActivityThread.mAllApplications.add(app);
- return mApplication = app;
+ mApplication = app;
+
+ if (instrumentation != null) {
+ try {
+ instrumentation.callApplicationOnCreate(app);
+ } catch (Exception e) {
+ if (!instrumentation.onException(app, e)) {
+ throw new RuntimeException(
+ "Unable to create application " + app.getClass().getName()
+ + ": " + e.toString(), e);
+ }
+ }
+ }
+
+ return app;
}
-
+
public void removeContextRegistrations(Context context,
String who, String what) {
HashMap<BroadcastReceiver, ReceiverDispatcher> rmap =
@@ -643,13 +662,13 @@ public final class ActivityThread {
final static class InnerReceiver extends IIntentReceiver.Stub {
final WeakReference<ReceiverDispatcher> mDispatcher;
final ReceiverDispatcher mStrongRef;
-
+
InnerReceiver(ReceiverDispatcher rd, boolean strong) {
mDispatcher = new WeakReference<ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered) {
+ String data, Bundle extras, boolean ordered, boolean sticky) {
ReceiverDispatcher rd = mDispatcher.get();
if (DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
@@ -657,11 +676,12 @@ public final class ActivityThread {
+ " to " + rd);
}
if (rd != null) {
- rd.performReceive(intent, resultCode, data, extras, ordered);
+ rd.performReceive(intent, resultCode, data, extras,
+ ordered, sticky);
}
}
}
-
+
final IIntentReceiver.Stub mIIntentReceiver;
final BroadcastReceiver mReceiver;
final Context mContext;
@@ -677,6 +697,7 @@ public final class ActivityThread {
private String mCurData;
private Bundle mCurMap;
private boolean mCurOrdered;
+ private boolean mCurSticky;
public void run() {
BroadcastReceiver receiver = mReceiver;
@@ -702,6 +723,7 @@ public final class ActivityThread {
receiver.setResult(mCurCode, mCurData, mCurMap);
receiver.clearAbortBroadcast();
receiver.setOrderedHint(mCurOrdered);
+ receiver.setInitialStickyHint(mCurSticky);
receiver.onReceive(mContext, intent);
} catch (Exception e) {
if (mRegistered && mCurOrdered) {
@@ -770,7 +792,7 @@ public final class ActivityThread {
BroadcastReceiver getIntentReceiver() {
return mReceiver;
}
-
+
IIntentReceiver getIIntentReceiver() {
return mIIntentReceiver;
}
@@ -784,7 +806,7 @@ public final class ActivityThread {
}
public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered) {
+ String data, Bundle extras, boolean ordered, boolean sticky) {
if (DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Log.i(TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
@@ -796,6 +818,7 @@ public final class ActivityThread {
args.mCurData = data;
args.mCurMap = extras;
args.mCurOrdered = ordered;
+ args.mCurSticky = sticky;
if (!mActivityThread.post(args)) {
if (mRegistered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
@@ -901,7 +924,7 @@ public final class ActivityThread {
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference<ServiceDispatcher> mDispatcher;
-
+
InnerConnection(ServiceDispatcher sd) {
mDispatcher = new WeakReference<ServiceDispatcher>(sd);
}
@@ -913,7 +936,7 @@ public final class ActivityThread {
}
}
}
-
+
private final HashMap<ComponentName, ConnectionInfo> mActiveConnections
= new HashMap<ComponentName, ConnectionInfo>();
@@ -965,7 +988,7 @@ public final class ActivityThread {
IServiceConnection getIServiceConnection() {
return mIServiceConnection;
}
-
+
int getFlags() {
return mFlags;
}
@@ -1112,6 +1135,7 @@ public final class ActivityThread {
boolean stopped;
boolean hideForNow;
Configuration newConfig;
+ Configuration createdConfig;
ActivityRecord nextIdle;
ActivityInfo activityInfo;
@@ -1191,7 +1215,7 @@ public final class ActivityThread {
+ " mode=" + backupMode + "}";
}
}
-
+
private static final class CreateServiceData {
IBinder token;
ServiceInfo info;
@@ -1215,6 +1239,7 @@ public final class ActivityThread {
private static final class ServiceArgsData {
IBinder token;
int startId;
+ int flags;
Intent args;
public String toString() {
return "ServiceArgsData{token=" + token + " startId=" + startId
@@ -1270,10 +1295,10 @@ public final class ActivityThread {
private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%17s %8d";
private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d";
-
+
// Formatting for checkin service - update version if row format changes
private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
-
+
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges) {
queueOrSendMessage(
@@ -1342,7 +1367,7 @@ public final class ActivityThread {
synchronized (mRelaunchingActivities) {
mRelaunchingActivities.add(r);
}
-
+
queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges);
}
@@ -1417,10 +1442,11 @@ public final class ActivityThread {
}
public final void scheduleServiceArgs(IBinder token, int startId,
- Intent args) {
+ int flags ,Intent args) {
ServiceArgsData s = new ServiceArgsData();
s.token = token;
s.startId = startId;
+ s.flags = flags;
s.args = args;
queueOrSendMessage(H.SERVICE_ARGS, s);
@@ -1436,7 +1462,6 @@ public final class ActivityThread {
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
int debugMode, boolean isRestrictedBackupMode, Configuration config,
Map<String, IBinder> services) {
- Process.setArgV0(processName);
if (services != null) {
// Setup the service cache in the ServiceManager
@@ -1461,6 +1486,10 @@ public final class ActivityThread {
queueOrSendMessage(H.EXIT_APPLICATION, null);
}
+ public final void scheduleSuicide() {
+ queueOrSendMessage(H.SUICIDE, null);
+ }
+
public void requestThumbnail(IBinder token) {
queueOrSendMessage(H.REQUEST_THUMBNAIL, token);
}
@@ -1504,11 +1533,11 @@ public final class ActivityThread {
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String dataStr, Bundle extras, boolean ordered)
- throws RemoteException {
- receiver.performReceive(intent, resultCode, dataStr, extras, ordered);
+ int resultCode, String dataStr, Bundle extras, boolean ordered,
+ boolean sticky) throws RemoteException {
+ receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
}
-
+
public void scheduleLowMemory() {
queueOrSendMessage(H.LOW_MEMORY, null);
}
@@ -1524,7 +1553,7 @@ public final class ActivityThread {
} catch (RemoteException e) {
}
}
-
+
public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) {
ProfilerControlData pcd = new ProfilerControlData();
pcd.path = path;
@@ -1543,7 +1572,11 @@ public final class ActivityThread {
Log.w(TAG, "Failed setting process group to " + group, e);
}
}
-
+
+ public void getMemoryInfo(Debug.MemoryInfo outInfo) {
+ Debug.getMemoryInfo(outInfo);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
long nativeMax = Debug.getNativeHeapSize() / 1024;
@@ -1579,7 +1612,7 @@ public final class ActivityThread {
long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024;
SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats();
SQLiteDebug.getPagerStats(stats);
-
+
// Check to see if we were called by checkin server. If so, print terse format.
boolean doCheckinFormat = false;
if (args != null) {
@@ -1587,79 +1620,79 @@ public final class ActivityThread {
if ("-c".equals(arg)) doCheckinFormat = true;
}
}
-
+
// For checkin, we print one long comma-separated list of values
if (doCheckinFormat) {
// NOTE: if you change anything significant below, also consider changing
// ACTIVITY_THREAD_CHECKIN_VERSION.
- String processName = (mBoundApplication != null)
+ String processName = (mBoundApplication != null)
? mBoundApplication.processName : "unknown";
-
+
// Header
pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(',');
pw.print(Process.myPid()); pw.print(',');
pw.print(processName); pw.print(',');
-
+
// Heap info - max
pw.print(nativeMax); pw.print(',');
pw.print(dalvikMax); pw.print(',');
pw.print("N/A,");
pw.print(nativeMax + dalvikMax); pw.print(',');
-
+
// Heap info - allocated
pw.print(nativeAllocated); pw.print(',');
pw.print(dalvikAllocated); pw.print(',');
pw.print("N/A,");
pw.print(nativeAllocated + dalvikAllocated); pw.print(',');
-
+
// Heap info - free
pw.print(nativeFree); pw.print(',');
pw.print(dalvikFree); pw.print(',');
pw.print("N/A,");
pw.print(nativeFree + dalvikFree); pw.print(',');
-
+
// Heap info - proportional set size
pw.print(memInfo.nativePss); pw.print(',');
pw.print(memInfo.dalvikPss); pw.print(',');
pw.print(memInfo.otherPss); pw.print(',');
pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(',');
-
+
// Heap info - shared
- pw.print(nativeShared); pw.print(',');
- pw.print(dalvikShared); pw.print(',');
- pw.print(otherShared); pw.print(',');
+ pw.print(nativeShared); pw.print(',');
+ pw.print(dalvikShared); pw.print(',');
+ pw.print(otherShared); pw.print(',');
pw.print(nativeShared + dalvikShared + otherShared); pw.print(',');
-
+
// Heap info - private
- pw.print(nativePrivate); pw.print(',');
+ pw.print(nativePrivate); pw.print(',');
pw.print(dalvikPrivate); pw.print(',');
pw.print(otherPrivate); pw.print(',');
pw.print(nativePrivate + dalvikPrivate + otherPrivate); pw.print(',');
-
+
// Object counts
pw.print(viewInstanceCount); pw.print(',');
pw.print(viewRootInstanceCount); pw.print(',');
pw.print(appContextInstanceCount); pw.print(',');
pw.print(activityInstanceCount); pw.print(',');
-
+
pw.print(globalAssetCount); pw.print(',');
pw.print(globalAssetManagerCount); pw.print(',');
pw.print(binderLocalObjectCount); pw.print(',');
pw.print(binderProxyObjectCount); pw.print(',');
-
+
pw.print(binderDeathObjectCount); pw.print(',');
pw.print(openSslSocketCount); pw.print(',');
-
+
// SQL
pw.print(sqliteAllocated); pw.print(',');
- pw.print(stats.databaseBytes / 1024); pw.print(',');
+ pw.print(stats.databaseBytes / 1024); pw.print(',');
pw.print(stats.numPagers); pw.print(',');
pw.print((stats.totalBytes - stats.referencedBytes) / 1024); pw.print(',');
pw.print(stats.referencedBytes / 1024); pw.print('\n');
-
+
return;
}
-
+
// otherwise, show human-readable format
printRow(pw, HEAP_COLUMN, "", "native", "dalvik", "other", "total");
printRow(pw, HEAP_COLUMN, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax);
@@ -1692,7 +1725,7 @@ public final class ActivityThread {
printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", binderDeathObjectCount);
printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", openSslSocketCount);
-
+
// SQLite mem info
pw.println(" ");
pw.println(" SQL");
@@ -1701,7 +1734,7 @@ public final class ActivityThread {
printRow(pw, TWO_COUNT_COLUMNS, "numPagers:", stats.numPagers, "inactivePageKB:",
(stats.totalBytes - stats.referencedBytes) / 1024);
printRow(pw, ONE_COUNT_COLUMN, "activePageKB:", stats.referencedBytes / 1024);
-
+
// Asset details.
String assetAlloc = AssetManager.getAssetAllocations();
if (assetAlloc != null) {
@@ -1717,6 +1750,10 @@ public final class ActivityThread {
}
private final class H extends Handler {
+ private H() {
+ SamplingProfiler.getInstance().setEventThread(mLooper.getThread());
+ }
+
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
@@ -1746,7 +1783,9 @@ public final class ActivityThread {
public static final int RELAUNCH_ACTIVITY = 126;
public static final int PROFILER_CONTROL = 127;
public static final int CREATE_BACKUP_AGENT = 128;
- public static final int DESTROY_BACKUP_AGENT = 129;
+ public static final int DESTROY_BACKUP_AGENT = 129;
+ public static final int SUICIDE = 130;
+ public static final int REMOVE_PROVIDER = 131;
String codeToString(int code) {
if (localLOGV) {
switch (code) {
@@ -1780,6 +1819,8 @@ public final class ActivityThread {
case PROFILER_CONTROL: return "PROFILER_CONTROL";
case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
+ case SUICIDE: return "SUICIDE";
+ case REMOVE_PROVIDER: return "REMOVE_PROVIDER";
}
}
return "(unknown)";
@@ -1799,6 +1840,7 @@ public final class ActivityThread {
} break;
case PAUSE_ACTIVITY:
handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
+ maybeSnapshot();
break;
case PAUSE_ACTIVITY_FINISHING:
handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
@@ -1841,6 +1883,7 @@ public final class ActivityThread {
break;
case RECEIVER:
handleReceiver((ReceiverData)msg.obj);
+ maybeSnapshot();
break;
case CREATE_SERVICE:
handleCreateService((CreateServiceData)msg.obj);
@@ -1856,6 +1899,7 @@ public final class ActivityThread {
break;
case STOP_SERVICE:
handleStopService((IBinder)msg.obj);
+ maybeSnapshot();
break;
case REQUEST_THUMBNAIL:
handleRequestThumbnail((IBinder)msg.obj);
@@ -1888,6 +1932,19 @@ public final class ActivityThread {
case DESTROY_BACKUP_AGENT:
handleDestroyBackupAgent((CreateBackupAgentData)msg.obj);
break;
+ case SUICIDE:
+ Process.killProcess(Process.myPid());
+ break;
+ case REMOVE_PROVIDER:
+ completeRemoveProvider((IContentProvider)msg.obj);
+ break;
+ }
+ }
+
+ void maybeSnapshot() {
+ if (mBoundApplication != null) {
+ SamplingProfilerIntegration.writeSnapshot(
+ mBoundApplication.processName);
}
}
}
@@ -1906,7 +1963,8 @@ public final class ActivityThread {
(a.activity != null ? a.activity.mFinished : false));
if (a.activity != null && !a.activity.mFinished) {
try {
- am.activityIdle(a.token);
+ am.activityIdle(a.token, a.createdConfig);
+ a.createdConfig = null;
} catch (RemoteException ex) {
}
}
@@ -1930,13 +1988,13 @@ public final class ActivityThread {
final private String mResDir;
final private float mScale;
final private int mHash;
-
+
ResourcesKey(String resDir, float scale) {
mResDir = resDir;
mScale = scale;
mHash = mResDir.hashCode() << 2 + (int) (mScale * 2);
}
-
+
@Override
public int hashCode() {
return mHash;
@@ -1987,7 +2045,7 @@ public final class ActivityThread {
final ArrayList<ActivityRecord> mRelaunchingActivities
= new ArrayList<ActivityRecord>();
Configuration mPendingConfiguration = null;
-
+
// These can be accessed by multiple threads; mPackages is the lock.
// XXX For now we keep around information about all packages we have
// seen, not removing entries from this map.
@@ -2122,7 +2180,7 @@ public final class ActivityThread {
return false;
}
}
-
+
ActivityThread() {
}
@@ -2155,17 +2213,17 @@ public final class ActivityThread {
public Application getApplication() {
return mInitialApplication;
}
-
+
public String getProcessName() {
return mBoundApplication.processName;
}
-
+
public ApplicationContext getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
ApplicationContext context =
ApplicationContext.createSystemContext(this);
- PackageInfo info = new PackageInfo(this, "android", context);
+ PackageInfo info = new PackageInfo(this, "android", context, null);
context.init(info, null, this);
context.getResources().updateConfiguration(
getConfiguration(), getDisplayMetricsLocked(false));
@@ -2177,6 +2235,13 @@ public final class ActivityThread {
return mSystemContext;
}
+ public void installSystemApplicationInfo(ApplicationInfo info) {
+ synchronized (this) {
+ ApplicationContext context = getSystemContext();
+ context.init(new PackageInfo(this, "android", context, info), null, this);
+ }
+ }
+
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
@@ -2214,7 +2279,7 @@ public final class ActivityThread {
}
return aInfo;
}
-
+
public final Activity startActivityNow(Activity parent, String id,
Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
Object lastNonConfigurationInstance) {
@@ -2297,7 +2362,7 @@ public final class ActivityThread {
r.packageInfo = getPackageInfo(aInfo.applicationInfo,
Context.CONTEXT_INCLUDE_CODE);
}
-
+
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
@@ -2328,8 +2393,8 @@ public final class ActivityThread {
}
try {
- Application app = r.packageInfo.makeApplication(false);
-
+ Application app = r.packageInfo.makeApplication(false, mInstrumentation);
+
if (localLOGV) Log.v(TAG, "Performing launch of " + r);
if (localLOGV) Log.v(
TAG, r + ": app=" + app
@@ -2344,11 +2409,13 @@ public final class ActivityThread {
appContext.setOuterContext(activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mConfiguration);
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Launching activity "
+ + r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);
-
+
if (customIntent != null) {
activity.mIntent = customIntent;
}
@@ -2417,6 +2484,7 @@ public final class ActivityThread {
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
+ r.createdConfig = new Configuration(a.getResources().getConfiguration());
handleResumeActivity(r.token, false, r.isForward);
if (!r.activity.mFinished && r.startsNotResumed) {
@@ -2486,7 +2554,7 @@ public final class ActivityThread {
}
}
}
-
+
private final void handleNewIntent(NewIntentData data) {
performNewIntents(data.token, data.intents);
}
@@ -2523,8 +2591,8 @@ public final class ActivityThread {
}
try {
- Application app = packageInfo.makeApplication(false);
-
+ Application app = packageInfo.makeApplication(false, mInstrumentation);
+
if (localLOGV) Log.v(
TAG, "Performing receive of " + data.intent
+ ": app=" + app
@@ -2581,7 +2649,7 @@ public final class ActivityThread {
+ " already exists");
return;
}
-
+
BackupAgent agent = null;
String classname = data.appInfo.backupAgentName;
if (classname == null) {
@@ -2635,7 +2703,7 @@ public final class ActivityThread {
// Tear down a BackupAgent
private final void handleDestroyBackupAgent(CreateBackupAgentData data) {
if (DEBUG_BACKUP) Log.v(TAG, "handleDestroyBackupAgent: " + data);
-
+
PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo);
String packageName = packageInfo.mPackageName;
BackupAgent agent = mBackupAgents.get(packageName);
@@ -2677,14 +2745,15 @@ public final class ActivityThread {
ApplicationContext context = new ApplicationContext();
context.init(packageInfo, null, this);
- Application app = packageInfo.makeApplication(false);
+ Application app = packageInfo.makeApplication(false, mInstrumentation);
context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
try {
- ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
+ ActivityManagerNative.getDefault().serviceDoneExecuting(
+ data.token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2710,7 +2779,7 @@ public final class ActivityThread {
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
- data.token);
+ data.token, 0, 0, 0);
}
} catch (RemoteException ex) {
}
@@ -2736,7 +2805,7 @@ public final class ActivityThread {
data.token, data.intent, doRebind);
} else {
ActivityManagerNative.getDefault().serviceDoneExecuting(
- data.token);
+ data.token, 0, 0, 0);
}
} catch (RemoteException ex) {
}
@@ -2773,9 +2842,10 @@ public final class ActivityThread {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
}
- s.onStart(data.args, data.startId);
+ int res = s.onStartCommand(data.args, data.flags, data.startId);
try {
- ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
+ ActivityManagerNative.getDefault().serviceDoneExecuting(
+ data.token, 1, data.startId, res);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2801,7 +2871,8 @@ public final class ActivityThread {
((ApplicationContext) context).scheduleFinalCleanup(who, "Service");
}
try {
- ActivityManagerNative.getDefault().serviceDoneExecuting(token);
+ ActivityManagerNative.getDefault().serviceDoneExecuting(
+ token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2837,9 +2908,9 @@ public final class ActivityThread {
}
r.activity.performResume();
- EventLog.writeEvent(LOG_ON_RESUME_CALLED,
+ EventLog.writeEvent(LOG_ON_RESUME_CALLED,
r.activity.getComponentName().getClassName());
-
+
r.paused = false;
r.stopped = false;
if (r.activity.mStartedActivity) {
@@ -2875,7 +2946,7 @@ public final class ActivityThread {
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
-
+
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
@@ -2904,9 +2975,11 @@ public final class ActivityThread {
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
- if (!r.activity.mFinished && r.activity.mDecor != null
- && !r.hideForNow) {
+ if (!r.activity.mFinished && !a.mStartedActivity
+ && r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Resuming activity "
+ + r.activityInfo.name + " with newConfig " + r.newConfig);
performConfigurationChanged(r.activity, r.newConfig);
r.newConfig = null;
}
@@ -2919,9 +2992,11 @@ public final class ActivityThread {
l.softInputMode = (l.softInputMode
& (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
| forwardBit;
- ViewManager wm = a.getWindowManager();
- View decor = r.window.getDecorView();
- wm.updateViewLayout(decor, l);
+ if (r.activity.mVisibleFromClient) {
+ ViewManager wm = a.getWindowManager();
+ View decor = r.window.getDecorView();
+ wm.updateViewLayout(decor, l);
+ }
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
@@ -2994,7 +3069,7 @@ public final class ActivityThread {
if (userLeaving) {
performUserLeavingActivity(r);
}
-
+
r.activity.mConfigChangeFlags |= configChanges;
Bundle state = performPauseActivity(token, finished, true);
@@ -3146,6 +3221,8 @@ public final class ActivityThread {
}
}
if (r.newConfig != null) {
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Updating activity vis "
+ + r.activityInfo.name + " with new config " + r.newConfig);
performConfigurationChanged(r.activity, r.newConfig);
r.newConfig = null;
}
@@ -3171,7 +3248,7 @@ public final class ActivityThread {
+ " win=" + r.window);
updateVisibility(r, show);
-
+
// Tell activity manager we have been stopped.
try {
ActivityManagerNative.getDefault().activityStopped(
@@ -3287,7 +3364,7 @@ public final class ActivityThread {
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
- EventLog.writeEvent(LOG_ON_PAUSE_CALLED,
+ EventLog.writeEvent(LOG_ON_PAUSE_CALLED,
r.activity.getComponentName().getClassName());
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
@@ -3344,7 +3421,7 @@ public final class ActivityThread {
+ ": " + e.toString(), e);
}
}
-
+
}
try {
r.activity.mCalled = false;
@@ -3426,6 +3503,10 @@ public final class ActivityThread {
unscheduleGcIdler();
Configuration changedConfig = null;
+
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Relaunching activity "
+ + tmp.token + " with configChanges=0x"
+ + Integer.toHexString(configChanges));
// First: make sure we have the most recent configuration and most
// recent version of the activity, or skip it if some previous call
@@ -3443,38 +3524,42 @@ public final class ActivityThread {
N--;
}
}
-
+
if (tmp == null) {
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Abort, activity not relaunching!");
return;
}
-
+
if (mPendingConfiguration != null) {
changedConfig = mPendingConfiguration;
mPendingConfiguration = null;
}
}
+
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Relaunching activity "
+ + tmp.token + ": changedConfig=" + changedConfig);
// If there was a pending configuration change, execute it first.
if (changedConfig != null) {
handleConfigurationChanged(changedConfig);
}
-
+
ActivityRecord r = mActivities.get(tmp.token);
- if (localLOGV) Log.v(TAG, "Handling relaunch of " + r);
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Handling relaunch of " + r);
if (r == null) {
return;
}
-
+
r.activity.mConfigChangeFlags |= configChanges;
Intent currentIntent = r.activity.mIntent;
-
+
Bundle savedState = null;
if (!r.paused) {
savedState = performPauseActivity(r.token, false, true);
}
-
+
handleDestroyActivity(r.token, false, configChanges, true);
-
+
r.activity = null;
r.window = null;
r.hideForNow = false;
@@ -3498,7 +3583,7 @@ public final class ActivityThread {
if (savedState != null) {
r.state = savedState;
}
-
+
handleLaunchActivity(r, currentIntent);
}
@@ -3528,7 +3613,7 @@ public final class ActivityThread {
boolean allActivities, Configuration newConfig) {
ArrayList<ComponentCallbacks> callbacks
= new ArrayList<ComponentCallbacks>();
-
+
if (mActivities.size() > 0) {
Iterator<ActivityRecord> it = mActivities.values().iterator();
while (it.hasNext()) {
@@ -3546,6 +3631,8 @@ public final class ActivityThread {
// the activity manager may, before then, decide the
// activity needs to be destroyed to handle its new
// configuration.
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Setting activity "
+ + ar.activityInfo.name + " newConfig=" + newConfig);
ar.newConfig = newConfig;
}
}
@@ -3569,10 +3656,10 @@ public final class ActivityThread {
for (int i=0; i<N; i++) {
callbacks.add(mAllApplications.get(i));
}
-
+
return callbacks;
}
-
+
private final void performConfigurationChanged(
ComponentCallbacks cb, Configuration config) {
// Only for Activity objects, check that they actually call up to their
@@ -3582,18 +3669,18 @@ public final class ActivityThread {
if (activity != null) {
activity.mCalled = false;
}
-
+
boolean shouldChangeConfig = false;
if ((activity == null) || (activity.mCurrentConfig == null)) {
shouldChangeConfig = true;
} else {
-
+
// If the new config is the same as the config this Activity
// is already running with then don't bother calling
// onConfigurationChanged
int diff = activity.mCurrentConfig.diff(config);
if (diff != 0) {
-
+
// If this activity doesn't handle any of the config changes
// then don't bother calling onConfigurationChanged as we're
// going to destroy it.
@@ -3602,10 +3689,12 @@ public final class ActivityThread {
}
}
}
-
+
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Config callback " + cb
+ + ": shouldChangeConfig=" + shouldChangeConfig);
if (shouldChangeConfig) {
cb.onConfigurationChanged(config);
-
+
if (activity != null) {
if (!activity.mCalled) {
throw new SuperNotCalledException(
@@ -3619,16 +3708,19 @@ public final class ActivityThread {
}
final void handleConfigurationChanged(Configuration config) {
-
+
synchronized (mRelaunchingActivities) {
if (mPendingConfiguration != null) {
config = mPendingConfiguration;
mPendingConfiguration = null;
}
}
-
+
ArrayList<ComponentCallbacks> callbacks
= new ArrayList<ComponentCallbacks>();
+
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Handle configuration changed: "
+ + config);
synchronized(mPackages) {
if (mConfiguration == null) {
@@ -3664,10 +3756,10 @@ public final class ActivityThread {
}
}
}
-
+
callbacks = collectComponentCallbacksLocked(false, config);
}
-
+
final int N = callbacks.size();
for (int i=0; i<N; i++) {
performConfigurationChanged(callbacks.get(i), config);
@@ -3679,6 +3771,9 @@ public final class ActivityThread {
if (r == null || r.activity == null) {
return;
}
+
+ if (DEBUG_CONFIGURATION) Log.v(TAG, "Handle activity config changed: "
+ + r.activityInfo.name);
performConfigurationChanged(r.activity, mConfiguration);
}
@@ -3702,7 +3797,7 @@ public final class ActivityThread {
Debug.stopMethodTracing();
}
}
-
+
final void handleLowMemory() {
ArrayList<ComponentCallbacks> callbacks
= new ArrayList<ComponentCallbacks>();
@@ -3710,7 +3805,7 @@ public final class ActivityThread {
synchronized(mPackages) {
callbacks = collectComponentCallbacksLocked(true, null);
}
-
+
final int N = callbacks.size();
for (int i=0; i<N; i++) {
callbacks.get(i).onLowMemory();
@@ -3721,7 +3816,7 @@ public final class ActivityThread {
int sqliteReleased = SQLiteDatabase.releaseMemory();
EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased);
}
-
+
// Ask graphics to free up as much as possible (font/image caches)
Canvas.freeCaches();
@@ -3737,6 +3832,7 @@ public final class ActivityThread {
//Process.setUid(data.appInfo.uid);
// send up app name; do this *before* waiting for debugger
+ Process.setArgV0(data.processName);
android.ddm.DdmHandleAppName.setAppName(data.processName);
/*
@@ -3768,7 +3864,7 @@ public final class ActivityThread {
== 0) {
Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
}
-
+
if (data.debugMode != IApplicationThread.DEBUG_OFF) {
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
@@ -3859,7 +3955,7 @@ public final class ActivityThread {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
- Application app = data.info.makeApplication(data.restrictedBackupMode);
+ Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
List<ProviderInfo> providers = data.providers;
@@ -3992,22 +4088,51 @@ public final class ActivityThread {
} else {
prc.count--;
if(prc.count == 0) {
- mProviderRefCountMap.remove(jBinder);
- //invoke removeProvider to dereference provider
- removeProviderLocked(provider);
+ // Schedule the actual remove asynchronously, since we
+ // don't know the context this will be called in.
+ // TODO: it would be nice to post a delayed message, so
+ // if we come back and need the same provider quickly
+ // we will still have it available.
+ Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, provider);
+ mH.sendMessage(msg);
} //end if
} //end else
} //end synchronized
return true;
}
- public final void removeProviderLocked(IContentProvider provider) {
+ final void completeRemoveProvider(IContentProvider provider) {
+ IBinder jBinder = provider.asBinder();
+ String name = null;
+ synchronized(mProviderMap) {
+ ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
+ if(prc != null && prc.count == 0) {
+ mProviderRefCountMap.remove(jBinder);
+ //invoke removeProvider to dereference provider
+ name = removeProviderLocked(provider);
+ }
+ }
+
+ if (name != null) {
+ try {
+ if(localLOGV) Log.v(TAG, "removeProvider::Invoking " +
+ "ActivityManagerNative.removeContentProvider(" + name);
+ ActivityManagerNative.getDefault().removeContentProvider(
+ getApplicationThread(), name);
+ } catch (RemoteException e) {
+ //do nothing content provider object is dead any way
+ } //end catch
+ }
+ }
+
+ public final String removeProviderLocked(IContentProvider provider) {
if (provider == null) {
- return;
+ return null;
}
IBinder providerBinder = provider.asBinder();
- boolean amRemoveFlag = false;
+ String name = null;
+
// remove the provider from mProviderMap
Iterator<ProviderRecord> iter = mProviderMap.values().iterator();
while (iter.hasNext()) {
@@ -4017,7 +4142,7 @@ public final class ActivityThread {
//find if its published by this process itself
if(pr.mLocalProvider != null) {
if(localLOGV) Log.i(TAG, "removeProvider::found local provider returning");
- return;
+ return name;
}
if(localLOGV) Log.v(TAG, "removeProvider::Not local provider Unlinking " +
"death recipient");
@@ -4025,18 +4150,13 @@ public final class ActivityThread {
myBinder.unlinkToDeath(pr, 0);
iter.remove();
//invoke remove only once for the very first name seen
- if(!amRemoveFlag) {
- try {
- if(localLOGV) Log.v(TAG, "removeProvider::Invoking " +
- "ActivityManagerNative.removeContentProvider("+pr.mName);
- ActivityManagerNative.getDefault().removeContentProvider(getApplicationThread(), pr.mName);
- amRemoveFlag = true;
- } catch (RemoteException e) {
- //do nothing content provider object is dead any way
- } //end catch
+ if(name == null) {
+ name = pr.mName;
}
} //end if myBinder
} //end while iter
+
+ return name;
}
final void removeDeadProvider(String name, IContentProvider provider) {
@@ -4193,6 +4313,8 @@ public final class ActivityThread {
}
public static final void main(String[] args) {
+ SamplingProfilerIntegration.start();
+
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
@@ -4207,9 +4329,9 @@ public final class ActivityThread {
}
thread.detach();
- String name;
- if (thread.mInitialApplication != null) name = thread.mInitialApplication.getPackageName();
- else name = "<unknown>";
+ String name = (thread.mInitialApplication != null)
+ ? thread.mInitialApplication.getPackageName()
+ : "<unknown>";
Log.i(TAG, "Main thread of " + name + " is now exiting");
}
}
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 92929ea..f48f150 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -22,8 +22,6 @@ import com.google.android.collect.Maps;
import org.xmlpull.v1.XmlPullParserException;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.IBluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -40,6 +38,7 @@ import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
@@ -59,8 +58,6 @@ import android.content.res.XmlResourceParser;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.SensorManager;
import android.location.ILocationManager;
@@ -78,7 +75,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.Looper;
-import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -88,14 +84,14 @@ import android.os.FileUtils.FileStatus;
import android.telephony.TelephonyManager;
import android.text.ClipboardManager;
import android.util.AndroidRuntimeException;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextThemeWrapper;
-import android.view.Display;
import android.view.LayoutInflater;
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
+import android.accounts.AccountManager;
+import android.accounts.IAccountManager;
import java.io.File;
import java.io.FileInputStream;
@@ -160,9 +156,6 @@ class ApplicationContext extends Context {
private static ConnectivityManager sConnectivityManager;
private static WifiManager sWifiManager;
private static LocationManager sLocationManager;
- private static boolean sIsBluetoothDeviceCached = false;
- private static BluetoothDevice sBluetoothDevice;
- private static IWallpaperService sWallpaperService;
private static final HashMap<File, SharedPreferencesImpl> sSharedPrefs =
new HashMap<File, SharedPreferencesImpl>();
@@ -177,8 +170,8 @@ class ApplicationContext extends Context {
private Resources.Theme mTheme = null;
private PackageManager mPackageManager;
private NotificationManager mNotificationManager = null;
- private AccessibilityManager mAccessibilityManager = null;
private ActivityManager mActivityManager = null;
+ private WallpaperManager mWallpaperManager = null;
private Context mReceiverRestrictedContext = null;
private SearchManager mSearchManager = null;
private SensorManager mSensorManager = null;
@@ -188,6 +181,7 @@ class ApplicationContext extends Context {
private TelephonyManager mTelephonyManager = null;
private ClipboardManager mClipboardManager = null;
private boolean mRestricted;
+ private AccountManager mAccountManager; // protected by mSync
private final Object mSync = new Object();
@@ -198,9 +192,6 @@ class ApplicationContext extends Context {
private File mCacheDir;
- private Drawable mWallpaper;
- private IWallpaperServiceCallback mWallpaperCallback = null;
-
private static long sInstanceCount = 0;
private static final String[] EMPTY_FILE_LIST = {};
@@ -520,127 +511,37 @@ class ApplicationContext extends Context {
@Override
public Drawable getWallpaper() {
- Drawable dr = peekWallpaper();
- return dr != null ? dr : getResources().getDrawable(
- com.android.internal.R.drawable.default_wallpaper);
+ return getWallpaperManager().getDrawable();
}
@Override
- public synchronized Drawable peekWallpaper() {
- if (mWallpaper != null) {
- return mWallpaper;
- }
- mWallpaperCallback = new WallpaperCallback(this);
- mWallpaper = getCurrentWallpaperLocked();
- return mWallpaper;
- }
-
- private Drawable getCurrentWallpaperLocked() {
- try {
- ParcelFileDescriptor fd = getWallpaperService().getWallpaper(mWallpaperCallback);
- if (fd != null) {
- Bitmap bm = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
- if (bm != null) {
- // For now clear the density until we figure out how
- // to deal with it for wallpapers.
- bm.setDensity(0);
- return new BitmapDrawable(getResources(), bm);
- }
- }
- } catch (RemoteException e) {
- }
- return null;
+ public Drawable peekWallpaper() {
+ return getWallpaperManager().peekDrawable();
}
@Override
public int getWallpaperDesiredMinimumWidth() {
- try {
- return getWallpaperService().getWidthHint();
- } catch (RemoteException e) {
- // Shouldn't happen!
- return 0;
- }
+ return getWallpaperManager().getDesiredMinimumWidth();
}
@Override
public int getWallpaperDesiredMinimumHeight() {
- try {
- return getWallpaperService().getHeightHint();
- } catch (RemoteException e) {
- // Shouldn't happen!
- return 0;
- }
+ return getWallpaperManager().getDesiredMinimumHeight();
}
@Override
public void setWallpaper(Bitmap bitmap) throws IOException {
- try {
- ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
- if (fd == null) {
- return;
- }
- FileOutputStream fos = null;
- try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
- } finally {
- if (fos != null) {
- fos.close();
- }
- }
- } catch (RemoteException e) {
- }
+ getWallpaperManager().setBitmap(bitmap);
}
@Override
public void setWallpaper(InputStream data) throws IOException {
- try {
- ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
- if (fd == null) {
- return;
- }
- FileOutputStream fos = null;
- try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- setWallpaper(data, fos);
- } finally {
- if (fos != null) {
- fos.close();
- }
- }
- } catch (RemoteException e) {
- }
- }
-
- private void setWallpaper(InputStream data, FileOutputStream fos)
- throws IOException {
- byte[] buffer = new byte[32768];
- int amt;
- while ((amt=data.read(buffer)) > 0) {
- fos.write(buffer, 0, amt);
- }
+ getWallpaperManager().setStream(data);
}
@Override
public void clearWallpaper() throws IOException {
- try {
- /* Set the wallpaper to the default values */
- ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
- if (fd != null) {
- FileOutputStream fos = null;
- try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- setWallpaper(getResources().openRawResource(
- com.android.internal.R.drawable.default_wallpaper),
- fos);
- } finally {
- if (fos != null) {
- fos.close();
- }
- }
- }
- } catch (RemoteException e) {
- }
+ getWallpaperManager().clear();
}
@Override
@@ -656,6 +557,27 @@ class ApplicationContext extends Context {
}
@Override
+ public void startIntentSender(IntentSender intent,
+ Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
+ throws IntentSender.SendIntentException {
+ try {
+ String resolvedType = null;
+ if (fillInIntent != null) {
+ resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
+ }
+ int result = ActivityManagerNative.getDefault()
+ .startActivityIntentSender(mMainThread.getApplicationThread(), intent,
+ fillInIntent, resolvedType, null, null,
+ 0, flagsMask, flagsValues);
+ if (result == IActivityManager.START_CANCELED) {
+ throw new IntentSender.SendIntentException();
+ }
+ Instrumentation.checkStartActivityResult(result, null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public void sendBroadcast(Intent intent) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
@@ -733,6 +655,38 @@ class ApplicationContext extends Context {
}
@Override
+ public void sendStickyOrderedBroadcast(Intent intent,
+ BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData,
+ Bundle initialExtras) {
+ IIntentReceiver rd = null;
+ if (resultReceiver != null) {
+ if (mPackageInfo != null) {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = mPackageInfo.getReceiverDispatcher(
+ resultReceiver, getOuterContext(), scheduler,
+ mMainThread.getInstrumentation(), false);
+ } else {
+ if (scheduler == null) {
+ scheduler = mMainThread.getHandler();
+ }
+ rd = new ActivityThread.PackageInfo.ReceiverDispatcher(
+ resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
+ }
+ }
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ ActivityManagerNative.getDefault().broadcastIntent(
+ mMainThread.getApplicationThread(), intent, resolvedType, rd,
+ initialCode, initialData, initialExtras, null,
+ true, true);
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public void removeStickyBroadcast(Intent intent) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
if (resolvedType != null) {
@@ -901,8 +855,12 @@ class ApplicationContext extends Context {
}
} else if (ACTIVITY_SERVICE.equals(name)) {
return getActivityManager();
+ } else if (INPUT_METHOD_SERVICE.equals(name)) {
+ return InputMethodManager.getInstance(this);
} else if (ALARM_SERVICE.equals(name)) {
return getAlarmManager();
+ } else if (ACCOUNT_SERVICE.equals(name)) {
+ return getAccountManager();
} else if (POWER_SERVICE.equals(name)) {
return getPowerManager();
} else if (CONNECTIVITY_SERVICE.equals(name)) {
@@ -919,10 +877,8 @@ class ApplicationContext extends Context {
return getLocationManager();
} else if (SEARCH_SERVICE.equals(name)) {
return getSearchManager();
- } else if ( SENSOR_SERVICE.equals(name)) {
+ } else if (SENSOR_SERVICE.equals(name)) {
return getSensorManager();
- } else if (BLUETOOTH_SERVICE.equals(name)) {
- return getBluetoothDevice();
} else if (VIBRATOR_SERVICE.equals(name)) {
return getVibrator();
} else if (STATUS_BAR_SERVICE.equals(name)) {
@@ -938,13 +894,24 @@ class ApplicationContext extends Context {
return getTelephonyManager();
} else if (CLIPBOARD_SERVICE.equals(name)) {
return getClipboardManager();
- } else if (INPUT_METHOD_SERVICE.equals(name)) {
- return InputMethodManager.getInstance(this);
+ } else if (WALLPAPER_SERVICE.equals(name)) {
+ return getWallpaperManager();
}
return null;
}
+ private AccountManager getAccountManager() {
+ synchronized (mSync) {
+ if (mAccountManager == null) {
+ IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
+ IAccountManager service = IAccountManager.Stub.asInterface(b);
+ mAccountManager = new AccountManager(this, service);
+ }
+ return mAccountManager;
+ }
+ }
+
private ActivityManager getActivityManager() {
synchronized (mSync) {
if (mActivityManager == null) {
@@ -1001,8 +968,7 @@ class ApplicationContext extends Context {
return sWifiManager;
}
- private NotificationManager getNotificationManager()
- {
+ private NotificationManager getNotificationManager() {
synchronized (mSync) {
if (mNotificationManager == null) {
mNotificationManager = new NotificationManager(
@@ -1013,6 +979,16 @@ class ApplicationContext extends Context {
return mNotificationManager;
}
+ private WallpaperManager getWallpaperManager() {
+ synchronized (mSync) {
+ if (mWallpaperManager == null) {
+ mWallpaperManager = new WallpaperManager(getOuterContext(),
+ mMainThread.getHandler());
+ }
+ }
+ return mWallpaperManager;
+ }
+
private TelephonyManager getTelephonyManager() {
synchronized (mSync) {
if (mTelephonyManager == null) {
@@ -1052,23 +1028,6 @@ class ApplicationContext extends Context {
return mSearchManager;
}
- private BluetoothDevice getBluetoothDevice() {
- if (sIsBluetoothDeviceCached) {
- return sBluetoothDevice;
- }
- synchronized (sSync) {
- IBinder b = ServiceManager.getService(BLUETOOTH_SERVICE);
- if (b == null) {
- sBluetoothDevice = null;
- } else {
- IBluetoothDevice service = IBluetoothDevice.Stub.asInterface(b);
- sBluetoothDevice = new BluetoothDevice(service);
- }
- sIsBluetoothDeviceCached = true;
- }
- return sBluetoothDevice;
- }
-
private SensorManager getSensorManager() {
synchronized (mSync) {
if (mSensorManager == null) {
@@ -1087,16 +1046,6 @@ class ApplicationContext extends Context {
return mVibrator;
}
- private IWallpaperService getWallpaperService() {
- synchronized (sSync) {
- if (sWallpaperService == null) {
- IBinder b = ServiceManager.getService(WALLPAPER_SERVICE);
- sWallpaperService = IWallpaperService.Stub.asInterface(b);
- }
- }
- return sWallpaperService;
- }
-
private AudioManager getAudioManager()
{
if (mAudioManager == null) {
@@ -1710,6 +1659,24 @@ class ApplicationContext extends Context {
}
@Override
+ public FeatureInfo[] getSystemAvailableFeatures() {
+ try {
+ return mPM.getSystemAvailableFeatures();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
+ public boolean hasSystemFeature(String name) {
+ try {
+ return mPM.hasSystemFeature(name);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
public int checkPermission(String permName, String pkgName) {
try {
return mPM.checkPermission(permName, pkgName);
@@ -1746,6 +1713,15 @@ class ApplicationContext extends Context {
}
@Override
+ public int checkSignatures(int uid1, int uid2) {
+ try {
+ return mPM.checkUidSignatures(uid1, uid2);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
public String[] getPackagesForUid(int uid) {
try {
return mPM.getPackagesForUid(uid);
@@ -2766,6 +2742,7 @@ class ApplicationContext extends Context {
if (mFile.exists()) {
if (!mFile.renameTo(mBackupFile)) {
Log.e(TAG, "Couldn't rename file " + mFile + " to backup file " + mBackupFile);
+ return false;
}
}
@@ -2801,25 +2778,4 @@ class ApplicationContext extends Context {
return false;
}
}
-
- private static class WallpaperCallback extends IWallpaperServiceCallback.Stub {
- private WeakReference<ApplicationContext> mContext;
-
- public WallpaperCallback(ApplicationContext context) {
- mContext = new WeakReference<ApplicationContext>(context);
- }
-
- public synchronized void onWallpaperChanged() {
-
- /* The wallpaper has changed but we shouldn't eagerly load the
- * wallpaper as that would be inefficient. Reset the cached wallpaper
- * to null so if the user requests the wallpaper again then we'll
- * fetch it.
- */
- final ApplicationContext applicationContext = mContext.get();
- if (applicationContext != null) {
- applicationContext.mWallpaper = null;
- }
- }
- }
}
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 6b17236..aeae5f9 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -171,6 +171,11 @@ public class ApplicationErrorReport implements Parcelable {
public String throwMethodName;
/**
+ * Line number the exception was thrown from.
+ */
+ public int throwLineNumber;
+
+ /**
* Stack trace.
*/
public String stackTrace;
@@ -190,6 +195,7 @@ public class ApplicationErrorReport implements Parcelable {
throwFileName = in.readString();
throwClassName = in.readString();
throwMethodName = in.readString();
+ throwLineNumber = in.readInt();
stackTrace = in.readString();
}
@@ -202,6 +208,7 @@ public class ApplicationErrorReport implements Parcelable {
dest.writeString(throwFileName);
dest.writeString(throwClassName);
dest.writeString(throwMethodName);
+ dest.writeInt(throwLineNumber);
dest.writeString(stackTrace);
}
@@ -214,6 +221,7 @@ public class ApplicationErrorReport implements Parcelable {
pw.println(prefix + "throwFileName: " + throwFileName);
pw.println(prefix + "throwClassName: " + throwClassName);
pw.println(prefix + "throwMethodName: " + throwMethodName);
+ pw.println(prefix + "throwLineNumber: " + throwLineNumber);
pw.println(prefix + "stackTrace: " + stackTrace);
}
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index a3c6325..a772a8f 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -26,6 +26,7 @@ import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.IBinder;
@@ -206,8 +207,14 @@ public abstract class ApplicationThreadNative extends Binder
data.enforceInterface(IApplicationThread.descriptor);
IBinder token = data.readStrongBinder();
int startId = data.readInt();
- Intent args = Intent.CREATOR.createFromParcel(data);
- scheduleServiceArgs(token, startId, args);
+ int fl = data.readInt();
+ Intent args;
+ if (data.readInt() != 0) {
+ args = Intent.CREATOR.createFromParcel(data);
+ } else {
+ args = null;
+ }
+ scheduleServiceArgs(token, startId, fl, args);
return true;
}
@@ -251,6 +258,13 @@ public abstract class ApplicationThreadNative extends Binder
return true;
}
+ case SCHEDULE_SUICIDE_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ scheduleSuicide();
+ return true;
+ }
+
case REQUEST_THUMBNAIL_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -303,8 +317,9 @@ public abstract class ApplicationThreadNative extends Binder
String dataStr = data.readString();
Bundle extras = data.readBundle();
boolean ordered = data.readInt() != 0;
+ boolean sticky = data.readInt() != 0;
scheduleRegisteredReceiver(receiver, intent,
- resultCode, dataStr, extras, ordered);
+ resultCode, dataStr, extras, ordered, sticky);
return true;
}
@@ -364,6 +379,16 @@ public abstract class ApplicationThreadNative extends Binder
scheduleDestroyBackupAgent(appInfo);
return true;
}
+
+ case GET_MEMORY_INFO_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ Debug.MemoryInfo mi = new Debug.MemoryInfo();
+ getMemoryInfo(mi);
+ reply.writeNoException();
+ mi.writeToParcel(reply, 0);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -524,7 +549,8 @@ class ApplicationThreadProxy implements IApplicationThread {
data.writeInterfaceToken(IApplicationThread.descriptor);
app.writeToParcel(data, 0);
data.writeInt(backupMode);
- mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null, 0);
+ mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
data.recycle();
}
@@ -532,7 +558,8 @@ class ApplicationThreadProxy implements IApplicationThread {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
app.writeToParcel(data, 0);
- mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null, 0);
+ mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
data.recycle();
}
@@ -571,12 +598,18 @@ class ApplicationThreadProxy implements IApplicationThread {
}
public final void scheduleServiceArgs(IBinder token, int startId,
- Intent args) throws RemoteException {
+ int flags, Intent args) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
data.writeInt(startId);
- args.writeToParcel(data, 0);
+ data.writeInt(flags);
+ if (args != null) {
+ data.writeInt(1);
+ args.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(SCHEDULE_SERVICE_ARGS_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
@@ -627,7 +660,15 @@ class ApplicationThreadProxy implements IApplicationThread {
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
+ public final void scheduleSuicide() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(SCHEDULE_SUICIDE_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
public final void requestThumbnail(IBinder token)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -676,7 +717,7 @@ class ApplicationThreadProxy implements IApplicationThread {
}
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String dataStr, Bundle extras, boolean ordered)
+ int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky)
throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -686,6 +727,7 @@ class ApplicationThreadProxy implements IApplicationThread {
data.writeString(dataStr);
data.writeBundle(extras);
data.writeInt(ordered ? 1 : 0);
+ data.writeInt(sticky ? 1 : 0);
mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
@@ -742,5 +784,16 @@ class ApplicationThreadProxy implements IApplicationThread {
IBinder.FLAG_ONEWAY);
data.recycle();
}
+
+ public void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(GET_MEMORY_INFO_TRANSACTION, data, reply, 0);
+ reply.readException();
+ outInfo.readFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ }
}
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java
index 0ac8a1e..b207998 100644
--- a/core/java/android/app/BackupAgent.java
+++ b/core/java/android/app/BackupAgent.java
@@ -36,6 +36,7 @@ import java.io.IOException;
*/
public abstract class BackupAgent extends ContextWrapper {
private static final String TAG = "BackupAgent";
+ private static final boolean DEBUG = false;
public BackupAgent() {
super(null);
@@ -116,7 +117,7 @@ public abstract class BackupAgent extends ContextWrapper {
ParcelFileDescriptor data,
ParcelFileDescriptor newState) throws RemoteException {
// !!! TODO - real implementation; for now just invoke the callbacks directly
- Log.v(TAG, "doBackup() invoked");
+ if (DEBUG) Log.v(TAG, "doBackup() invoked");
BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
try {
BackupAgent.this.onBackup(oldState, output, newState);
@@ -132,7 +133,7 @@ public abstract class BackupAgent extends ContextWrapper {
public void doRestore(ParcelFileDescriptor data, int appVersionCode,
ParcelFileDescriptor newState) throws RemoteException {
// !!! TODO - real implementation; for now just invoke the callbacks directly
- Log.v(TAG, "doRestore() invoked");
+ if (DEBUG) Log.v(TAG, "doRestore() invoked");
BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
try {
BackupAgent.this.onRestore(input, appVersionCode, newState);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 9432755..58e8b32 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -21,6 +21,7 @@ import com.android.internal.policy.PolicyManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.ComponentName;
+import android.content.ContextWrapper;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -480,17 +481,15 @@ public class Dialog implements DialogInterface, Window.Callback,
*
* <p>If the focused view didn't want this event, this method is called.
*
- * <p>The default implementation handles KEYCODE_BACK to close the
- * dialog.
+ * <p>The default implementation consumed the KEYCODE_BACK to later
+ * handle it in {@link #onKeyUp}.
*
* @see #onKeyUp
* @see android.view.KeyEvent
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
- if (mCancelable) {
- cancel();
- }
+ event.startTracking();
return true;
}
@@ -498,12 +497,29 @@ public class Dialog implements DialogInterface, Window.Callback,
}
/**
+ * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
+ * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
+ * the event).
+ */
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
* A key was released.
*
+ * <p>The default implementation handles KEYCODE_BACK to close the
+ * dialog.
+ *
* @see #onKeyDown
* @see KeyEvent
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+ && !event.isCanceled()) {
+ onBackPressed();
+ return true;
+ }
return false;
}
@@ -517,6 +533,17 @@ public class Dialog implements DialogInterface, Window.Callback,
}
/**
+ * Called when the dialog has detected the user's press of the back
+ * key. The default implementation simply cancels the dialog (only if
+ * it is cancelable), but you can override this to do whatever you want.
+ */
+ public void onBackPressed() {
+ if (mCancelable) {
+ cancel();
+ }
+ }
+
+ /**
* Called when a touch screen event was not handled by any of the views
* under it. This is most useful to process touch events that happen outside
* of your window bounds, where there is no view to receive it.
@@ -576,6 +603,12 @@ public class Dialog implements DialogInterface, Window.Callback,
public void onWindowFocusChanged(boolean hasFocus) {
}
+ public void onAttachedToWindow() {
+ }
+
+ public void onDetachedFromWindow() {
+ }
+
/**
* Called to process key events. You can override this to intercept all
* key events before they are dispatched to the window. Be sure to call
@@ -592,7 +625,8 @@ public class Dialog implements DialogInterface, Window.Callback,
if (mWindow.superDispatchKeyEvent(event)) {
return true;
}
- return event.dispatch(this);
+ return event.dispatch(this, mDecor != null
+ ? mDecor.getKeyDispatcherState() : null, this);
}
/**
@@ -795,14 +829,31 @@ public class Dialog implements DialogInterface, Window.Callback,
// associate search with owner activity if possible (otherwise it will default to
// global search).
- final ComponentName appName = mOwnerActivity == null ? null
- : mOwnerActivity.getComponentName();
+ final ComponentName appName = getAssociatedActivity();
final boolean globalSearch = (appName == null);
searchManager.startSearch(null, false, appName, null, globalSearch);
dismiss();
return true;
}
+ /**
+ * @return The activity associated with this dialog, or null if there is no assocaited activity.
+ */
+ private ComponentName getAssociatedActivity() {
+ Activity activity = mOwnerActivity;
+ Context context = getContext();
+ while (activity == null && context != null) {
+ if (context instanceof Activity) {
+ activity = (Activity) context; // found it!
+ } else {
+ context = (context instanceof ContextWrapper) ?
+ ((ContextWrapper) context).getBaseContext() : // unwrap one level
+ null; // done
+ }
+ }
+ return activity == null ? null : activity.getComponentName();
+ }
+
/**
* Request that key events come to this dialog. Use this if your
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f6ef549..9f505ac 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IIntentSender;
import android.content.IIntentReceiver;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
@@ -30,6 +31,7 @@ import android.content.pm.ProviderInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.Debug;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
@@ -76,10 +78,16 @@ public interface IActivityManager extends IInterface {
public static final int START_CLASS_NOT_FOUND = -2;
public static final int START_FORWARD_AND_REQUEST_CONFLICT = -3;
public static final int START_PERMISSION_DENIED = -4;
+ public static final int START_NOT_ACTIVITY = -5;
+ public static final int START_CANCELED = -6;
public int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo, String resultWho, int requestCode,
boolean onlyIfNeeded, boolean debug) throws RemoteException;
+ public int startActivityIntentSender(IApplicationThread caller,
+ IntentSender intent, Intent fillInIntent, String resolvedType,
+ IBinder resultTo, String resultWho, int requestCode,
+ int flagsMask, int flagsValues) throws RemoteException;
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent) throws RemoteException;
public boolean finishActivity(IBinder token, int code, Intent data)
@@ -101,7 +109,7 @@ public interface IActivityManager extends IInterface {
public void setPersistent(IBinder token, boolean isPersistent) throws RemoteException;
public void attachApplication(IApplicationThread app) throws RemoteException;
/* oneway */
- public void activityIdle(IBinder token) throws RemoteException;
+ public void activityIdle(IBinder token, Configuration config) throws RemoteException;
public void activityPaused(IBinder token, Bundle state) throws RemoteException;
/* oneway */
public void activityStopped(IBinder token,
@@ -125,13 +133,15 @@ public interface IActivityManager extends IInterface {
public void finishOtherInstances(IBinder token, ComponentName className) throws RemoteException;
/* oneway */
public void reportThumbnail(IBinder token,
- Bitmap thumbnail, CharSequence description) throws RemoteException;
+ Bitmap thumbnail, CharSequence description) throws RemoteException;
public ContentProviderHolder getContentProvider(IApplicationThread caller,
- String name) throws RemoteException;
+ String name) throws RemoteException;
public void removeContentProvider(IApplicationThread caller,
- String name) throws RemoteException;
+ String name) throws RemoteException;
public void publishContentProviders(IApplicationThread caller,
- List<ContentProviderHolder> providers) throws RemoteException;
+ List<ContentProviderHolder> providers) throws RemoteException;
+ public PendingIntent getRunningServiceControlPanel(ComponentName service)
+ throws RemoteException;
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType) throws RemoteException;
public int stopService(IApplicationThread caller, Intent service,
@@ -139,7 +149,7 @@ public interface IActivityManager extends IInterface {
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) throws RemoteException;
public void setServiceForeground(ComponentName className, IBinder token,
- boolean isForeground) throws RemoteException;
+ int id, Notification notification, boolean keepNotification) throws RemoteException;
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags) throws RemoteException;
@@ -149,13 +159,15 @@ public interface IActivityManager extends IInterface {
public void unbindFinished(IBinder token, Intent service,
boolean doRebind) throws RemoteException;
/* oneway */
- public void serviceDoneExecuting(IBinder token) throws RemoteException;
+ public void serviceDoneExecuting(IBinder token, int type, int startId,
+ int res) throws RemoteException;
public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
throws RemoteException;
public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
+ public void killApplicationProcess(String processName, int uid) throws RemoteException;
public boolean startInstrumentation(ComponentName className, String profileFile,
int flags, Bundle arguments, IInstrumentationWatcher watcher)
@@ -230,7 +242,6 @@ public interface IActivityManager extends IInterface {
// Special low-level communication with activity manager.
public void startRunning(String pkg, String cls, String action,
String data) throws RemoteException;
- public void systemReady() throws RemoteException;
// Returns 1 if the user wants to debug.
public int handleApplicationError(IBinder app,
int flags, /* 1 == can debug */
@@ -271,6 +282,12 @@ public interface IActivityManager extends IInterface {
public void closeSystemDialogs(String reason) throws RemoteException;
+ public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
+ throws RemoteException;
+
+ public void overridePendingTransition(IBinder token, String packageName,
+ int enterAnim, int exitAnim) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -362,7 +379,7 @@ public interface IActivityManager extends IInterface {
int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
int SET_PERSISTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
int FINISH_SUB_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
- int SYSTEM_READY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
+ int GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
int START_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
int STOP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
int BIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
@@ -427,4 +444,8 @@ public interface IActivityManager extends IInterface {
int START_ACTIVITY_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
int KILL_APPLICATION_WITH_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96;
+ int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97;
+ int KILL_APPLICATION_PROCESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+98;
+ int START_ACTIVITY_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+99;
+ int OVERRIDE_PENDING_TRANSITION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+100;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index c915770..89a52fd 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -25,6 +25,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.Debug;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.IBinder;
@@ -71,7 +72,8 @@ public interface IApplicationThread extends IInterface {
Intent intent, boolean rebind) throws RemoteException;
void scheduleUnbindService(IBinder token,
Intent intent) throws RemoteException;
- void scheduleServiceArgs(IBinder token, int startId, Intent args) throws RemoteException;
+ void scheduleServiceArgs(IBinder token, int startId, int flags, Intent args)
+ throws RemoteException;
void scheduleStopService(IBinder token) throws RemoteException;
static final int DEBUG_OFF = 0;
static final int DEBUG_ON = 1;
@@ -81,6 +83,7 @@ public interface IApplicationThread extends IInterface {
IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
Configuration config, Map<String, IBinder> services) throws RemoteException;
void scheduleExit() throws RemoteException;
+ void scheduleSuicide() throws RemoteException;
void requestThumbnail(IBinder token) throws RemoteException;
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
void updateTimeZone() throws RemoteException;
@@ -88,7 +91,7 @@ public interface IApplicationThread extends IInterface {
void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String data, Bundle extras, boolean ordered)
+ int resultCode, String data, Bundle extras, boolean ordered, boolean sticky)
throws RemoteException;
void scheduleLowMemory() throws RemoteException;
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
@@ -96,6 +99,7 @@ public interface IApplicationThread extends IInterface {
void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
throws RemoteException;
void setSchedulingGroup(int group) throws RemoteException;
+ void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException;
String descriptor = "android.app.IApplicationThread";
@@ -129,4 +133,6 @@ public interface IApplicationThread extends IInterface {
int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
+ int GET_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
+ int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index c1035b6..4d5238c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -24,11 +24,15 @@ import android.content.Intent;
/** {@hide} */
interface INotificationManager
{
+ /** @deprecated use {@link #enqueueNotificationWithTag} instead */
void enqueueNotification(String pkg, int id, in Notification notification, inout int[] idReceived);
+ /** @deprecated use {@link #cancelNotificationWithTag} instead */
void cancelNotification(String pkg, int id);
void cancelAllNotifications(String pkg);
void enqueueToast(String pkg, ITransientNotification callback, int duration);
void cancelToast(String pkg, ITransientNotification callback);
+ void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived);
+ void cancelNotificationWithTag(String pkg, String tag, int id);
}
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index bd72544..a7d6378 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -36,6 +36,16 @@ interface ISearchManager {
boolean globalSearch,
ISearchManagerCallback searchManagerCallback,
int ident);
+
+ void triggerSearch(in String query,
+ in ComponentName launchActivity,
+ in Bundle appSearchData,
+ ISearchManagerCallback searchManagerCallback,
+ int ident);
+
void stopSearch();
+
+
boolean isVisible();
+
}
diff --git a/core/java/android/app/IWallpaperService.aidl b/core/java/android/app/IWallpaperManager.aidl
index a332b1a..69f64a1 100644
--- a/core/java/android/app/IWallpaperService.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -16,21 +16,35 @@
package android.app;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
-import android.app.IWallpaperServiceCallback;
+import android.app.IWallpaperManagerCallback;
+import android.app.WallpaperInfo;
+import android.content.ComponentName;
/** @hide */
-interface IWallpaperService {
+interface IWallpaperManager {
/**
* Set the wallpaper.
*/
- ParcelFileDescriptor setWallpaper();
+ ParcelFileDescriptor setWallpaper(String name);
+
+ /**
+ * Set the live wallpaper.
+ */
+ void setWallpaperComponent(in ComponentName name);
/**
* Get the wallpaper.
*/
- ParcelFileDescriptor getWallpaper(IWallpaperServiceCallback cb);
+ ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
+ out Bundle outParams);
+
+ /**
+ * Get information about a live wallpaper.
+ */
+ WallpaperInfo getWallpaperInfo();
/**
* Clear the wallpaper.
diff --git a/core/java/android/app/IWallpaperServiceCallback.aidl b/core/java/android/app/IWallpaperManagerCallback.aidl
index 6086f40..991b2bc 100644
--- a/core/java/android/app/IWallpaperServiceCallback.aidl
+++ b/core/java/android/app/IWallpaperManagerCallback.aidl
@@ -17,13 +17,13 @@
package android.app;
/**
- * Callback interface used by IWallpaperService to send asynchronous
+ * Callback interface used by IWallpaperManager to send asynchronous
* notifications back to its clients. Note that this is a
* one-way interface so the server does not block waiting for the client.
*
* @hide
*/
-oneway interface IWallpaperServiceCallback {
+oneway interface IWallpaperManagerCallback {
/**
* Called when the wallpaper has changed
*/
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e31f4f8..b8c3aa3 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -24,6 +24,7 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.PerformanceCollector;
import android.os.RemoteException;
import android.os.Debug;
import android.os.IBinder;
@@ -83,10 +84,8 @@ public class Instrumentation {
private List<ActivityWaiter> mWaitingActivities;
private List<ActivityMonitor> mActivityMonitors;
private IInstrumentationWatcher mWatcher;
- private long mPreCpuTime;
- private long mStart;
private boolean mAutomaticPerformanceSnapshots = false;
- private Bundle mPrePerfMetrics = new Bundle();
+ private PerformanceCollector mPerformanceCollector;
private Bundle mPerfMetrics = new Bundle();
public Instrumentation() {
@@ -191,96 +190,21 @@ public class Instrumentation {
public void setAutomaticPerformanceSnapshots() {
mAutomaticPerformanceSnapshots = true;
+ mPerformanceCollector = new PerformanceCollector();
}
public void startPerformanceSnapshot() {
- mStart = 0;
if (!isProfiling()) {
- // Add initial binder counts
- Bundle binderCounts = getBinderCounts();
- for (String key: binderCounts.keySet()) {
- addPerfMetricLong("pre_" + key, binderCounts.getLong(key));
- }
-
- // Force a GC and zero out the performance counters. Do this
- // before reading initial CPU/wall-clock times so we don't include
- // the cost of this setup in our final metrics.
- startAllocCounting();
-
- // Record CPU time up to this point, and start timing. Note: this
- // must happen at the end of this method, otherwise the timing will
- // include noise.
- mStart = SystemClock.uptimeMillis();
- mPreCpuTime = Process.getElapsedCpuTime();
+ mPerformanceCollector.beginSnapshot(null);
}
}
public void endPerformanceSnapshot() {
if (!isProfiling()) {
- // Stop the timing. This must be done first before any other counting is stopped.
- long cpuTime = Process.getElapsedCpuTime();
- long duration = SystemClock.uptimeMillis();
-
- stopAllocCounting();
-
- long nativeMax = Debug.getNativeHeapSize() / 1024;
- long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
- long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
-
- Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
- Debug.getMemoryInfo(memInfo);
-
- Runtime runtime = Runtime.getRuntime();
-
- long dalvikMax = runtime.totalMemory() / 1024;
- long dalvikFree = runtime.freeMemory() / 1024;
- long dalvikAllocated = dalvikMax - dalvikFree;
-
- // Add final binder counts
- Bundle binderCounts = getBinderCounts();
- for (String key: binderCounts.keySet()) {
- addPerfMetricLong(key, binderCounts.getLong(key));
- }
-
- // Add alloc counts
- Bundle allocCounts = getAllocCounts();
- for (String key: allocCounts.keySet()) {
- addPerfMetricLong(key, allocCounts.getLong(key));
- }
-
- addPerfMetricLong("execution_time", duration - mStart);
- addPerfMetricLong("pre_cpu_time", mPreCpuTime);
- addPerfMetricLong("cpu_time", cpuTime - mPreCpuTime);
-
- addPerfMetricLong("native_size", nativeMax);
- addPerfMetricLong("native_allocated", nativeAllocated);
- addPerfMetricLong("native_free", nativeFree);
- addPerfMetricInt("native_pss", memInfo.nativePss);
- addPerfMetricInt("native_private_dirty", memInfo.nativePrivateDirty);
- addPerfMetricInt("native_shared_dirty", memInfo.nativeSharedDirty);
-
- addPerfMetricLong("java_size", dalvikMax);
- addPerfMetricLong("java_allocated", dalvikAllocated);
- addPerfMetricLong("java_free", dalvikFree);
- addPerfMetricInt("java_pss", memInfo.dalvikPss);
- addPerfMetricInt("java_private_dirty", memInfo.dalvikPrivateDirty);
- addPerfMetricInt("java_shared_dirty", memInfo.dalvikSharedDirty);
-
- addPerfMetricInt("other_pss", memInfo.otherPss);
- addPerfMetricInt("other_private_dirty", memInfo.otherPrivateDirty);
- addPerfMetricInt("other_shared_dirty", memInfo.otherSharedDirty);
-
+ mPerfMetrics = mPerformanceCollector.endSnapshot();
}
}
- private void addPerfMetricLong(String key, long value) {
- mPerfMetrics.putLong("performance." + key, value);
- }
-
- private void addPerfMetricInt(String key, int value) {
- mPerfMetrics.putInt("performance." + key, value);
- }
-
/**
* Called when the instrumented application is stopping, after all of the
* normal application cleanup has occurred.
@@ -1468,7 +1392,7 @@ public class Instrumentation {
mWatcher = watcher;
}
- /*package*/ static void checkStartActivityResult(int res, Intent intent) {
+ /*package*/ static void checkStartActivityResult(int res, Object intent) {
if (res >= IActivityManager.START_SUCCESS) {
return;
}
@@ -1476,10 +1400,10 @@ public class Instrumentation {
switch (res) {
case IActivityManager.START_INTENT_NOT_RESOLVED:
case IActivityManager.START_CLASS_NOT_FOUND:
- if (intent.getComponent() != null)
+ if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
- + intent.getComponent().toShortString()
+ + ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
@@ -1489,6 +1413,9 @@ public class Instrumentation {
case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
throw new AndroidRuntimeException(
"FORWARD_RESULT_FLAG used while also requesting a result");
+ case IActivityManager.START_NOT_ACTIVITY:
+ throw new IllegalArgumentException(
+ "PendingIntent is not an activity");
default:
throw new AndroidRuntimeException("Unknown error code "
+ res + " when starting " + intent);
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 2b12a2a..804c8eb 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -18,6 +18,7 @@ public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
+ private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
@@ -36,6 +37,19 @@ public abstract class IntentService extends Service {
mName = name;
}
+ /**
+ * Control redelivery of intents. If called with true,
+ * {@link #onStartCommand(Intent, int, int)} will return
+ * {@link Service#START_REDELIVER_INTENT} instead of
+ * {@link Service#START_NOT_STICKY}, so that if this service's process
+ * is called while it is executing the Intent in
+ * {@link #onHandleIntent(Intent)}, then when later restarted the same Intent
+ * will be re-delivered to it, to retry its execution.
+ */
+ public void setIntentRedelivery(boolean enabled) {
+ mRedelivery = enabled;
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -48,7 +62,6 @@ public abstract class IntentService extends Service {
@Override
public void onStart(Intent intent, int startId) {
- super.onStart(intent, startId);
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
@@ -56,6 +69,12 @@ public abstract class IntentService extends Service {
}
@Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ onStart(intent, startId);
+ return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
+ }
+
+ @Override
public void onDestroy() {
mServiceLooper.quit();
}
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index d788c43..0ece2fc 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -18,6 +18,7 @@ package android.app;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
@@ -52,9 +53,9 @@ import java.util.List;
*
*/
public abstract class LauncherActivity extends ListActivity {
-
Intent mIntent;
PackageManager mPackageManager;
+ IconResizer mIconResizer;
/**
* An item in the list
@@ -70,13 +71,17 @@ public abstract class LauncherActivity extends ListActivity {
ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
this.resolveInfo = resolveInfo;
label = resolveInfo.loadLabel(pm);
- if (label == null && resolveInfo.activityInfo != null) {
+ ComponentInfo ci = resolveInfo.activityInfo;
+ if (ci == null) ci = resolveInfo.serviceInfo;
+ if (label == null && ci != null) {
label = resolveInfo.activityInfo.name;
}
- icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
- packageName = resolveInfo.activityInfo.applicationInfo.packageName;
- className = resolveInfo.activityInfo.name;
+ if (resizer != null) {
+ icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
+ }
+ packageName = ci.applicationInfo.packageName;
+ className = ci.name;
}
public ListItem() {
@@ -90,13 +95,15 @@ public abstract class LauncherActivity extends ListActivity {
private final Object lock = new Object();
private ArrayList<ListItem> mOriginalValues;
+ protected final IconResizer mIconResizer;
protected final LayoutInflater mInflater;
protected List<ListItem> mActivitiesList;
private Filter mFilter;
- public ActivityAdapter() {
+ public ActivityAdapter(IconResizer resizer) {
+ mIconResizer = resizer;
mInflater = (LayoutInflater) LauncherActivity.this.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
mActivitiesList = makeListItems();
@@ -151,6 +158,10 @@ public abstract class LauncherActivity extends ListActivity {
private void bindView(View view, ListItem item) {
TextView text = (TextView) view;
text.setText(item.label);
+ if (item.icon == null) {
+ item.icon = mIconResizer.createIconThumbnail(
+ item.resolveInfo.loadIcon(getPackageManager()));
+ }
text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
}
@@ -325,12 +336,13 @@ public abstract class LauncherActivity extends ListActivity {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarIndeterminateVisibility(true);
- setContentView(com.android.internal.R.layout.activity_list);
-
+ onSetContentView();
+ mIconResizer = new IconResizer();
+
mIntent = new Intent(getTargetIntent());
mIntent.setComponent(null);
- mAdapter = new ActivityAdapter();
+ mAdapter = new ActivityAdapter(mIconResizer);
setListAdapter(mAdapter);
getListView().setTextFilterEnabled(true);
@@ -338,10 +350,17 @@ public abstract class LauncherActivity extends ListActivity {
setProgressBarIndeterminateVisibility(false);
}
+ /**
+ * Override to call setContentView() with your own content view to
+ * customize the list layout.
+ */
+ protected void onSetContentView() {
+ setContentView(com.android.internal.R.layout.activity_list);
+ }
+
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
- Intent intent = ((ActivityAdapter)mAdapter).intentForPosition(position);
-
+ Intent intent = intentForPosition(position);
startActivity(intent);
}
@@ -374,21 +393,26 @@ public abstract class LauncherActivity extends ListActivity {
}
/**
+ * Perform query on package manager for list items. The default
+ * implementation queries for activities.
+ */
+ protected List<ResolveInfo> onQueryPackageManager(Intent queryIntent) {
+ return mPackageManager.queryIntentActivities(queryIntent, /* no flags */ 0);
+ }
+
+ /**
* Perform the query to determine which results to show and return a list of them.
*/
public List<ListItem> makeListItems() {
// Load all matching activities and sort correctly
- List<ResolveInfo> list = mPackageManager.queryIntentActivities(mIntent,
- /* no flags */ 0);
+ List<ResolveInfo> list = onQueryPackageManager(mIntent);
Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager));
- IconResizer resizer = new IconResizer();
-
ArrayList<ListItem> result = new ArrayList<ListItem>(list.size());
int listSize = list.size();
for (int i = 0; i < listSize; i++) {
ResolveInfo resolveInfo = list.get(i);
- result.add(new ListItem(mPackageManager, resolveInfo, resizer));
+ result.add(new ListItem(mPackageManager, resolveInfo, null));
}
return result;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9834c75..be5a7d3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -257,6 +257,13 @@ public class Notification implements Parcelable
*/
public static final int FLAG_NO_CLEAR = 0x00000020;
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set if this notification represents a currently running service. This
+ * will normally be set for you by {@link Service#startForeground}.
+ */
+ public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
+
public int flags;
/**
@@ -458,7 +465,9 @@ public class Notification implements Parcelable
sb.append(this.vibrate[i]);
sb.append(',');
}
- sb.append(this.vibrate[N]);
+ if (N != -1) {
+ sb.append(this.vibrate[N]);
+ }
sb.append("]");
} else if ((this.defaults & DEFAULT_VIBRATE) != 0) {
sb.append("default");
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 39edab7..6fe12fc 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -61,7 +61,8 @@ public class NotificationManager
private static INotificationManager sService;
- static private INotificationManager getService()
+ /** @hide */
+ static public INotificationManager getService()
{
if (sService != null) {
return sService;
@@ -86,12 +87,27 @@ public class NotificationManager
*/
public void notify(int id, Notification notification)
{
+ notify(null, id, notification);
+ }
+
+ /**
+ * Persistent notification on the status bar,
+ *
+ * @param tag An string identifier for this notification unique within your
+ * application.
+ * @param notification A {@link Notification} object describing how to
+ * notify the user, other than the view you're providing. Must not be null.
+ * @return the id of the notification that is associated with the string identifier that
+ * can be used to cancel the notification
+ */
+ public void notify(String tag, int id, Notification notification)
+ {
int[] idOut = new int[1];
INotificationManager service = getService();
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
try {
- service.enqueueNotification(pkg, id, notification, idOut);
+ service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
@@ -106,11 +122,21 @@ public class NotificationManager
*/
public void cancel(int id)
{
+ cancel(null, id);
+ }
+
+ /**
+ * Cancel a previously shown notification. If it's transient, the view
+ * will be hidden. If it's persistent, it will be removed from the status
+ * bar.
+ */
+ public void cancel(String tag, int id)
+ {
INotificationManager service = getService();
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
try {
- service.cancelNotification(pkg, id);
+ service.cancelNotificationWithTag(pkg, tag, id);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index f7479bc..be1dc4a 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -147,7 +147,7 @@ public final class PendingIntent implements Parcelable {
mHandler = handler;
}
public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean serialized) {
+ String data, Bundle extras, boolean serialized, boolean sticky) {
mIntent = intent;
mResultCode = resultCode;
mResultData = data;
@@ -519,7 +519,8 @@ public final class PendingIntent implements Parcelable {
mTarget = IIntentSender.Stub.asInterface(target);
}
- /*package*/ IIntentSender getTarget() {
+ /** @hide */
+ public IIntentSender getTarget() {
return mTarget;
}
}
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 5844079..e5a769b 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -30,7 +30,6 @@ import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
-import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -51,7 +50,6 @@ import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.KeyEvent;
-import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -65,9 +63,9 @@ import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
-import android.widget.ListAdapter;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
@@ -98,6 +96,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// The extra key used in an intent to the speech recognizer for in-app voice search.
private static final String EXTRA_CALLING_PACKAGE = "calling_package";
+
+ // The string used for privateImeOptions to identify to the IME that it should not show
+ // a microphone button since one already exists in the search dialog.
+ private static final String IME_OPTION_NO_MICROPHONE = "nm";
private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12;
private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7;
@@ -129,8 +131,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
private ArrayList<ComponentName> mPreviousComponents;
// For voice searching
- private Intent mVoiceWebSearchIntent;
- private Intent mVoiceAppSearchIntent;
+ private final Intent mVoiceWebSearchIntent;
+ private final Intent mVoiceAppSearchIntent;
// support for AutoCompleteTextView suggestions display
private SuggestionsAdapter mSuggestionsAdapter;
@@ -158,18 +160,25 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
*/
public SearchDialog(Context context) {
super(context, com.android.internal.R.style.Theme_GlobalSearchBar);
+
+ // Save voice intent for later queries/launching
+ mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
+ mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
+
+ mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
/**
- * We create the search dialog just once, and it stays around (hidden)
- * until activated by the user.
+ * Create the search dialog and any resources that are used for the
+ * entire lifetime of the dialog.
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(com.android.internal.R.layout.search_bar);
-
Window theWindow = getWindow();
WindowManager.LayoutParams lp = theWindow.getAttributes();
lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR;
@@ -182,7 +191,21 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
theWindow.setAttributes(lp);
+ // Touching outside of the search dialog will dismiss it
+ setCanceledOnTouchOutside(true);
+ }
+
+ /**
+ * We recreate the dialog view each time it becomes visible so as to limit
+ * the scope of any problems with the contained resources.
+ */
+ private void createContentView() {
+ setContentView(com.android.internal.R.layout.search_bar);
+
// get the view elements for local access
+ SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar);
+ searchBar.setSearchDialog(this);
+
mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
mSearchAutoComplete = (SearchAutoComplete)
findViewById(com.android.internal.R.id.search_src_text);
@@ -192,7 +215,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mSearchPlate = findViewById(com.android.internal.R.id.search_plate);
mWorkingSpinner = getContext().getResources().
getDrawable(com.android.internal.R.drawable.search_spinner);
-
+ mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
+ null, null, mWorkingSpinner, null);
+ setWorking(false);
+
// attach listeners
mSearchAutoComplete.addTextChangedListener(mTextWatcher);
mSearchAutoComplete.setOnKeyListener(mTextKeyListener);
@@ -203,25 +229,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mVoiceButton.setOnClickListener(mVoiceButtonClickListener);
mVoiceButton.setOnKeyListener(mButtonsKeyListener);
- mSearchAutoComplete.setSearchDialog(this);
-
// pre-hide all the extraneous elements
mBadgeLabel.setVisibility(View.GONE);
// Additional adjustments to make Dialog work for Search
-
- // Touching outside of the search dialog will dismiss it
- setCanceledOnTouchOutside(true);
-
- // Save voice intent for later queries/launching
- mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
- mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
- RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
-
- mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
- mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
mSearchAutoCompleteImeOptions = mSearchAutoComplete.getImeOptions();
}
@@ -356,9 +367,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// isDefaultSearchable() should always give the same result.
mGlobalSearchMode = globalSearch || searchManager.isDefaultSearchable(mSearchable);
mActivityContext = mSearchable.getActivityContext(getContext());
-
+
// show the dialog. this will call onStart().
- if (!isShowing()) {
+ if (!isShowing()) {
+ // Recreate the search bar view every time the dialog is shown, to get rid
+ // of any bad state in the AutoCompleteTextView etc
+ createContentView();
+
// The Dialog uses a ContextThemeWrapper for the context; use this to change the
// theme out from underneath us, between the global search theme and the in-app
// search theme. They are identical except that the global search theme does not
@@ -408,15 +423,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @param working true to show spinner, false to hide spinner
*/
public void setWorking(boolean working) {
- if (working) {
- mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
- null, null, mWorkingSpinner, null);
- ((Animatable) mWorkingSpinner).start();
- } else {
- mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
- null, null, null, null);
- ((Animatable) mWorkingSpinner).stop();
- }
+ mWorkingSpinner.setAlpha(working ? 255 : 0);
+ mWorkingSpinner.setVisible(working, false);
+ mWorkingSpinner.invalidateSelf();
}
/**
@@ -502,6 +511,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
updateSearchAppIcon();
updateSearchBadge();
updateQueryHint();
+ mSearchAutoComplete.showDropDownAfterLayout();
}
}
@@ -537,6 +547,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mSearchAutoComplete.setInputType(inputType);
mSearchAutoCompleteImeOptions = mSearchable.getImeOptions();
mSearchAutoComplete.setImeOptions(mSearchAutoCompleteImeOptions);
+
+ // If the search dialog is going to show a voice search button, then don't let
+ // the soft keyboard display a microphone button if it would have otherwise.
+ if (mSearchable.getVoiceSearchEnabled()) {
+ mSearchAutoComplete.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
+ } else {
+ mSearchAutoComplete.setPrivateImeOptions(null);
+ }
}
}
@@ -740,17 +758,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
return false;
}
- // handle back key to go back to previous searchable, etc.
- if (handleBackKey(keyCode, event)) {
+ if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() == 0) {
+ event.startTracking();
+ // Consume search key for later use.
return true;
}
-
- if (keyCode == KeyEvent.KEYCODE_SEARCH) {
- // If the search key is pressed, toggle between global and in-app search. If we are
- // currently doing global search and there is no in-app search context to toggle to,
- // just don't do anything.
- return toggleGlobalSearch();
- }
// if it's an action specified by the searchable activity, launch the
// entered query with the action key
@@ -759,8 +771,26 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
launchQuerySearch(keyCode, actionKey.getQueryActionMsg());
return true;
}
-
- return false;
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (DBG) Log.d(LOG_TAG, "onKeyUp(" + keyCode + "," + event + ")");
+ if (mSearchable == null) {
+ return false;
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_SEARCH && event.isTracking()
+ && !event.isCanceled()) {
+ // If the search key is pressed, toggle between global and in-app search. If we are
+ // currently doing global search and there is no in-app search context to toggle to,
+ // just don't do anything.
+ return toggleGlobalSearch();
+ }
+
+ return super.onKeyUp(keyCode, event);
}
/**
@@ -1120,7 +1150,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
/**
* Launch a search for the text in the query text field.
*/
- protected void launchQuerySearch() {
+ public void launchQuerySearch() {
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
}
@@ -1136,7 +1166,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
String query = mSearchAutoComplete.getText().toString();
String action = mGlobalSearchMode ? Intent.ACTION_WEB_SEARCH : Intent.ACTION_SEARCH;
Intent intent = createIntent(action, null, null, query, null,
- actionKey, actionMsg);
+ actionKey, actionMsg, null);
+ // Allow GlobalSearch to log and create shortcut for searches launched by
+ // the search button, enter key or an action key.
+ if (mGlobalSearchMode) {
+ mSuggestionsAdapter.reportSearch(query);
+ }
launchIntent(intent);
}
@@ -1169,7 +1204,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// report back about the click
if (mGlobalSearchMode) {
// in global search mode, do it via cursor
- mSuggestionsAdapter.callCursorOnClick(c, position);
+ mSuggestionsAdapter.callCursorOnClick(c, position, actionKey, actionMsg);
} else if (intent != null
&& mPreviousComponents != null
&& !mPreviousComponents.isEmpty()) {
@@ -1206,7 +1241,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction());
cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString());
cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME,
- intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY));
+ intent.getComponent().flattenToShortString());
// ensure the icons will work for global search
cv.put(SearchManager.SUGGEST_COLUMN_ICON_1,
@@ -1292,6 +1327,12 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// intent, and to avoid the extra step of going through GlobalSearch.
if (mGlobalSearchMode) {
launchGlobalSearchIntent(intent);
+ if (mStoredComponentName != null) {
+ // If we're embedded in an application, dismiss the dialog.
+ // This ensures that if the intent is handled by the current
+ // activity, it's not obscured by the dialog.
+ dismiss();
+ }
} else {
// If the intent was created from a suggestion, it will always have an explicit
// component here.
@@ -1463,6 +1504,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
/**
+ * Checks if there are any previous searchable components in the history stack.
+ */
+ private boolean hasPreviousComponent() {
+ return mPreviousComponents != null && !mPreviousComponents.isEmpty();
+ }
+
+ /**
* Saves the previous component that was searched, so that we can go
* back to it.
*/
@@ -1480,14 +1528,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* no previous component.
*/
private ComponentName popPreviousComponent() {
- if (mPreviousComponents == null) {
- return null;
- }
- int size = mPreviousComponents.size();
- if (size == 0) {
+ if (!hasPreviousComponent()) {
return null;
}
- return mPreviousComponents.remove(size - 1);
+ return mPreviousComponents.remove(mPreviousComponents.size() - 1);
}
/**
@@ -1500,17 +1544,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (previous == null) {
return false;
}
+
if (!show(previous, mAppSearchData, false)) {
Log.w(LOG_TAG, "Failed to switch to source " + previous);
return false;
}
-
+
// must touch text to trigger suggestions
// TODO: should this be the text as it was when the user left
// the source that we are now going back to?
String query = mSearchAutoComplete.getText().toString();
setUserQuery(query);
-
return true;
}
@@ -1563,9 +1607,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
+ String mode = mGlobalSearchMode ? SearchManager.MODE_GLOBAL_SEARCH_SUGGESTION : null;
return createIntent(action, dataUri, extraData, query, componentName, actionKey,
- actionMsg);
+ actionMsg, mode);
} catch (RuntimeException e ) {
int rowNum;
try { // be really paranoid now
@@ -1591,13 +1636,21 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
* @param actionMsg The message for the action key that was pressed,
* or <code>null</code> if none.
+ * @param mode The search mode, one of the acceptable values for
+ * {@link SearchManager#SEARCH_MODE}, or {@code null}.
* @return The intent.
*/
private Intent createIntent(String action, Uri data, String extraData, String query,
- String componentName, int actionKey, String actionMsg) {
+ String componentName, int actionKey, String actionMsg, String mode) {
// Now build the Intent
Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // We need CLEAR_TOP to avoid reusing an old task that has other activities
+ // on top of the one we want. We don't want to do this in in-app search though,
+ // as it can be destructive to the activity stack.
+ if (mGlobalSearchMode) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ }
if (data != null) {
intent.setData(data);
}
@@ -1618,6 +1671,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
}
+ if (mode != null) {
+ intent.putExtra(SearchManager.SEARCH_MODE, mode);
+ }
// Only allow 3rd-party intents from GlobalSearch
if (!mGlobalSearchMode) {
intent.setComponent(mSearchable.getSearchActivity());
@@ -1648,15 +1704,59 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
return result;
}
-
+
+ /**
+ * The root element in the search bar layout. This is a custom view just to override
+ * the handling of the back button.
+ */
+ public static class SearchBar extends LinearLayout {
+
+ private SearchDialog mSearchDialog;
+
+ public SearchBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SearchBar(Context context) {
+ super(context);
+ }
+
+ public void setSearchDialog(SearchDialog searchDialog) {
+ mSearchDialog = searchDialog;
+ }
+
+ /**
+ * Overrides the handling of the back key to move back to the previous sources or dismiss
+ * the search dialog, instead of dismissing the input method.
+ */
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (DBG) Log.d(LOG_TAG, "onKeyPreIme(" + event + ")");
+ if (mSearchDialog != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (state != null) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getRepeatCount() == 0) {
+ state.startTracking(event, this);
+ return true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP
+ && !event.isCanceled() && state.isTracking(event)) {
+ mSearchDialog.onBackPressed();
+ return true;
+ }
+ }
+ }
+ return super.dispatchKeyEventPreIme(event);
+ }
+ }
+
/**
* Local subclass for AutoCompleteTextView.
*/
public static class SearchAutoComplete extends AutoCompleteTextView {
private int mThreshold;
- private SearchDialog mSearchDialog;
-
+
public SearchAutoComplete(Context context) {
super(context);
mThreshold = getThreshold();
@@ -1672,10 +1772,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mThreshold = getThreshold();
}
- private void setSearchDialog(SearchDialog searchDialog) {
- mSearchDialog = searchDialog;
- }
-
@Override
public void setThreshold(int threshold) {
super.setThreshold(threshold);
@@ -1729,54 +1825,26 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
return mThreshold <= 0 || super.enoughToFilter();
}
- /**
- * {@link AutoCompleteTextView#onKeyPreIme(int, KeyEvent)}) dismisses the drop-down on BACK,
- * so we must override this method to modify the BACK behavior.
- */
- @Override
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (mSearchDialog.mSearchable == null) {
- return false;
- }
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
- if (mSearchDialog.backToPreviousComponent()) {
- return true;
- }
- // If the drop-down obscures the keyboard, the user wouldn't see anything
- // happening when pressing back, so we dismiss the entire dialog instead.
- //
- // also: if there is no text entered, we also want to dismiss the whole dialog,
- // not just the soft keyboard. the exception to this is if there are shortcuts
- // that aren't displayed (e.g are being obscured by the soft keyboard); in that
- // case we want to dismiss the soft keyboard so the user can see the rest of the
- // shortcuts.
- if (isInputMethodNotNeeded() ||
- (isEmpty() && getDropDownChildCount() >= getAdapterCount())) {
- mSearchDialog.cancel();
- return true;
- }
- return false; // will dismiss soft keyboard if necessary
- }
- return false;
- }
+ }
- private int getAdapterCount() {
- final ListAdapter adapter = getAdapter();
- return adapter == null ? 0 : adapter.getCount();
+ @Override
+ public void onBackPressed() {
+ // If the input method is covering the search dialog completely,
+ // e.g. in landscape mode with no hard keyboard, dismiss just the input method
+ InputMethodManager imm = (InputMethodManager)getContext()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null && imm.isFullscreenMode() &&
+ imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0)) {
+ return;
}
- }
-
- protected boolean handleBackKey(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
- if (backToPreviousComponent()) {
- return true;
- }
+ // Otherwise, go back to any previous source (e.g. back to QSB when
+ // pivoted into a source.
+ if (!backToPreviousComponent()) {
+ // If no previous source, close search dialog
cancel();
- return true;
}
- return false;
}
-
+
/**
* Implements OnItemClickListener
*/
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 2245562..7f5a1e7 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -27,6 +27,7 @@ import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.server.search.SearchableInfo;
+import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
@@ -329,8 +330,8 @@ import java.util.List;
* you'll need to update your searchable activity (or other activities) to receive the intents
* as you've defined them.</li>
* <li>Implement a Content Provider that provides suggestions. If you already have one, and it
- * has access to your suggestions data. If not, you'll have to create one.
- * You'll also provide information about your Content Provider in your
+ * has access to your suggestions data, you can use that provider. If not, you'll have to create
+ * one. You'll also provide information about your Content Provider in your
* package's <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a>.</li>
* <li>Update your searchable activity's XML configuration file. There are two categories of
* information used for suggestions:
@@ -768,8 +769,11 @@ import java.util.List;
* </tr>
*
* <tr><th>android:icon</th>
- * <td>If provided, this icon will be used <i>in place</i> of the label string. This
- * is provided in order to present logos or other non-textual banners.</td>
+ * <td>If provided, this icon will be shown in place of the label above the search box.
+ * This is a reference to a drawable (icon) resource. Note that the application icon
+ * is also used as an icon to the left of the search box and you cannot modify this
+ * behavior, so including the icon attribute is unecessary and this may be
+ * deprecated in the future.</td>
* <td align="center">No</td>
* </tr>
*
@@ -778,11 +782,6 @@ import java.util.List;
* entered.</td>
* <td align="center">No</td>
* </tr>
- *
- * <tr><th>android:searchButtonText</th>
- * <td>If provided, this text will replace the default text in the "Search" button.</td>
- * <td align="center">No</td>
- * </tr>
*
* <tr><th>android:searchMode</th>
* <td>If provided and non-zero, sets additional modes for control of the search
@@ -791,15 +790,17 @@ import java.util.List;
* <tbody>
* <tr><th>showSearchLabelAsBadge</th>
* <td>If set, this flag enables the display of the search target (label)
- * within the search bar. If this flag and showSearchIconAsBadge
+ * above the search box. If this flag and showSearchIconAsBadge
* (see below) are both not set, no badge will be shown.</td>
* </tr>
* <tr><th>showSearchIconAsBadge</th>
- * <td>If set, this flag enables the display of the search target (icon) within
- * the search bar. If this flag and showSearchLabelAsBadge
+ * <td>If set, this flag enables the display of the search target (icon)
+ * above the search box. If this flag and showSearchLabelAsBadge
* (see above) are both not set, no badge will be shown. If both flags
* are set, showSearchIconAsBadge has precedence and the icon will be
- * shown.</td>
+ * shown. Because the application icon is now used to the left of the
+ * search box by default, using this search mode is no longer necessary
+ * and may be deprecated in the future.</td>
* </tr>
* <tr><th>queryRewriteFromData</th>
* <td>If set, this flag causes the suggestion column SUGGEST_COLUMN_INTENT_DATA
@@ -1180,7 +1181,7 @@ import java.util.List;
* Bundle appData = new Bundle();
* appData.put...();
* appData.put...();
- * startSearch(null, false, appData);
+ * startSearch(null, false, appData, false);
* return true;
* }</pre>
*
@@ -1288,6 +1289,25 @@ public class SearchManager
public final static String SOURCE = "source";
/**
+ * Intent extra data key: Use {@link android.content.Intent#getBundleExtra
+ * content.Intent.getBundleExtra(SEARCH_MODE)} to get the search mode used
+ * to launch the intent.
+ * The only current value for this is {@link #MODE_GLOBAL_SEARCH_SUGGESTION}.
+ *
+ * @hide
+ */
+ public final static String SEARCH_MODE = "search_mode";
+
+ /**
+ * Value for the {@link #SEARCH_MODE} key.
+ * This is used if the intent was launched by clicking a suggestion in global search
+ * mode (Quick Search Box).
+ *
+ * @hide
+ */
+ public static final String MODE_GLOBAL_SEARCH_SUGGESTION = "global_search_suggestion";
+
+ /**
* Intent extra data key: Use this key with Intent.ACTION_SEARCH and
* {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()}
* to obtain the keycode that the user used to trigger this query. It will be zero if the
@@ -1342,6 +1362,10 @@ public class SearchManager
= "DialogCursorProtocol.CLICK.sendPosition";
public final static String CLICK_SEND_MAX_DISPLAY_POS
= "DialogCursorProtocol.CLICK.sendDisplayPosition";
+ public final static String CLICK_SEND_ACTION_KEY
+ = "DialogCursorProtocol.CLICK.sendActionKey";
+ public final static String CLICK_SEND_ACTION_MSG
+ = "DialogCursorProtocol.CLICK.sendActionMsg";
public final static String CLICK_RECEIVE_SELECTED_POS
= "DialogCursorProtocol.CLICK.receiveSelectedPosition";
@@ -1349,6 +1373,14 @@ public class SearchManager
* When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed.
*/
public final static int THRESH_HIT = 3;
+
+ /**
+ * When a search is started without using a suggestion.
+ */
+ public final static int SEARCH = 4;
+ public final static String SEARCH_SEND_MAX_DISPLAY_POS
+ = "DialogCursorProtocol.SEARCH.sendDisplayPosition";
+ public final static String SEARCH_SEND_QUERY = "DialogCursorProtocol.SEARCH.query";
}
/**
@@ -1559,6 +1591,12 @@ public class SearchManager
public final static String SUGGEST_NEVER_MAKE_SHORTCUT = "_-1";
/**
+ * Query parameter added to suggestion queries to limit the number of suggestions returned.
+ * This limit is only advisory and suggestion providers may chose to ignore it.
+ */
+ public final static String SUGGEST_PARAMETER_LIMIT = "limit";
+
+ /**
* If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
* the search dialog will switch to a different suggestion source when the
* suggestion is clicked.
@@ -1636,7 +1674,17 @@ public class SearchManager
private final Context mContext;
+ /**
+ * compact representation of the activity associated with this search manager so
+ * we can say who we are when starting search. the search managerservice, in turn,
+ * uses this to properly handle the back stack.
+ */
private int mIdent;
+
+ /**
+ * The package associated with this seach manager.
+ */
+ private String mAssociatedPackage;
// package private since they are used by the inner class SearchManagerCallback
/* package */ final Handler mHandler;
@@ -1656,11 +1704,15 @@ public class SearchManager
return mIdent != 0;
}
- /*package*/ void setIdent(int ident) {
+ /*package*/ void setIdent(int ident, ComponentName component) {
if (mIdent != 0) {
throw new IllegalStateException("mIdent already set");
}
+ if (component == null) {
+ throw new IllegalArgumentException("component must be non-null");
+ }
mIdent = ident;
+ mAssociatedPackage = component.getPackageName();
}
/**
@@ -1710,12 +1762,50 @@ public class SearchManager
boolean globalSearch) {
if (mIdent == 0) throw new IllegalArgumentException(
"Called from outside of an Activity context");
+ if (!globalSearch && !mAssociatedPackage.equals(launchActivity.getPackageName())) {
+ Log.w(TAG, "invoking app search on a different package " +
+ "not associated with this search manager");
+ }
try {
// activate the search manager and start it up!
mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData,
globalSearch, mSearchManagerCallback, mIdent);
} catch (RemoteException ex) {
- Log.e(TAG, "startSearch() failed: " + ex);
+ Log.e(TAG, "startSearch() failed.", ex);
+ }
+ }
+
+ /**
+ * Similar to {@link #startSearch} but actually fires off the search query after invoking
+ * the search dialog. Made available for testing purposes.
+ *
+ * @param query The query to trigger. If empty, request will be ignored.
+ * @param launchActivity The ComponentName of the activity that has launched this search.
+ * @param appSearchData An application can insert application-specific
+ * context here, in order to improve quality or specificity of its own
+ * searches. This data will be returned with SEARCH intent(s). Null if
+ * no extra data is required.
+ *
+ * @see #startSearch
+ */
+ public void triggerSearch(String query,
+ ComponentName launchActivity,
+ Bundle appSearchData) {
+ if (mIdent == 0) throw new IllegalArgumentException(
+ "Called from outside of an Activity context");
+ if (!mAssociatedPackage.equals(launchActivity.getPackageName())) {
+ throw new IllegalArgumentException("invoking app search on a different package " +
+ "not associated with this search manager");
+ }
+ if (query == null || TextUtils.getTrimmedLength(query) == 0) {
+ Log.w(TAG, "triggerSearch called with empty query, ignoring.");
+ return;
+ }
+ try {
+ mService.triggerSearch(query, launchActivity, appSearchData, mSearchManagerCallback,
+ mIdent);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "triggerSearch() failed.", ex);
}
}
@@ -1836,6 +1926,7 @@ public class SearchManager
/**
* @deprecated This method is an obsolete internal implementation detail. Do not use.
*/
+ @Deprecated
public void onCancel(DialogInterface dialog) {
throw new UnsupportedOperationException();
}
@@ -1843,6 +1934,7 @@ public class SearchManager
/**
* @deprecated This method is an obsolete internal implementation detail. Do not use.
*/
+ @Deprecated
public void onDismiss(DialogInterface dialog) {
throw new UnsupportedOperationException();
}
@@ -1889,6 +1981,21 @@ public class SearchManager
* @hide because SearchableInfo is not part of the API.
*/
public Cursor getSuggestions(SearchableInfo searchable, String query) {
+ return getSuggestions(searchable, query, -1);
+ }
+
+ /**
+ * Gets a cursor with search suggestions.
+ *
+ * @param searchable Information about how to get the suggestions.
+ * @param query The search text entered (so far).
+ * @param limit The query limit to pass to the suggestion provider. This is advisory,
+ * the returned cursor may contain more rows. Pass {@code -1} for no limit.
+ * @return a cursor with suggestions, or <code>null</null> the suggestion query failed.
+ *
+ * @hide because SearchableInfo is not part of the API.
+ */
+ public Cursor getSuggestions(SearchableInfo searchable, String query, int limit) {
if (searchable == null) {
return null;
}
@@ -1900,7 +2007,9 @@ public class SearchManager
Uri.Builder uriBuilder = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
- .authority(authority);
+ .authority(authority)
+ .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
+ .fragment(""); // TODO: Remove, workaround for a bug in Uri.writeToParcel()
// if content path provided, insert it now
final String contentPath = searchable.getSuggestPath();
@@ -1908,7 +2017,7 @@ public class SearchManager
uriBuilder.appendEncodedPath(contentPath);
}
- // append standard suggestion query path
+ // append standard suggestion query path
uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
// get the query selection, may be null
@@ -1921,10 +2030,11 @@ public class SearchManager
uriBuilder.appendPath(query);
}
- Uri uri = uriBuilder
- .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
- .fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel()
- .build();
+ if (limit > 0) {
+ uriBuilder.appendQueryParameter(SUGGEST_PARAMETER_LIMIT, String.valueOf(limit));
+ }
+
+ Uri uri = uriBuilder.build();
// finally, make the query
return mContext.getContentResolver().query(uri, null, selection, selArgs, null);
@@ -2000,4 +2110,4 @@ public class SearchManager
Thread thread = Thread.currentThread();
Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")");
}
-}
+} \ No newline at end of file
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index d2fb605..30e1712 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -22,8 +22,10 @@ import android.content.Intent;
import android.content.ContextWrapper;
import android.content.Context;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.RemoteException;
import android.os.IBinder;
+import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -62,18 +64,28 @@ import java.io.PrintWriter;
* <p>There are two reasons that a service can be run by the system. If someone
* calls {@link android.content.Context#startService Context.startService()} then the system will
* retrieve the service (creating it and calling its {@link #onCreate} method
- * if needed) and then call its {@link #onStart} method with the
+ * if needed) and then call its {@link #onStartCommand} method with the
* arguments supplied by the client. The service will at this point continue
* running until {@link android.content.Context#stopService Context.stopService()} or
* {@link #stopSelf()} is called. Note that multiple calls to
* Context.startService() do not nest (though they do result in multiple corresponding
- * calls to onStart()), so no matter how many times it is started a service
- * will be stopped once Context.stopService() or stopSelf() is called.
+ * calls to onStartCommand()), so no matter how many times it is started a service
+ * will be stopped once Context.stopService() or stopSelf() is called; however,
+ * services can use their {@link #stopSelf(int)} method to ensure the service is
+ * not stopped until started intents have been processed.
+ *
+ * <p>For started services, there are two additional major modes of operation
+ * they can decide to run in, depending on the value they return from
+ * onStartCommand(): {@link #START_STICKY} is used for services that are
+ * explicitly started and stopped as needed, while {@link #START_NOT_STICKY}
+ * or {@link #START_REDELIVER_INTENT} are used for services that should only
+ * remain running while processing any commands sent to them. See the linked
+ * documentation for more detail on the semantics.
*
* <p>Clients can also use {@link android.content.Context#bindService Context.bindService()} to
* obtain a persistent connection to a service. This likewise creates the
* service if it is not already running (calling {@link #onCreate} while
- * doing so), but does not call onStart(). The client will receive the
+ * doing so), but does not call onStartCommand(). The client will receive the
* {@link android.os.IBinder} object that the service returns from its
* {@link #onBind} method, allowing the client to then make calls back
* to the service. The service will remain running as long as the connection
@@ -120,7 +132,7 @@ import java.io.PrintWriter;
*
* <ul>
* <li><p>If the service is currently executing code in its
- * {@link #onCreate onCreate()}, {@link #onStart onStart()},
+ * {@link #onCreate onCreate()}, {@link #onStartCommand onStartCommand()},
* or {@link #onDestroy onDestroy()} methods, then the hosting process will
* be a foreground process to ensure this code can execute without
* being killed.
@@ -133,16 +145,22 @@ import java.io.PrintWriter;
* process is never less important than the most important client. That is,
* if one of its clients is visible to the user, then the service itself is
* considered to be visible.
+ * <li><p>A started service can use the {@link #startForeground(int, Notification)}
+ * API to put the service in a foreground state, where the system considers
+ * it to be something the user is actively aware of and thus not a candidate
+ * for killing when low on memory. (It is still theoretically possible for
+ * the service to be killed under extreme memory pressure from the current
+ * foreground application, but in practice this should not be a concern.)
* </ul>
*
* <p>Note this means that most of the time your service is running, it may
* be killed by the system if it is under heavy memory pressure. If this
* happens, the system will later try to restart the service. An important
- * consequence of this is that if you implement {@link #onStart onStart()}
+ * consequence of this is that if you implement {@link #onStartCommand onStartCommand()}
* to schedule work to be done asynchronously or in another thread, then you
- * may want to write information about that work into persistent storage
- * during the onStart() call so that it does not get lost if the service later
- * gets killed.
+ * may want to use {@link #START_FLAG_REDELIVERY} to have the system
+ * re-deliver an Intent for you so that it does not get lost if your service
+ * is killed while processing it.
*
* <p>Other application components running in the same process as the service
* (such as an {@link android.app.Activity}) can, of course, increase the
@@ -168,20 +186,127 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
+ * @deprecated Implement {@link #onStartCommand(Intent, int, int)} instead.
+ */
+ @Deprecated
+ public void onStart(Intent intent, int startId) {
+ }
+
+ /**
+ * Bits returned by {@link #onStartCommand} describing how to continue
+ * the service if it is killed. May be {@link #START_STICKY},
+ * {@link #START_NOT_STICKY}, {@link #START_REDELIVER_INTENT},
+ * or {@link #START_STICKY_COMPATIBILITY}.
+ */
+ public static final int START_CONTINUATION_MASK = 0xf;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: compatibility
+ * version of {@link #START_STICKY} that does not guarantee that
+ * {@link #onStartCommand} will be called again after being killed.
+ */
+ public static final int START_STICKY_COMPATIBILITY = 0;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), then leave it in the started state but
+ * don't retain this delivered intent. Later the system will try to
+ * re-create the service. Because it is in the started state, it will
+ * guarantee to call {@link #onStartCommand} after creating the new
+ * service instance; if there are not any pending start commands to be
+ * delivered to the service, it will be called with a null intent
+ * object, so you must take care to check for this.
+ *
+ * <p>This mode makes sense for things that will be explicitly started
+ * and stopped to run for arbitrary periods of time, such as a service
+ * performing background music playback.
+ */
+ public static final int START_STICKY = 1;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), and there are no new start intents to
+ * deliver to it, then take the service out of the started state and
+ * don't recreate until a future explicit call to
+ * {@link Context#startService Context.startService(Intent)}. The
+ * service will not receive a {@link #onStartCommand(Intent, int, int)}
+ * call with a null Intent because it will not be re-started if there
+ * are no pending Intents to deliver.
+ *
+ * <p>This mode makes sense for things that want to do some work as a
+ * result of being started, but can be stopped when under memory pressure
+ * and will explicit start themselves again later to do more work. An
+ * example of such a service would be one that polls for data from
+ * a server: it could schedule an alarm to poll every N minutes by having
+ * the alarm start its service. When its {@link #onStartCommand} is
+ * called from the alarm, it schedules a new alarm for N minutes later,
+ * and spawns a thread to do its networking. If its process is killed
+ * while doing that check, the service will not be restarted until the
+ * alarm goes off.
+ */
+ public static final int START_NOT_STICKY = 2;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), then it will be scheduled for a restart
+ * and the last delivered Intent re-delivered to it again via
+ * {@link #onStartCommand}. This Intent will remain scheduled for
+ * redelivery until the service calls {@link #stopSelf(int)} with the
+ * start ID provided to {@link #onStartCommand}. The
+ * service will not receive a {@link #onStartCommand(Intent, int, int)}
+ * call with a null Intent because it will will only be re-started if
+ * it is not finished processing all Intents sent to it (and any such
+ * pending events will be delivered at the point of restart).
+ */
+ public static final int START_REDELIVER_INTENT = 3;
+
+ /**
+ * This flag is set in {@link #onStartCommand} if the Intent is a
+ * re-delivery of a previously delivered intent, because the service
+ * had previously returned {@link #START_REDELIVER_INTENT} but had been
+ * killed before calling {@link #stopSelf(int)} for that Intent.
+ */
+ public static final int START_FLAG_REDELIVERY = 0x0001;
+
+ /**
+ * This flag is set in {@link #onStartCommand} if the Intent is a
+ * a retry because the original attempt never got to or returned from
+ * {@link #onStartCommand(Intent, int, int)}.
+ */
+ public static final int START_FLAG_RETRY = 0x0002;
+
+ /**
* Called by the system every time a client explicitly starts the service by calling
* {@link android.content.Context#startService}, providing the arguments it supplied and a
* unique integer token representing the start request. Do not call this method directly.
- *
+ *
+ * <p>For backwards compatibility, the default implementation calls
+ * {@link #onStart} and returns either {@link #START_STICKY}
+ * or {@link #START_STICKY_COMPATIBILITY}.
+ *
* @param intent The Intent supplied to {@link android.content.Context#startService},
- * as given.
+ * as given. This may be null if the service is being restarted after
+ * its process has gone away, and it had previously returned anything
+ * except {@link #START_STICKY_COMPATIBILITY}.
+ * @param flags Additional data about this start request. Currently either
+ * 0, {@link #START_FLAG_REDELIVERY}, or {@link #START_FLAG_RETRY}.
* @param startId A unique integer representing this specific request to
- * start. Use with {@link #stopSelfResult(int)}.
+ * start. Use with {@link #stopSelfResult(int)}.
+ *
+ * @return The return value indicates what semantics the system should
+ * use for the service's current started state. It may be one of the
+ * constants associated with the {@link #START_CONTINUATION_MASK} bits.
*
* @see #stopSelfResult(int)
*/
- public void onStart(Intent intent, int startId) {
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ onStart(intent, startId);
+ return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
-
+
/**
* Called by the system to notify a Service that it is no longer used and is being removed. The
* service should clean up an resources it holds (threads, registered
@@ -284,6 +409,13 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
* safely avoid stopping if there is a start request from a client that you
* haven't yet seen in {@link #onStart}.
*
+ * <p><em>Be careful about ordering of your calls to this function.</em>.
+ * If you call this function with the most-recently received ID before
+ * you have called it for previously received IDs, the service will be
+ * immediately stopped anyway. If you may end up processing IDs out
+ * of order (such as by dispatching them on separate threads), then you
+ * are responsible for stopping them in the same order you received them.</p>
+ *
* @param startId The most recent start identifier received in {@link
* #onStart}.
* @return Returns true if the startId matches the last start request
@@ -304,24 +436,61 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
- * Control whether this service is considered to be a foreground service.
+ * @deprecated This is a now a no-op, use
+ * {@link #startForeground(int, Notification)} instead. This method
+ * has been turned into a no-op rather than simply being deprecated
+ * because analysis of numerous poorly behaving devices has shown that
+ * increasingly often the trouble is being caused in part by applications
+ * that are abusing it. Thus, given a choice between introducing
+ * problems in existing applications using this API (by allowing them to
+ * be killed when they would like to avoid it), vs allowing the performance
+ * of the entire system to be decreased, this method was deemed less
+ * important.
+ */
+ @Deprecated
+ public final void setForeground(boolean isForeground) {
+ Log.w(TAG, "setForeground: ignoring old API call on " + getClass().getName());
+ }
+
+ /**
+ * Make this service run in the foreground, supplying the ongoing
+ * notification to be shown to the user while in this state.
* By default services are background, meaning that if the system needs to
* kill them to reclaim more memory (such as to display a large page in a
* web browser), they can be killed without too much harm. You can set this
- * flag if killing your service would be disruptive to the user: such as
+ * flag if killing your service would be disruptive to the user, such as
* if your service is performing background music playback, so the user
* would notice if their music stopped playing.
*
- * @param isForeground Determines whether this service is considered to
- * be foreground (true) or background (false).
+ * @param id The identifier for this notification as per
+ * {@link NotificationManager#notify(int, Notification)
+ * NotificationManager.notify(int, Notification)}.
+ * @param notification The Notification to be displayed.
+ *
+ * @see #stopForeground(boolean)
*/
- public final void setForeground(boolean isForeground) {
- if (mActivityManager == null) {
- return;
+ public final void startForeground(int id, Notification notification) {
+ try {
+ mActivityManager.setServiceForeground(
+ new ComponentName(this, mClassName), mToken, id,
+ notification, true);
+ } catch (RemoteException ex) {
}
+ }
+
+ /**
+ * Remove this service from foreground state, allowing it to be killed if
+ * more memory is needed.
+ * @param removeNotification If true, the notification previously provided
+ * to {@link #startForeground} will be removed. Otherwise it will remain
+ * until a later call removes it (or the service is destroyed).
+ * @see #startForeground(int, Notification)
+ */
+ public final void stopForeground(boolean removeNotification) {
try {
mActivityManager.setServiceForeground(
- new ComponentName(this, mClassName), mToken, isForeground);
+ new ComponentName(this, mClassName), mToken, 0, null,
+ removeNotification);
} catch (RemoteException ex) {
}
}
@@ -363,6 +532,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
mToken = token;
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
+ mStartCompatibility = getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.ECLAIR;
}
final String getClassName() {
@@ -375,4 +546,5 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
private IBinder mToken = null;
private Application mApplication = null;
private IActivityManager mActivityManager = null;
+ private boolean mStartCompatibility = false;
}
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 90f8c50..12be97c 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -20,6 +20,7 @@ import android.app.SearchManager.DialogCursorProtocol;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.ContentResolver.OpenResourceIdResult;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -27,7 +28,6 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.DrawableContainer;
import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
import android.os.Bundle;
@@ -36,12 +36,13 @@ import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
+import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Filter;
import android.widget.ImageView;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
-import android.widget.Filter;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -57,6 +58,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
private static final boolean DBG = false;
private static final String LOG_TAG = "SuggestionsAdapter";
+ private static final int QUERY_LIMIT = 50;
private SearchManager mSearchManager;
private SearchDialog mSearchDialog;
@@ -185,7 +187,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
}
try {
- final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query);
+ final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
// trigger fill window so the spinner stays up until the results are copied over and
// closer to being ready
if (!mGlobalSearchMode && cursor != null) cursor.getCount();
@@ -288,12 +290,16 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
* @param cursor The cursor
* @param position The position that was clicked.
*/
- void callCursorOnClick(Cursor cursor, int position) {
+ void callCursorOnClick(Cursor cursor, int position, int actionKey, String actionMsg) {
if (!mGlobalSearchMode) return;
- final Bundle request = new Bundle(1);
+ final Bundle request = new Bundle(5);
request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.CLICK);
request.putInt(DialogCursorProtocol.CLICK_SEND_POSITION, position);
request.putInt(DialogCursorProtocol.CLICK_SEND_MAX_DISPLAY_POS, mMaxDisplayed);
+ if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
+ request.putInt(DialogCursorProtocol.CLICK_SEND_ACTION_KEY, actionKey);
+ request.putString(DialogCursorProtocol.CLICK_SEND_ACTION_MSG, actionMsg);
+ }
final Bundle response = cursor.respond(request);
mMaxDisplayed = -1;
mListItemToSelect = response.getInt(
@@ -301,6 +307,23 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
}
/**
+ * Tell the cursor that a search was started without using a suggestion.
+ *
+ * @param query The search query.
+ */
+ void reportSearch(String query) {
+ if (!mGlobalSearchMode) return;
+ Cursor cursor = getCursor();
+ if (cursor == null) return;
+ final Bundle request = new Bundle(3);
+ request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.SEARCH);
+ request.putString(DialogCursorProtocol.SEARCH_SEND_QUERY, query);
+ request.putInt(DialogCursorProtocol.SEARCH_SEND_MAX_DISPLAY_POS, mMaxDisplayed);
+ // the response is always empty
+ cursor.respond(request);
+ }
+
+ /**
* Tags the view with cached child view look-ups.
*/
@Override
@@ -378,7 +401,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
Drawable.ConstantState cachedBg = mBackgroundsCache.get(backgroundColor);
if (cachedBg != null) {
if (DBG) Log.d(LOG_TAG, "Background cache hit for color " + backgroundColor);
- return cachedBg.newDrawable();
+ return cachedBg.newDrawable(mProviderContext.getResources());
}
if (DBG) Log.d(LOG_TAG, "Creating new background for color " + backgroundColor);
ColorDrawable transparent = new ColorDrawable(0);
@@ -550,54 +573,91 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
if (drawableId == null || drawableId.length() == 0 || "0".equals(drawableId)) {
return null;
}
-
- // First, check the cache.
- Drawable.ConstantState cached = mOutsideDrawablesCache.get(drawableId);
- if (cached != null) {
- if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + drawableId);
- return cached.newDrawable();
- }
-
- Drawable drawable = null;
try {
- // Not cached, try using it as a plain resource ID in the provider's context.
+ // First, see if it's just an integer
int resourceId = Integer.parseInt(drawableId);
+ // It's an int, look for it in the cache
+ String drawableUri = ContentResolver.SCHEME_ANDROID_RESOURCE
+ + "://" + mProviderContext.getPackageName() + "/" + resourceId;
+ // Must use URI as cache key, since ints are app-specific
+ Drawable drawable = checkIconCache(drawableUri);
+ if (drawable != null) {
+ return drawable;
+ }
+ // Not cached, find it by resource ID
drawable = mProviderContext.getResources().getDrawable(resourceId);
- if (DBG) Log.d(LOG_TAG, "Found icon by resource ID: " + drawableId);
+ // Stick it in the cache, using the URI as key
+ storeInIconCache(drawableUri, drawable);
+ return drawable;
} catch (NumberFormatException nfe) {
- // The id was not an integer resource id.
- // Let the ContentResolver handle content, android.resource and file URIs.
- try {
- Uri uri = Uri.parse(drawableId);
+ // It's not an integer, use it as a URI
+ Drawable drawable = checkIconCache(drawableId);
+ if (drawable != null) {
+ return drawable;
+ }
+ Uri uri = Uri.parse(drawableId);
+ drawable = getDrawable(uri);
+ storeInIconCache(drawableId, drawable);
+ return drawable;
+ } catch (Resources.NotFoundException nfe) {
+ // It was an integer, but it couldn't be found, bail out
+ Log.w(LOG_TAG, "Icon resource not found: " + drawableId);
+ return null;
+ }
+ }
+
+ /**
+ * Gets a drawable by URI, without using the cache.
+ *
+ * @return A drawable, or {@code null} if the drawable could not be loaded.
+ */
+ private Drawable getDrawable(Uri uri) {
+ try {
+ String scheme = uri.getScheme();
+ if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
+ // Load drawables through Resources, to get the source density information
+ OpenResourceIdResult r =
+ mProviderContext.getContentResolver().getResourceId(uri);
+ try {
+ return r.r.getDrawable(r.id);
+ } catch (Resources.NotFoundException ex) {
+ throw new FileNotFoundException("Resource does not exist: " + uri);
+ }
+ } else {
+ // Let the ContentResolver handle content and file URIs.
InputStream stream = mProviderContext.getContentResolver().openInputStream(uri);
- if (stream != null) {
+ if (stream == null) {
+ throw new FileNotFoundException("Failed to open " + uri);
+ }
+ try {
+ return Drawable.createFromStream(stream, null);
+ } finally {
try {
- drawable = Drawable.createFromStream(stream, null);
- } finally {
- try {
- stream.close();
- } catch (IOException ex) {
- Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex);
- }
+ stream.close();
+ } catch (IOException ex) {
+ Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex);
}
}
- if (DBG) Log.d(LOG_TAG, "Opened icon input stream: " + drawableId);
- } catch (FileNotFoundException fnfe) {
- if (DBG) Log.d(LOG_TAG, "Icon stream not found: " + drawableId);
- // drawable = null;
}
+ } catch (FileNotFoundException fnfe) {
+ Log.w(LOG_TAG, "Icon not found: " + uri + ", " + fnfe.getMessage());
+ return null;
+ }
+ }
- // If we got a drawable for this resource id, then stick it in the
- // map so we don't do this lookup again.
- if (drawable != null) {
- mOutsideDrawablesCache.put(drawableId, drawable.getConstantState());
- }
- } catch (Resources.NotFoundException nfe) {
- if (DBG) Log.d(LOG_TAG, "Icon resource not found: " + drawableId);
- // drawable = null;
+ private Drawable checkIconCache(String resourceUri) {
+ Drawable.ConstantState cached = mOutsideDrawablesCache.get(resourceUri);
+ if (cached == null) {
+ return null;
}
+ if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + resourceUri);
+ return cached.newDrawable();
+ }
- return drawable;
+ private void storeInIconCache(String resourceUri, Drawable drawable) {
+ if (drawable != null) {
+ mOutsideDrawablesCache.put(resourceUri, drawable.getConstantState());
+ }
}
/**
@@ -646,7 +706,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
// Using containsKey() since we also store null values.
if (mOutsideDrawablesCache.containsKey(componentIconKey)) {
Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey);
- return cached == null ? null : cached.newDrawable();
+ return cached == null ? null : cached.newDrawable(mProviderContext.getResources());
}
// Then try the activity or application icon
Drawable drawable = getActivityIcon(component);
diff --git a/core/java/android/app/WallpaperInfo.aidl b/core/java/android/app/WallpaperInfo.aidl
new file mode 100644
index 0000000..7bbdcae
--- /dev/null
+++ b/core/java/android/app/WallpaperInfo.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2009, 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 android.app;
+
+parcelable WallpaperInfo;
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
new file mode 100644
index 0000000..34d3133
--- /dev/null
+++ b/core/java/android/app/WallpaperInfo.java
@@ -0,0 +1,277 @@
+package android.app;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources.NotFoundException;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.wallpaper.WallpaperService;
+import android.util.AttributeSet;
+import android.util.Printer;
+import android.util.Xml;
+
+import java.io.IOException;
+
+/**
+ * This class is used to specify meta information of a wallpaper service.
+ */
+public final class WallpaperInfo implements Parcelable {
+ static final String TAG = "WallpaperInfo";
+
+ /**
+ * The Service that implements this wallpaper component.
+ */
+ final ResolveInfo mService;
+
+ /**
+ * The wallpaper setting activity's name, to
+ * launch the setting activity of this wallpaper.
+ */
+ final String mSettingsActivityName;
+
+ /**
+ * Resource identifier for this wallpaper's thumbnail image.
+ */
+ final int mThumbnailResource;
+
+ /**
+ * Resource identifier for a string indicating the author of the wallpaper.
+ */
+ final int mAuthorResource;
+
+ /**
+ * Resource identifier for a string containing a short description of the wallpaper.
+ */
+ final int mDescriptionResource;
+
+ /**
+ * Constructor.
+ *
+ * @param context The Context in which we are parsing the wallpaper.
+ * @param service The ResolveInfo returned from the package manager about
+ * this wallpaper's component.
+ */
+ public WallpaperInfo(Context context, ResolveInfo service)
+ throws XmlPullParserException, IOException {
+ mService = service;
+ ServiceInfo si = service.serviceInfo;
+
+ PackageManager pm = context.getPackageManager();
+ String settingsActivityComponent = null;
+ int thumbnailRes = -1;
+ int authorRes = -1;
+ int descriptionRes = -1;
+
+ XmlResourceParser parser = null;
+ try {
+ parser = si.loadXmlMetaData(pm, WallpaperService.SERVICE_META_DATA);
+ if (parser == null) {
+ throw new XmlPullParserException("No "
+ + WallpaperService.SERVICE_META_DATA + " meta-data");
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!"wallpaper".equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data does not start with wallpaper tag");
+ }
+
+ TypedArray sa = context.getResources().obtainAttributes(attrs,
+ com.android.internal.R.styleable.Wallpaper);
+ settingsActivityComponent = sa.getString(
+ com.android.internal.R.styleable.Wallpaper_settingsActivity);
+
+ thumbnailRes = sa.getResourceId(
+ com.android.internal.R.styleable.Wallpaper_thumbnail,
+ -1);
+ authorRes = sa.getResourceId(
+ com.android.internal.R.styleable.Wallpaper_wallpaperAuthor,
+ -1);
+ descriptionRes = sa.getResourceId(
+ com.android.internal.R.styleable.Wallpaper_wallpaperDescription,
+ -1);
+
+ sa.recycle();
+ } finally {
+ if (parser != null) parser.close();
+ }
+
+ mSettingsActivityName = settingsActivityComponent;
+ mThumbnailResource = thumbnailRes;
+ mAuthorResource = authorRes;
+ mDescriptionResource = descriptionRes;
+ }
+
+ WallpaperInfo(Parcel source) {
+ mSettingsActivityName = source.readString();
+ mThumbnailResource = source.readInt();
+ mAuthorResource = source.readInt();
+ mDescriptionResource = source.readInt();
+ mService = ResolveInfo.CREATOR.createFromParcel(source);
+ }
+
+ /**
+ * Return the .apk package that implements this wallpaper.
+ */
+ public String getPackageName() {
+ return mService.serviceInfo.packageName;
+ }
+
+ /**
+ * Return the class name of the service component that implements
+ * this wallpaper.
+ */
+ public String getServiceName() {
+ return mService.serviceInfo.name;
+ }
+
+ /**
+ * Return the raw information about the Service implementing this
+ * wallpaper. Do not modify the returned object.
+ */
+ public ServiceInfo getServiceInfo() {
+ return mService.serviceInfo;
+ }
+
+ /**
+ * Return the component of the service that implements this wallpaper.
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(mService.serviceInfo.packageName,
+ mService.serviceInfo.name);
+ }
+
+ /**
+ * Load the user-displayed label for this wallpaper.
+ *
+ * @param pm Supply a PackageManager used to load the wallpaper's
+ * resources.
+ */
+ public CharSequence loadLabel(PackageManager pm) {
+ return mService.loadLabel(pm);
+ }
+
+ /**
+ * Load the user-displayed icon for this wallpaper.
+ *
+ * @param pm Supply a PackageManager used to load the wallpaper's
+ * resources.
+ */
+ public Drawable loadIcon(PackageManager pm) {
+ return mService.loadIcon(pm);
+ }
+
+ /**
+ * Load the thumbnail image for this wallpaper.
+ *
+ * @param pm Supply a PackageManager used to load the wallpaper's
+ * resources.
+ */
+ public Drawable loadThumbnail(PackageManager pm) {
+ if (mThumbnailResource < 0) return null;
+
+ return pm.getDrawable(mService.serviceInfo.packageName,
+ mThumbnailResource,
+ null);
+ }
+
+ /**
+ * Return a string indicating the author(s) of this wallpaper.
+ */
+ public CharSequence loadAuthor(PackageManager pm) throws NotFoundException {
+ if (mAuthorResource <= 0) throw new NotFoundException();
+ return pm.getText(
+ (mService.resolvePackageName != null)
+ ? mService.resolvePackageName
+ : getPackageName(),
+ mAuthorResource,
+ null);
+ }
+
+ /**
+ * Return a brief summary of this wallpaper's behavior.
+ */
+ public CharSequence loadDescription(PackageManager pm) throws NotFoundException {
+ if (mDescriptionResource <= 0) throw new NotFoundException();
+ return pm.getText(
+ (mService.resolvePackageName != null)
+ ? mService.resolvePackageName
+ : getPackageName(),
+ mDescriptionResource,
+ null);
+ }
+
+ /**
+ * Return the class name of an activity that provides a settings UI for
+ * the wallpaper. You can launch this activity be starting it with
+ * an {@link android.content.Intent} whose action is MAIN and with an
+ * explicit {@link android.content.ComponentName}
+ * composed of {@link #getPackageName} and the class name returned here.
+ *
+ * <p>A null will be returned if there is no settings activity associated
+ * with the wallpaper.
+ */
+ public String getSettingsActivity() {
+ return mSettingsActivityName;
+ }
+
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "Service:");
+ mService.dump(pw, prefix + " ");
+ pw.println(prefix + "mSettingsActivityName=" + mSettingsActivityName);
+ }
+
+ @Override
+ public String toString() {
+ return "WallpaperInfo{" + mService.serviceInfo.name
+ + ", settings: "
+ + mSettingsActivityName + "}";
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mSettingsActivityName);
+ dest.writeInt(mThumbnailResource);
+ dest.writeInt(mAuthorResource);
+ dest.writeInt(mDescriptionResource);
+ mService.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<WallpaperInfo> CREATOR = new Parcelable.Creator<WallpaperInfo>() {
+ public WallpaperInfo createFromParcel(Parcel source) {
+ return new WallpaperInfo(source);
+ }
+
+ public WallpaperInfo[] newArray(int size) {
+ return new WallpaperInfo[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
new file mode 100644
index 0000000..e98b286
--- /dev/null
+++ b/core/java/android/app/WallpaperManager.java
@@ -0,0 +1,709 @@
+/*
+ * Copyright (C) 2009 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 android.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.ViewRoot;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Provides access to the system wallpaper. With WallpaperManager, you can
+ * get the current wallpaper, get the desired dimensions for the wallpaper, set
+ * the wallpaper, and more. Get an instance of WallpaperManager with
+ * {@link #getInstance(android.content.Context) getInstance()}.
+ */
+public class WallpaperManager {
+ private static String TAG = "WallpaperManager";
+ private static boolean DEBUG = false;
+ private float mWallpaperXStep = -1;
+ private float mWallpaperYStep = -1;
+
+ /**
+ * Launch an activity for the user to pick the current global live
+ * wallpaper.
+ */
+ public static final String ACTION_LIVE_WALLPAPER_CHOOSER
+ = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER";
+
+ private final Context mContext;
+
+ /**
+ * Special drawable that draws a wallpaper as fast as possible. Assumes
+ * no scaling or placement off (0,0) of the wallpaper (this should be done
+ * at the time the bitmap is loaded).
+ */
+ static class FastBitmapDrawable extends Drawable {
+ private final Bitmap mBitmap;
+ private final int mWidth;
+ private final int mHeight;
+ private int mDrawLeft;
+ private int mDrawTop;
+
+ private FastBitmapDrawable(Bitmap bitmap) {
+ mBitmap = bitmap;
+ mWidth = bitmap.getWidth();
+ mHeight = bitmap.getHeight();
+ setBounds(0, 0, mWidth, mHeight);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawBitmap(mBitmap, mDrawLeft, mDrawTop, null);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+
+ @Override
+ public void setBounds(int left, int top, int right, int bottom) {
+ mDrawLeft = left + (right-left - mWidth) / 2;
+ mDrawTop = top + (bottom-top - mHeight) / 2;
+ }
+
+ @Override
+ public void setBounds(Rect bounds) {
+ // TODO Auto-generated method stub
+ super.setBounds(bounds);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ throw new UnsupportedOperationException(
+ "Not supported with this drawable");
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ throw new UnsupportedOperationException(
+ "Not supported with this drawable");
+ }
+
+ @Override
+ public void setDither(boolean dither) {
+ throw new UnsupportedOperationException(
+ "Not supported with this drawable");
+ }
+
+ @Override
+ public void setFilterBitmap(boolean filter) {
+ throw new UnsupportedOperationException(
+ "Not supported with this drawable");
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public int getMinimumWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getMinimumHeight() {
+ return mHeight;
+ }
+ }
+
+ static class Globals extends IWallpaperManagerCallback.Stub {
+ private IWallpaperManager mService;
+ private Bitmap mWallpaper;
+ private Bitmap mDefaultWallpaper;
+
+ private static final int MSG_CLEAR_WALLPAPER = 1;
+
+ private final Handler mHandler;
+
+ Globals(Looper looper) {
+ IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
+ mService = IWallpaperManager.Stub.asInterface(b);
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_CLEAR_WALLPAPER:
+ synchronized (this) {
+ mWallpaper = null;
+ mDefaultWallpaper = null;
+ }
+ break;
+ }
+ }
+ };
+ }
+
+ public void onWallpaperChanged() {
+ /* The wallpaper has changed but we shouldn't eagerly load the
+ * wallpaper as that would be inefficient. Reset the cached wallpaper
+ * to null so if the user requests the wallpaper again then we'll
+ * fetch it.
+ */
+ mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER);
+ }
+
+ public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault) {
+ synchronized (this) {
+ if (mWallpaper != null) {
+ return mWallpaper;
+ }
+ if (mDefaultWallpaper != null) {
+ return mDefaultWallpaper;
+ }
+ mWallpaper = getCurrentWallpaperLocked(context);
+ if (mWallpaper == null && returnDefault) {
+ mDefaultWallpaper = getDefaultWallpaperLocked(context);
+ return mDefaultWallpaper;
+ }
+ return mWallpaper;
+ }
+ }
+
+ private Bitmap getCurrentWallpaperLocked(Context context) {
+ try {
+ Bundle params = new Bundle();
+ ParcelFileDescriptor fd = mService.getWallpaper(this, params);
+ if (fd != null) {
+ int width = params.getInt("width", 0);
+ int height = params.getInt("height", 0);
+
+ if (width <= 0 || height <= 0) {
+ // Degenerate case: no size requested, just load
+ // bitmap as-is.
+ Bitmap bm = BitmapFactory.decodeFileDescriptor(
+ fd.getFileDescriptor(), null, null);
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ if (bm != null) {
+ bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
+ }
+ return bm;
+ }
+
+ // Load the bitmap with full color depth, to preserve
+ // quality for later processing.
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inDither = false;
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap bm = BitmapFactory.decodeFileDescriptor(
+ fd.getFileDescriptor(), null, options);
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+
+ return generateBitmap(context, bm, width, height);
+ }
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ private Bitmap getDefaultWallpaperLocked(Context context) {
+ try {
+ InputStream is = context.getResources().openRawResource(
+ com.android.internal.R.drawable.default_wallpaper);
+ if (is != null) {
+ int width = mService.getWidthHint();
+ int height = mService.getHeightHint();
+
+ if (width <= 0 || height <= 0) {
+ // Degenerate case: no size requested, just load
+ // bitmap as-is.
+ Bitmap bm = BitmapFactory.decodeStream(is, null, null);
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ if (bm != null) {
+ bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
+ }
+ return bm;
+ }
+
+ // Load the bitmap with full color depth, to preserve
+ // quality for later processing.
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inDither = false;
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap bm = BitmapFactory.decodeStream(is, null, options);
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+
+ return generateBitmap(context, bm, width, height);
+ }
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+ }
+
+ private static Object mSync = new Object();
+ private static Globals sGlobals;
+
+ static void initGlobals(Looper looper) {
+ synchronized (mSync) {
+ if (sGlobals == null) {
+ sGlobals = new Globals(looper);
+ }
+ }
+ }
+
+ /*package*/ WallpaperManager(Context context, Handler handler) {
+ mContext = context;
+ initGlobals(context.getMainLooper());
+ }
+
+ /**
+ * Retrieve a WallpaperManager associated with the given Context.
+ */
+ public static WallpaperManager getInstance(Context context) {
+ return (WallpaperManager)context.getSystemService(
+ Context.WALLPAPER_SERVICE);
+ }
+
+ /** @hide */
+ public IWallpaperManager getIWallpaperManager() {
+ return sGlobals.mService;
+ }
+
+ /**
+ * Retrieve the current system wallpaper; if
+ * no wallpaper is set, the system default wallpaper is returned.
+ * This is returned as an
+ * abstract Drawable that you can install in a View to display whatever
+ * wallpaper the user has currently set.
+ *
+ * @return Returns a Drawable object that will draw the wallpaper.
+ */
+ public Drawable getDrawable() {
+ Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
+ if (bm != null) {
+ Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
+ dr.setDither(false);
+ return dr;
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve the current system wallpaper; if there is no wallpaper set,
+ * a null pointer is returned. This is returned as an
+ * abstract Drawable that you can install in a View to display whatever
+ * wallpaper the user has currently set.
+ *
+ * @return Returns a Drawable object that will draw the wallpaper or a
+ * null pointer if these is none.
+ */
+ public Drawable peekDrawable() {
+ Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
+ if (bm != null) {
+ Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
+ dr.setDither(false);
+ return dr;
+ }
+ return null;
+ }
+
+ /**
+ * Like {@link #getDrawable()}, but the returned Drawable has a number
+ * of limitations to reduce its overhead as much as possible. It will
+ * never scale the wallpaper (only centering it if the requested bounds
+ * do match the bitmap bounds, which should not be typical), doesn't
+ * allow setting an alpha, color filter, or other attributes, etc. The
+ * bounds of the returned drawable will be initialized to the same bounds
+ * as the wallpaper, so normally you will not need to touch it. The
+ * drawable also assumes that it will be used in a context running in
+ * the same density as the screen (not in density compatibility mode).
+ *
+ * @return Returns a Drawable object that will draw the wallpaper.
+ */
+ public Drawable getFastDrawable() {
+ Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true);
+ if (bm != null) {
+ Drawable dr = new FastBitmapDrawable(bm);
+ return dr;
+ }
+ return null;
+ }
+
+ /**
+ * Like {@link #getFastDrawable()}, but if there is no wallpaper set,
+ * a null pointer is returned.
+ *
+ * @return Returns an optimized Drawable object that will draw the
+ * wallpaper or a null pointer if these is none.
+ */
+ public Drawable peekFastDrawable() {
+ Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false);
+ if (bm != null) {
+ Drawable dr = new FastBitmapDrawable(bm);
+ return dr;
+ }
+ return null;
+ }
+
+ /**
+ * If the current wallpaper is a live wallpaper component, return the
+ * information about that wallpaper. Otherwise, if it is a static image,
+ * simply return null.
+ */
+ public WallpaperInfo getWallpaperInfo() {
+ try {
+ return sGlobals.mService.getWallpaperInfo();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Change the current system wallpaper to the bitmap in the given resource.
+ * The resource is opened as a raw data stream and copied into the
+ * wallpaper; it must be a valid PNG or JPEG image. On success, the intent
+ * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
+ *
+ * @param resid The bitmap to save.
+ *
+ * @throws IOException If an error occurs reverting to the default
+ * wallpaper.
+ */
+ public void setResource(int resid) throws IOException {
+ try {
+ Resources resources = mContext.getResources();
+ /* Set the wallpaper to the default values */
+ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
+ "res:" + resources.getResourceName(resid));
+ if (fd != null) {
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ setWallpaper(resources.openRawResource(resid), fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Change the current system wallpaper to a bitmap. The given bitmap is
+ * converted to a PNG and stored as the wallpaper. On success, the intent
+ * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
+ *
+ * @param bitmap The bitmap to save.
+ *
+ * @throws IOException If an error occurs reverting to the default
+ * wallpaper.
+ */
+ public void setBitmap(Bitmap bitmap) throws IOException {
+ try {
+ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
+ if (fd == null) {
+ return;
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Change the current system wallpaper to a specific byte stream. The
+ * give InputStream is copied into persistent storage and will now be
+ * used as the wallpaper. Currently it must be either a JPEG or PNG
+ * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
+ * is broadcast.
+ *
+ * @param data A stream containing the raw data to install as a wallpaper.
+ *
+ * @throws IOException If an error occurs reverting to the default
+ * wallpaper.
+ */
+ public void setStream(InputStream data) throws IOException {
+ try {
+ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
+ if (fd == null) {
+ return;
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ setWallpaper(data, fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void setWallpaper(InputStream data, FileOutputStream fos)
+ throws IOException {
+ byte[] buffer = new byte[32768];
+ int amt;
+ while ((amt=data.read(buffer)) > 0) {
+ fos.write(buffer, 0, amt);
+ }
+ }
+
+ /**
+ * Returns the desired minimum width for the wallpaper. Callers of
+ * {@link #setBitmap(android.graphics.Bitmap)} or
+ * {@link #setStream(java.io.InputStream)} should check this value
+ * beforehand to make sure the supplied wallpaper respects the desired
+ * minimum width.
+ *
+ * If the returned value is <= 0, the caller should use the width of
+ * the default display instead.
+ *
+ * @return The desired minimum width for the wallpaper. This value should
+ * be honored by applications that set the wallpaper but it is not
+ * mandatory.
+ */
+ public int getDesiredMinimumWidth() {
+ try {
+ return sGlobals.mService.getWidthHint();
+ } catch (RemoteException e) {
+ // Shouldn't happen!
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the desired minimum height for the wallpaper. Callers of
+ * {@link #setBitmap(android.graphics.Bitmap)} or
+ * {@link #setStream(java.io.InputStream)} should check this value
+ * beforehand to make sure the supplied wallpaper respects the desired
+ * minimum height.
+ *
+ * If the returned value is <= 0, the caller should use the height of
+ * the default display instead.
+ *
+ * @return The desired minimum height for the wallpaper. This value should
+ * be honored by applications that set the wallpaper but it is not
+ * mandatory.
+ */
+ public int getDesiredMinimumHeight() {
+ try {
+ return sGlobals.mService.getHeightHint();
+ } catch (RemoteException e) {
+ // Shouldn't happen!
+ return 0;
+ }
+ }
+
+ /**
+ * For use only by the current home application, to specify the size of
+ * wallpaper it would like to use. This allows such applications to have
+ * a virtual wallpaper that is larger than the physical screen, matching
+ * the size of their workspace.
+ * @param minimumWidth Desired minimum width
+ * @param minimumHeight Desired minimum height
+ */
+ public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
+ try {
+ sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Set the position of the current wallpaper within any larger space, when
+ * that wallpaper is visible behind the given window. The X and Y offsets
+ * are floating point numbers ranging from 0 to 1, representing where the
+ * wallpaper should be positioned within the screen space. These only
+ * make sense when the wallpaper is larger than the screen.
+ *
+ * @param windowToken The window who these offsets should be associated
+ * with, as returned by {@link android.view.View#getWindowToken()
+ * View.getWindowToken()}.
+ * @param xOffset The offset along the X dimension, from 0 to 1.
+ * @param yOffset The offset along the Y dimension, from 0 to 1.
+ */
+ public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
+ try {
+ //Log.v(TAG, "Sending new wallpaper offsets from app...");
+ ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
+ //Log.v(TAG, "...app returning after sending offsets!");
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
+
+ /**
+ * For applications that use multiple virtual screens showing a wallpaper,
+ * specify the step size between virtual screens. For example, if the
+ * launcher has 5 virtual screens, it would specify an xStep of 0.5,
+ * since the X offset for those screens are 0.0, 0.5 and 1.0
+ * @param xStep The X offset delta from one screen to the next one
+ * @param yStep The Y offset delta from one screen to the next one
+ */
+ public void setWallpaperOffsetSteps(float xStep, float yStep) {
+ mWallpaperXStep = xStep;
+ mWallpaperYStep = yStep;
+ }
+
+ /**
+ * Send an arbitrary command to the current active wallpaper.
+ *
+ * @param windowToken The window who these offsets should be associated
+ * with, as returned by {@link android.view.View#getWindowToken()
+ * View.getWindowToken()}.
+ * @param action Name of the command to perform. This must be a scoped
+ * name to avoid collisions, such as "com.mycompany.wallpaper.DOIT".
+ * @param x Arbitrary integer argument based on command.
+ * @param y Arbitrary integer argument based on command.
+ * @param z Arbitrary integer argument based on command.
+ * @param extras Optional additional information for the command, or null.
+ */
+ public void sendWallpaperCommand(IBinder windowToken, String action,
+ int x, int y, int z, Bundle extras) {
+ try {
+ //Log.v(TAG, "Sending new wallpaper offsets from app...");
+ ViewRoot.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
+ windowToken, action, x, y, z, extras, false);
+ //Log.v(TAG, "...app returning after sending offsets!");
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
+
+ /**
+ * Clear the offsets previously associated with this window through
+ * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts
+ * the window to its default state, where it does not cause the wallpaper
+ * to scroll from whatever its last offsets were.
+ *
+ * @param windowToken The window who these offsets should be associated
+ * with, as returned by {@link android.view.View#getWindowToken()
+ * View.getWindowToken()}.
+ */
+ public void clearWallpaperOffsets(IBinder windowToken) {
+ try {
+ ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ windowToken, -1, -1, -1, -1);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
+
+ /**
+ * Remove any currently set wallpaper, reverting to the system's default
+ * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
+ * is broadcast.
+ *
+ * @throws IOException If an error occurs reverting to the default
+ * wallpaper.
+ */
+ public void clear() throws IOException {
+ setResource(com.android.internal.R.drawable.default_wallpaper);
+ }
+
+ static Bitmap generateBitmap(Context context, Bitmap bm, int width, int height) {
+ if (bm == null) {
+ return bm;
+ }
+ bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
+
+ // This is the final bitmap we want to return.
+ // XXX We should get the pixel depth from the system (to match the
+ // physical display depth), when there is a way.
+ Bitmap newbm = Bitmap.createBitmap(width, height,
+ Bitmap.Config.RGB_565);
+ newbm.setDensity(DisplayMetrics.DENSITY_DEVICE);
+ Canvas c = new Canvas(newbm);
+ c.setDensity(DisplayMetrics.DENSITY_DEVICE);
+ Rect targetRect = new Rect();
+ targetRect.left = targetRect.top = 0;
+ targetRect.right = bm.getWidth();
+ targetRect.bottom = bm.getHeight();
+
+ int deltaw = width - targetRect.right;
+ int deltah = height - targetRect.bottom;
+
+ if (deltaw > 0 || deltah > 0) {
+ // We need to scale up so it covers the entire
+ // area.
+ float scale = 1.0f;
+ if (deltaw > deltah) {
+ scale = width / (float)targetRect.right;
+ } else {
+ scale = height / (float)targetRect.bottom;
+ }
+ targetRect.right = (int)(targetRect.right*scale);
+ targetRect.bottom = (int)(targetRect.bottom*scale);
+ deltaw = width - targetRect.right;
+ deltah = height - targetRect.bottom;
+ }
+
+ targetRect.offset(deltaw/2, deltah/2);
+ Paint paint = new Paint();
+ paint.setFilterBitmap(true);
+ paint.setDither(true);
+ c.drawBitmap(bm, null, targetRect, paint);
+
+ bm.recycle();
+ return newbm;
+ }
+}