diff --git a/Android.mk b/Android.mk
index d67a21e46e897f77ff43c431c69dcc9014c9e337..8013c9360c0a18407ea5ff5678a5d85d316f987a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -97,7 +97,10 @@ LOCAL_SRC_FILES += \
core/java/android/view/IWindowManager.aidl \
core/java/android/view/IWindowSession.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
- core/java/com/android/internal/gadget/IGadgetService.aidl \
+ core/java/com/android/internal/app/IUsageStats.aidl \
+ core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
+ core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
+ core/java/com/android/internal/os/IResultReceiver.aidl \
core/java/com/android/internal/view/IInputContext.aidl \
core/java/com/android/internal/view/IInputContextCallback.aidl \
core/java/com/android/internal/view/IInputMethod.aidl \
@@ -165,7 +168,7 @@ aidl_files := \
frameworks/base/core/java/android/content/Intent.aidl \
frameworks/base/core/java/android/content/SyncStats.aidl \
frameworks/base/core/java/android/content/res/Configuration.aidl \
- frameworks/base/core/java/android/gadget/GadgetInfo.aidl \
+ frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
frameworks/base/core/java/android/net/Uri.aidl \
frameworks/base/core/java/android/os/Bundle.aidl \
frameworks/base/core/java/android/os/ParcelFileDescriptor.aidl \
@@ -173,6 +176,7 @@ aidl_files := \
frameworks/base/core/java/android/view/MotionEvent.aidl \
frameworks/base/core/java/android/view/Surface.aidl \
frameworks/base/core/java/android/view/WindowManager.aidl \
+ frameworks/base/core/java/android/widget/RemoteViews.aidl \
frameworks/base/core/java/com/android/internal/view/IInputContext.aidl \
frameworks/base/core/java/com/android/internal/view/IInputMethod.aidl \
frameworks/base/core/java/com/android/internal/view/IInputMethodCallback.aidl \
diff --git a/api/3.xml b/api/3.xml
index 9d9ce98b41a0436eacd8019e4234298b4ee48cb9..512fef8b184f850fc0acb109cc5d3ce16126d4b8 100644
--- a/api/3.xml
+++ b/api/3.xml
@@ -25763,17 +25763,6 @@
visibility="public"
>
-AndroidManifest.xml
.
The Activity class is an important part of an - * application's overall lifecycle, + *
The Activity class is an important part of an application's overall lifecycle, * and the way activities are launched and put together is a fundamental - * part of the platform's - * application model.
+ * part of the platform's application model. For a detailed perspective on the structure of + * Android applications and lifecycles, please read the Dev Guide document on + * Application Fundamentals. * *Topics covered here: *
See the Security Model + *
See the Security and Permissions * document for more information on permissions and security in general. * * @@ -629,6 +630,9 @@ public class Activity extends ContextThemeWrapper private WindowManager mWindowManager; /*package*/ View mDecor = null; + /*package*/ boolean mWindowAdded = false; + /*package*/ boolean mVisibleFromServer = false; + /*package*/ boolean mVisibleFromClient = true; private CharSequence mTitle; private int mTitleColor = 0; @@ -779,6 +783,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); mCalled = true; } @@ -884,9 +890,9 @@ public class Activity extends ContextThemeWrapper } /** - * Called after {@link #onCreate} or {@link #onStop} when the current - * activity is now being displayed to the user. It will - * be followed by {@link #onRestart}. + * Called after {@link #onCreate} — or after {@link #onRestart} when + * the activity had been stopped, but is now again being displayed to the + * user. It will be followed by {@link #onResume}. * *
Derived classes must call through to the super class's
* implementation of this method. If they do not, an exception will be
@@ -901,9 +907,9 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Called after {@link #onStart} when the current activity is being
+ * Called after {@link #onStop} when the current activity is being
* re-displayed to the user (the user has navigated back to it). It will
- * be followed by {@link #onResume}.
+ * be followed by {@link #onStart} and then {@link #onResume}.
*
* For activities that are using raw {@link Cursor} objects (instead of
* creating them through
@@ -917,6 +923,7 @@ public class Activity extends ContextThemeWrapper
* thrown.
This callback and {@link #onUserInteraction} are intended to help + * activities manage status bar notifications intelligently; specifically, + * for helping activities determine the proper time to cancel a notfication. + * + * @see #onUserInteraction() */ - protected void onUserLeaving() { + protected void onUserLeaveHint() { } /** @@ -1207,7 +1221,7 @@ public class Activity extends ContextThemeWrapper /** * Called when you are no longer visible to the user. You will next - * receive either {@link #onStart}, {@link #onDestroy}, or nothing, + * receive either {@link #onRestart}, {@link #onDestroy}, or nothing, * depending on later user activity. * *
Note that this method may never be called, in low memory situations @@ -1443,7 +1457,6 @@ public class Activity extends ContextThemeWrapper * @return The Cursor that was returned by query(). * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) - * @see #managedCommitUpdates * @see #startManagingCursor * @hide */ @@ -1475,7 +1488,6 @@ public class Activity extends ContextThemeWrapper * @return The Cursor that was returned by query(). * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) - * @see #managedCommitUpdates * @see #startManagingCursor */ public final Cursor managedQuery(Uri uri, @@ -1863,6 +1875,28 @@ public class Activity extends ContextThemeWrapper return false; } + /** + * Called whenever a key, touch, or trackball event is dispatched to the + * activity. Implement this method if you wish to know that the user has + * interacted with the device in some way while your activity is running. + * This callback and {@link #onUserLeaveHint} are intended to help + * activities manage status bar notifications intelligently; specifically, + * for helping activities determine the proper time to cancel a notfication. + * + *
All calls to your activity's {@link #onUserLeaveHint} callback will + * be accompanied by calls to {@link #onUserInteraction}. This + * ensures that your activity will be told of relevant user activity such + * as pulling down the notification pane and touching an item there. + * + *
Note that this callback will be invoked for the touch down action + * that begins a touch gesture, but may not be invoked for the touch-moved + * and touch-up actions that follow. + * + * @see #onUserLeaveHint() + */ + public void onUserInteraction() { + } + public void onWindowAttributesChanged(WindowManager.LayoutParams params) { // Update window manager if: we have a view, that view is // attached to its parent (which will be a RootView), and @@ -1935,6 +1969,7 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event) { + onUserInteraction(); if (getWindow().superDispatchKeyEvent(event)) { return true; } @@ -1952,6 +1987,9 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + onUserInteraction(); + } if (getWindow().superDispatchTouchEvent(ev)) { return true; } @@ -1969,6 +2007,7 @@ public class Activity extends ContextThemeWrapper * @return boolean Return true if this event was consumed. */ public boolean dispatchTrackballEvent(MotionEvent ev) { + onUserInteraction(); if (getWindow().superDispatchTrackballEvent(ev)) { return true; } @@ -2864,6 +2903,35 @@ public class Activity extends ContextThemeWrapper } } + /** + * Control whether this activity's main window is visible. This is intended + * only for the special case of an activity that is not going to show a + * UI itself, but can't just finish prior to onResume() because it needs + * to wait for a service binding or such. Setting this to false allows + * you to prevent your UI from being shown during that time. + * + *
The default value for this is taken from the
+ * {@link android.R.attr#windowNoDisplay} attribute of the activity's theme.
+ */
+ public void setVisible(boolean visible) {
+ if (mVisibleFromClient != visible) {
+ mVisibleFromClient = visible;
+ if (mVisibleFromServer) {
+ if (visible) makeVisible();
+ else mDecor.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ void makeVisible() {
+ if (!mWindowAdded) {
+ ViewManager wm = getWindowManager();
+ wm.addView(mDecor, getWindow().getAttributes());
+ mWindowAdded = true;
+ }
+ mDecor.setVisibility(View.VISIBLE);
+ }
+
/**
* Check to see whether this activity is in the process of finishing,
* either because you called {@link #finish} on it or someone else
@@ -3482,7 +3550,8 @@ public class Activity extends ContextThemeWrapper
}
final void performUserLeaving() {
- onUserLeaving();
+ onUserInteraction();
+ onUserLeaveHint();
}
final void performStop() {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f9b92218c56a97acb3df75155d27c2d498f2e406..07520c9d65f7dc57cc2bb9ee08bfe621006fe32d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -19,16 +19,14 @@ package android.app;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.graphics.Bitmap;
import android.os.RemoteException;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.Parcelable.Creator;
import android.text.TextUtils;
-import android.util.Log;
import java.util.List;
/**
@@ -617,7 +615,59 @@ public class ActivityManager {
public String pkgList[];
+ /**
+ * Constant for {@link #importance}: this process is running the
+ * foreground UI.
+ */
+ public static final int IMPORTANCE_FOREGROUND = 100;
+
+ /**
+ * Constant for {@link #importance}: this process is running something
+ * that is considered to be actively visible to the user.
+ */
+ public static final int IMPORTANCE_VISIBLE = 200;
+
+ /**
+ * Constant for {@link #importance}: this process is contains services
+ * that should remain running.
+ */
+ public static final int IMPORTANCE_SERVICE = 300;
+
+ /**
+ * Constant for {@link #importance}: this process process contains
+ * background code that is expendable.
+ */
+ public static final int IMPORTANCE_BACKGROUND = 400;
+
+ /**
+ * Constant for {@link #importance}: this process is empty of any
+ * actively running code.
+ */
+ public static final int IMPORTANCE_EMPTY = 500;
+
+ /**
+ * The relative importance level that the system places on this
+ * process. May be one of {@link #IMPORTANCE_FOREGROUND},
+ * {@link #IMPORTANCE_VISIBLE}, {@link #IMPORTANCE_SERVICE},
+ * {@link #IMPORTANCE_BACKGROUND}, or {@link #IMPORTANCE_EMPTY}. These
+ * constants are numbered so that "more important" values are always
+ * smaller than "less important" values.
+ */
+ public int importance;
+
+ /**
+ * An additional ordering within a particular {@link #importance}
+ * category, providing finer-grained information about the relative
+ * utility of processes within a category. This number means nothing
+ * except that a smaller values are more recently used (and thus
+ * more important). Currently an LRU value is only maintained for
+ * the {@link #IMPORTANCE_BACKGROUND} category, though others may
+ * be maintained in the future.
+ */
+ public int lru;
+
public RunningAppProcessInfo() {
+ importance = IMPORTANCE_FOREGROUND;
}
public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
@@ -634,12 +684,16 @@ public class ActivityManager {
dest.writeString(processName);
dest.writeInt(pid);
dest.writeStringArray(pkgList);
+ dest.writeInt(importance);
+ dest.writeInt(lru);
}
public void readFromParcel(Parcel source) {
processName = source.readString();
pid = source.readInt();
pkgList = source.readStringArray();
+ importance = source.readInt();
+ lru = source.readInt();
}
public static final Creator You must hold the permission
+ * {@link android.Manifest.permission#RESTART_PACKAGES} to be able to
+ * call this method.
+ *
+ * @param packageName The name of the package to be stopped.
+ */
+ public void restartPackage(String packageName) {
+ try {
+ ActivityManagerNative.getDefault().restartPackage(packageName);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Get the device configuration attributes.
+ */
+ public ConfigurationInfo getDeviceConfigurationInfo() {
+ try {
+ return ActivityManagerNative.getDefault().getDeviceConfigurationInfo();
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ae9f3bf15029c2d664531aa4f4cedf94f5d5abe7..53e6f34a691564b24565339eb7f813c68101550e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -20,6 +20,7 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -82,6 +83,17 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return gDefault;
}
+ /**
+ * Convenience for checking whether the system is ready. For internal use only.
+ */
+ static public boolean isSystemReady() {
+ if (!sSystemReady) {
+ sSystemReady = getDefault().testIsSystemReady();
+ }
+ return sSystemReady;
+ }
+ static boolean sSystemReady = false;
+
/**
* Convenience for sending a sticky broadcast. For internal use only.
* If you don't care about permission, use null.
@@ -959,6 +971,34 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case GET_DEVICE_CONFIGURATION_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ConfigurationInfo config = getDeviceConfigurationInfo();
+ reply.writeNoException();
+ config.writeToParcel(reply, 0);
+ return true;
+ }
+
+ case PROFILE_CONTROL_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String process = data.readString();
+ boolean start = data.readInt() != 0;
+ String path = data.readString();
+ boolean res = profileControl(process, start, path);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
+ case PEEK_SERVICE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ Intent service = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ IBinder binder = peekService(service, resolvedType);
+ reply.writeNoException();
+ reply.writeStrongBinder(binder);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -1604,6 +1644,20 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
+
+ public IBinder peekService(Intent service, String resolvedType) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ service.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ mRemote.transact(PEEK_SERVICE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ IBinder binder = reply.readStrongBinder();
+ reply.recycle();
+ data.recycle();
+ return binder;
+ }
public boolean startInstrumentation(ComponentName className, String profileFile,
int flags, Bundle arguments, IInstrumentationWatcher watcher)
@@ -2028,6 +2082,11 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
+ public boolean testIsSystemReady()
+ {
+ /* this base class version is never called */
+ return true;
+ }
public int handleApplicationError(IBinder app, int flags,
String tag, String shortMsg, String longMsg,
byte[] crashData) throws RemoteException
@@ -2071,5 +2130,35 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_DEVICE_CONFIGURATION_TRANSACTION, data, reply, 0);
+ reply.readException();
+ ConfigurationInfo res = ConfigurationInfo.CREATOR.createFromParcel(reply);
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+
+ public boolean profileControl(String process, boolean start,
+ String path) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(process);
+ data.writeInt(start ? 1 : 0);
+ data.writeString(path);
+ mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ reply.recycle();
+ data.recycle();
+ return res;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e4c1057e3878de2c12e28bc16b7be4bbc37a42f2..f49005ec0e5c9a7380a0a8334e489259d59f1b65 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -144,13 +144,19 @@ public final class ActivityThread {
return sPackageManager;
}
- DisplayMetrics getDisplayMetricsLocked() {
+ DisplayMetrics getDisplayMetricsLocked(boolean forceUpdate) {
+ if (mDisplayMetrics != null && !forceUpdate) {
+ return mDisplayMetrics;
+ }
if (mDisplay == null) {
WindowManager wm = WindowManagerImpl.getDefault();
mDisplay = wm.getDefaultDisplay();
}
- DisplayMetrics metrics = new DisplayMetrics();
+ DisplayMetrics metrics = mDisplayMetrics = new DisplayMetrics();
mDisplay.getMetrics(metrics);
+ //Log.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
+ // + metrics.heightPixels + " den=" + metrics.density
+ // + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi);
return metrics;
}
@@ -173,7 +179,7 @@ public final class ActivityThread {
if (assets.addAssetPath(appDir) == 0) {
return null;
}
- DisplayMetrics metrics = getDisplayMetricsLocked();
+ DisplayMetrics metrics = getDisplayMetricsLocked(false);
r = new Resources(assets, metrics, getConfiguration());
//Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration());
// XXX need to remove entries when weak references go away
@@ -235,7 +241,7 @@ public final class ActivityThread {
ApplicationContext.createSystemContext(mainThread);
mSystemContext.getResources().updateConfiguration(
mainThread.getConfiguration(),
- mainThread.getDisplayMetricsLocked());
+ mainThread.getDisplayMetricsLocked(false));
//Log.i(TAG, "Created system resources "
// + mSystemContext.getResources() + ": "
// + mSystemContext.getResources().getConfiguration());
@@ -1205,7 +1211,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(
@@ -1440,6 +1449,10 @@ public final class ActivityThread {
}
}
+ public void profilerControl(boolean start, String path) {
+ queueOrSendMessage(H.PROFILER_CONTROL, path, start ? 1 : 0);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
long nativeMax = Debug.getNativeHeapSize() / 1024;
@@ -1462,7 +1475,101 @@ public final class ActivityThread {
long dalvikMax = runtime.totalMemory() / 1024;
long dalvikFree = runtime.freeMemory() / 1024;
long dalvikAllocated = dalvikMax - dalvikFree;
-
+ long viewInstanceCount = ViewDebug.getViewInstanceCount();
+ long viewRootInstanceCount = ViewDebug.getViewRootInstanceCount();
+ long appContextInstanceCount = ApplicationContext.getInstanceCount();
+ long activityInstanceCount = Activity.getInstanceCount();
+ int globalAssetCount = AssetManager.getGlobalAssetCount();
+ int globalAssetManagerCount = AssetManager.getGlobalAssetManagerCount();
+ int binderLocalObjectCount = Debug.getBinderLocalObjectCount();
+ int binderProxyObjectCount = Debug.getBinderProxyObjectCount();
+ int binderDeathObjectCount = Debug.getBinderDeathObjectCount();
+ int openSslSocketCount = OpenSSLSocketImpl.getInstanceCount();
+ 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) {
+ for (String arg : args) {
+ 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)
+ ? 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 + dalvikShared + otherShared); pw.print(',');
+
+ // Heap info - private
+ 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.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);
printRow(pw, HEAP_COLUMN, "allocated:", nativeAllocated, dalvikAllocated, "N/A",
@@ -1480,26 +1587,22 @@ public final class ActivityThread {
pw.println(" ");
pw.println(" Objects");
- printRow(pw, TWO_COUNT_COLUMNS, "Views:", ViewDebug.getViewInstanceCount(), "ViewRoots:",
- ViewDebug.getViewRootInstanceCount());
+ printRow(pw, TWO_COUNT_COLUMNS, "Views:", viewInstanceCount, "ViewRoots:",
+ viewRootInstanceCount);
- printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", ApplicationContext.getInstanceCount(),
- "Activities:", Activity.getInstanceCount());
+ printRow(pw, TWO_COUNT_COLUMNS, "AppContexts:", appContextInstanceCount,
+ "Activities:", activityInstanceCount);
- printRow(pw, TWO_COUNT_COLUMNS, "Assets:", AssetManager.getGlobalAssetCount(),
- "AssetManagers:", AssetManager.getGlobalAssetManagerCount());
+ printRow(pw, TWO_COUNT_COLUMNS, "Assets:", globalAssetCount,
+ "AssetManagers:", globalAssetManagerCount);
- printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", Debug.getBinderLocalObjectCount(),
- "Proxy Binders:", Debug.getBinderProxyObjectCount());
- printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", Debug.getBinderDeathObjectCount());
-
- printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", OpenSSLSocketImpl.getInstanceCount());
+ printRow(pw, TWO_COUNT_COLUMNS, "Local Binders:", binderLocalObjectCount,
+ "Proxy Binders:", binderProxyObjectCount);
+ printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", binderDeathObjectCount);
+ printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", openSslSocketCount);
+
// SQLite mem info
- long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024;
- SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats();
- SQLiteDebug.getPagerStats(stats);
-
pw.println(" ");
pw.println(" SQL");
printRow(pw, TWO_COUNT_COLUMNS, "heap:", sqliteAllocated, "dbFiles:",
@@ -1542,6 +1645,7 @@ public final class ActivityThread {
public static final int LOW_MEMORY = 124;
public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
public static final int RELAUNCH_ACTIVITY = 126;
+ public static final int PROFILER_CONTROL = 127;
String codeToString(int code) {
if (localLOGV) {
switch (code) {
@@ -1572,6 +1676,7 @@ public final class ActivityThread {
case LOW_MEMORY: return "LOW_MEMORY";
case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED";
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
+ case PROFILER_CONTROL: return "PROFILER_CONTROL";
}
}
return "(unknown)";
@@ -1671,6 +1776,9 @@ public final class ActivityThread {
case ACTIVITY_CONFIGURATION_CHANGED:
handleActivityConfigurationChanged((IBinder)msg.obj);
break;
+ case PROFILER_CONTROL:
+ handleProfilerControl(msg.arg1 != 0, (String)msg.obj);
+ break;
}
}
}
@@ -1751,6 +1859,7 @@ public final class ActivityThread {
final HashMap
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 353500e923459210aea7b38d1d27fdaa2661f53f..2ac6160cf83eb9a39a9e6bb9aacc8a190d092ae8 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -22,6 +22,7 @@ import android.content.ContentProviderNative;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.ProviderInfo;
import android.content.res.Configuration;
@@ -127,7 +128,8 @@ public interface IActivityManager extends IInterface {
boolean doRebind) throws RemoteException;
/* oneway */
public void serviceDoneExecuting(IBinder token) throws RemoteException;
-
+ public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
+
public boolean startInstrumentation(ComponentName className, String profileFile,
int flags, Bundle arguments, IInstrumentationWatcher watcher)
throws RemoteException;
@@ -216,6 +218,17 @@ public interface IActivityManager extends IInterface {
// Retrieve running application processes in the system
public List To use this class extend it and implement {@link #onHandleIntent}. The {@link Service} will
+ * automatically be stopped when the last enqueued {@link Intent} is handled.
+ */
+public abstract class IntentService extends Service {
+ private volatile Looper mServiceLooper;
+ private volatile ServiceHandler mServiceHandler;
+ private String mName;
+
+ private final class ServiceHandler extends Handler {
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ onHandleIntent((Intent)msg.obj);
+ stopSelf(msg.arg1);
+ }
+ }
+
+ public IntentService(String name) {
+ super();
+ mName = name;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
+ thread.start();
+
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper);
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ msg.obj = intent;
+ mServiceHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onDestroy() {
+ mServiceLooper.quit();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * Invoked on the Handler thread with the {@link Intent} that is passed to {@link #onStart}.
+ * Note that this will be invoked from a different thread than the one that handles the
+ * {@link #onStart} call.
+ */
+ protected abstract void onHandleIntent(Intent intent);
+}
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 8f0a4f58fe5fc553bc567ee3ffeeb3bb63940a89..d6fcbb1a6aa7ec8004eb6d85ae4451e1f125f36c 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -18,14 +18,23 @@ package android.app;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PaintDrawable;
import android.os.Bundle;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.Window;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
@@ -43,33 +52,59 @@ import java.util.List;
*
*/
public abstract class LauncherActivity extends ListActivity {
+
+ Intent mIntent;
+ PackageManager mPackageManager;
+ /**
+ * An item in the list
+ */
+ public static class ListItem {
+ public CharSequence label;
+ //public CharSequence description;
+ public Drawable icon;
+ public String packageName;
+ public String className;
+
+ ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
+ label = resolveInfo.loadLabel(pm);
+ if (label == null && resolveInfo.activityInfo != null) {
+ label = resolveInfo.activityInfo.name;
+ }
+
+ /*
+ if (resolveInfo.activityInfo != null &&
+ resolveInfo.activityInfo.applicationInfo != null) {
+ description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm);
+ }
+ */
+
+ icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
+ packageName = resolveInfo.activityInfo.applicationInfo.packageName;
+ className = resolveInfo.activityInfo.name;
+ }
+
+ public ListItem() {
+ }
+ }
+
/**
* Adapter which shows the set of activities that can be performed for a given intent.
*/
private class ActivityAdapter extends BaseAdapter implements Filterable {
private final Object lock = new Object();
- private ArrayList An array filters constrains the content of the array adapter with a prefix. Each item that
- * does not start with the supplied prefix is removed from the list. Managing focus and knowing if Search is active. The search UI is not a separate
* activity, and when the UI is invoked or dismissed, your activity will not typically be paused,
* resumed, or otherwise notified by the methods defined in
- * Activity Lifecycle. The search UI is
+ * Application Fundamentals:
+ * Activity Lifecycle. The search UI is
* handled in the same way as other system UI elements which may appear from time to time, such as
* notifications, screen locks, or other system alerts:
* When the search UI appears, your activity will lose input focus.
@@ -212,11 +213,11 @@ import android.view.KeyEvent;
* {@link #QUERY getStringExtra(SearchManager.QUERY)}.
* Metadata for searchable activity. As with your search implementations described
* above, you must first identify which of your activities is searchable. In the
- * manifest entry for this activity, you must
+ * manifest entry for this activity, you must
* provide two elements:
* Here is a snippet showing the necessary elements in the
- * manifest entry for your searchable activity.
+ * manifest entry for your searchable activity.
* For more complete documentation on this capability, see
- * Resources and
- * Internationalization: Supporting Alternate Resources for Alternate Languages and Configurations
- * .
+ * Resources and
+ * Internationalization: Alternate Resources.
*
* Metadata for non-searchable activities. Activities which are part of a searchable
* application, but don't implement search itself, require a bit of "glue" in order to cause
@@ -775,7 +783,7 @@ import android.view.KeyEvent;
* provided, then searches from these activities will use the system default search context.
*
* The simplest way to specify this is to add a search reference element to the
- * application entry in the manifest file.
+ * application entry in the manifest file.
* The value of this reference can be either of:
* First, in your manifest, you'll add the
+ * First, in your manifest, you'll add the
* following lines.
* The Service class is an important part of an
- * application's overall lifecycle. Topics covered here:
* A service can be both started and have connections bound to it. In such
@@ -106,7 +106,7 @@ import java.io.PrintWriter;
* {@link #checkCallingPermission}
* method before executing the implementation of that call.
*
- * See the Security Model
+ * See the Security and Permissions
* document for more information on permissions and security in general.
*
*
@@ -201,14 +201,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
* Return the communication channel to the service. May return null if
* clients can not bind to the service. The returned
* {@link android.os.IBinder} is usually for a complex interface
- * that has been described using
+ * that has been described using
* aidl.
*
* Note that unlike other application components, calls on to the
* IBinder interface returned here may not happen on the main thread
* of the process. More information about this can be found
- * in the Threading section
- * of the Application Model overview.
+ * You must supply the following extras:
+ *
+ * The system will respond with an onActivityResult call with the following extras in
+ * the intent:
+ *
+ * When you receive the result from the AppWidget pick activity, if the resultCode is
+ * {@link android.app.Activity#RESULT_OK}, an AppWidget has been selected. You should then
+ * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its configuration
+ * activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you should delete
+ * the appWidgetId.
+ *
+ * @see #ACTION_APPWIDGET_CONFIGURE
+ */
+ public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
+
+ /**
+ * Sent when it is time to configure your AppWidget while it is being added to a host.
+ * This action is not sent as a broadcast to the AppWidget provider, but as a startActivity
+ * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo meta-data}.
+ *
+ *
+ * The intent will contain the following extras:
+ * If you return {@link android.app.Activity#RESULT_OK} using
+ * {@link android.app.Activity#setResult Activity.setResult()}, the AppWidget will be added,
+ * and you will receive an {@link #ACTION_APPWIDGET_UPDATE} broadcast for this AppWidget.
+ * If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add
+ * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED} broadcast.
+ */
+ public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
+
+ /**
+ * An intent extra that contains one appWidgetId.
+ *
+ * The value will be an int that can be retrieved like this:
+ * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/AppWidgetHostActivity.java getExtra_EXTRA_APPWIDGET_ID}
+ */
+ public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
+
+ /**
+ * An intent extra that contains multiple appWidgetIds.
+ *
+ * The value will be an int array that can be retrieved like this:
+ * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS}
+ */
+ public static final String EXTRA_APPWIDGET_IDS = "appWidgetIds";
+
+ /**
+ * A sentiel value that the AppWidget manager will never return as a appWidgetId.
+ */
+ public static final int INVALID_APPWIDGET_ID = 0;
+
+ /**
+ * Sent when it is time to update your AppWidget.
+ *
+ * This may be sent in response to a new instance for this AppWidget provider having
+ * been instantiated, the requested {@link AppWidgetProviderInfo#updatePeriodMillis update interval}
+ * having lapsed, or the system booting.
+ *
+ *
+ * The intent will contain the following extras:
+ *
+ * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+ * and outside of the handler.
+ * This method will only work when called from the uid that owns the AppWidget provider.
+ *
+ * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
+ * @param views The RemoteViews object to show.
+ */
+ public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
+ try {
+ sService.updateAppWidgetIds(appWidgetIds, views);
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ /**
+ * Set the RemoteViews to use for the specified appWidgetId.
+ *
+ *
+ * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+ * and outside of the handler.
+ * This method will only work when called from the uid that owns the AppWidget provider.
+ *
+ * @param appWidgetId The AppWidget instance for which to set the RemoteViews.
+ * @param views The RemoteViews object to show.
+ */
+ public void updateAppWidget(int appWidgetId, RemoteViews views) {
+ updateAppWidget(new int[] { appWidgetId }, views);
+ }
+
+ /**
+ * Set the RemoteViews to use for all AppWidget instances for the supplied AppWidget provider.
+ *
+ *
+ * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
+ * and outside of the handler.
+ * This method will only work when called from the uid that owns the AppWidget provider.
+ *
+ * @param provider The {@link ComponentName} for the {@link
+ * android.content.BroadcastReceiver BroadcastReceiver} provider
+ * for your AppWidget.
+ * @param views The RemoteViews object to show.
+ */
+ public void updateAppWidget(ComponentName provider, RemoteViews views) {
+ try {
+ sService.updateAppWidgetProvider(provider, views);
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ /**
+ * Return a list of the AppWidget providers that are currently installed.
+ */
+ public List You need the APPWIDGET_LIST permission. This method is to be used by the
+ * AppWidget picker.
+ *
+ * @param appWidgetId The AppWidget instance for which to set the RemoteViews.
+ * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget
+ * provider for this AppWidget.
+ */
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
+ try {
+ sService.bindAppWidgetId(appWidgetId, provider);
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ /**
+ * Get the list of appWidgetIds that have been bound to the given AppWidget
+ * provider.
+ *
+ * @param provider The {@link android.content.BroadcastReceiver} that is the
+ * AppWidget provider to find appWidgetIds for.
+ */
+ public int[] getAppWidgetIds(ComponentName provider) {
+ try {
+ return sService.getAppWidgetIds(provider);
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+}
+
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
new file mode 100755
index 0000000000000000000000000000000000000000..f70de9cde67e09bdf339327008168381ecd8296f
--- /dev/null
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appwidget;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * A conveience class to aid in implementing an AppWidget provider.
+ * Everything you can do with AppWidgetProvider, you can do with a regular {@link BroadcastReceiver}.
+ * AppWidgetProvider merely parses the relevant fields out of the Intent that is received in
+ * {@link #onReceive(Context,Intent) onReceive(Context,Intent)}, and calls hook methods
+ * with the received extras.
+ *
+ * Extend this class and override one or more of the {@link #onUpdate}, {@link #onDeleted},
+ * {@link #onEnabled} or {@link #onDisabled} methods to implement your own AppWidget functionality.
+ *
+ * This field corresponds to the This field corresponds to the This field corresponds to the This field corresponds to the This field corresponds to the This class name of field corresponds to the This field corresponds to the This field corresponds to the Android allows applications to publish views to be embedded in other applications. These
+views are called widgets, and are published by "AppWidget providers." The component that can
+contain widgets is called a "AppWidget host."
+
+Any application can publish widgets. All an application needs to do to publish a widget is
+to have a {@link android.content.BroadcastReceiver} that receives the {@link
+android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE AppWidgetManager.ACTION_APPWIDGET_UPDATE} intent,
+and provide some meta-data about the widget. Android provides the
+{@link android.appwidget.AppWidgetProvider} class, which extends BroadcastReceiver, as a convenience
+class to aid in handling the broadcasts.
+
+
+First, declare the {@link android.content.BroadcastReceiver} in your application's
+
+The
+The
+The
+For a widget, the values in the {@link android.appwidget.AppWidgetProviderInfo} structure are supplied
+in an XML resource. In the example above, the xml resource is referenced with
+
+The attributes are as documented in the {@link android.appwidget.AppWidgetProviderInfo GagetInfo} class. (86400000 milliseconds means once per day)
+
+
+ The AppWidgetProvider class is the easiest way to handle the widget provider intent broadcasts.
+See the Keep in mind that since the the AppWidgetProvider is a BroadcastReceiver,
+your process is not guaranteed to keep running after the callback methods return. See
+Application Fundamentals >
+Broadcast Receiver Lifecycle for more information.
+
+
+
+
+Widget hosts have the ability to start a configuration activity when a widget is instantiated.
+The activity should be declared as normal in AndroidManifest.xml, and it should be listed in
+the AppWidgetProviderInfo XML file in the The activity you specified will be launched with the {@link
+android.appwidget.AppWidgetManager#ACTION_APPWIDGET_CONFIGURE} action. See the documentation for that
+action for more info.
+
+ See the {@link android.appwidget.AppWidgetProvider} is just a convenience class. If you would like
+to receive the widget broadcasts directly, you can. The four intents you need to care about are:
+ By way of example, the implementation of
+{@link android.appwidget.AppWidgetProvider#onReceive} is quite simple: Widget hosts are the containers in which widgets can be placed. Most of the look and feel
+details are left up to the widget hosts. For example, the home screen has one way of viewing
+widgets, but the lock screen could also contain widgets, and it would have a different way of
+adding, removing and otherwise managing widgets. For more information on implementing your own widget host, see the
+{@link android.appwidget.AppWidgetHost AppWidgetHost} class. The BroadcastReceiver class (when launched as a component through
* a manifest's {@link android.R.styleable#AndroidManifestReceiver <receiver>}
* tag) is an important part of an
- * application's overall lifecycle. Topics covered here:
* See the Security Model
+ * See the Security and Permissions
* document for more information on permissions and security in general.
*
*
@@ -175,13 +179,35 @@ public abstract class BroadcastReceiver {
* return a result to you asynchronously -- in particular, for interacting
* with services, you should use
* {@link Context#startService(Intent)} instead of
- * {@link Context#bindService(Intent, ServiceConnection, int)}.
+ * {@link Context#bindService(Intent, ServiceConnection, int)}. If you wish
+ * to interact with a service that is already running, you can use
+ * {@link #peekService}.
*
* @param context The Context in which the receiver is running.
* @param intent The Intent being received.
*/
public abstract void onReceive(Context context, Intent intent);
+ /**
+ * Provide a binder to an already-running service. This method is synchronous
+ * and will not start the target service if it is not present, so it is safe
+ * to call from {@link #onReceive}.
+ *
+ * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)}
+ * @param service The Intent indicating the service you wish to use. See {@link
+ * Context#startService(Intent)} for more information.
+ */
+ public IBinder peekService(Context myContext, Intent service) {
+ IActivityManager am = ActivityManagerNative.getDefault();
+ IBinder binder = null;
+ try {
+ binder = am.peekService(service, service.resolveTypeIfNeeded(
+ myContext.getContentResolver()));
+ } catch (RemoteException e) {
+ }
+ return binder;
+ }
+
/**
* Change the current result code of this broadcast; only works with
* broadcasts sent through
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 226c5ab77ebc5eff6602a15a67dfb6b194ad1384..25544deac10d1ee2eb2eacdf6dd371bbd3fe0cd6 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -18,6 +18,7 @@ package android.content;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
+import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.database.Cursor;
import android.database.CursorToBulkCursorAdaptor;
@@ -41,8 +42,8 @@ import java.io.FileNotFoundException;
* multiple applications you can use a database directly via
* {@link android.database.sqlite.SQLiteDatabase}.
*
- * See this page for more information on
- * content providers. For more information, read Content
+ * Providers. When a request is made via
* a {@link ContentResolver} the system inspects the authority of the given URI and passes the
@@ -162,6 +163,13 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.openFile(uri, mode);
}
+ public AssetFileDescriptor openAssetFile(Uri uri, String mode)
+ throws FileNotFoundException {
+ if (mode != null && mode.startsWith("rw")) checkWritePermission(uri);
+ else checkReadPermission(uri);
+ return ContentProvider.this.openAssetFile(uri, mode);
+ }
+
public ISyncAdapter getSyncAdapter() {
checkWritePermission(null);
return ContentProvider.this.getSyncAdapter().getISyncAdapter();
@@ -226,9 +234,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
/**
* Return the name of the permission required for read-only access to
* this content provider. This method can be called from multiple
- * threads, as described in the
- * Threading section of
- * the Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
*/
public final String getReadPermission() {
return mReadPermission;
@@ -248,9 +256,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
/**
* Return the name of the permission required for read/write access to
* this content provider. This method can be called from multiple
- * threads, as described in the
- * Threading section of
- * the Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
*/
public final String getWritePermission() {
return mWritePermission;
@@ -273,9 +281,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
* Receives a query request from a client in a local process, and
* returns a Cursor. This is called internally by the {@link ContentResolver}.
* This method can be called from multiple
- * threads, as described in the
- * Threading section of
- * the Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
*
* Example client call:
* The implementation is responsible for parsing out a row ID at the end
* of the URI, if a specific row is being deleted. That is, the client would
@@ -405,9 +413,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after updating.
* This method can be called from multiple
- * threads, as described in the
- * Threading section of the
- * Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
*
* @param uri The URI to query. This can potentially have a record ID if this
* is an update request for a specific record.
@@ -422,9 +430,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
/**
* Open a file blob associated with a content URI.
* This method can be called from multiple
- * threads, as described in the
- * Threading section of the
- * Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
*
* Returns a
* ParcelFileDescriptor, from which you can obtain a
@@ -438,8 +446,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
* of this method should create a new ParcelFileDescriptor for each call.
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access
- * or "rw" for read and write access.
+ * @param mode Access mode for the file. May be "r" for read-only access,
+ * "rw" for read and write access, or "rwt" for read and write access
+ * that truncates any existing file.
*
* @return Returns a new ParcelFileDescriptor which you can use to access
* the file.
@@ -448,19 +457,66 @@ public abstract class ContentProvider implements ComponentCallbacks {
* no file associated with the given URI or the mode is invalid.
* @throws SecurityException Throws SecurityException if the caller does
* not have permission to access the file.
- */
+ *
+ * @see #openAssetFile(Uri, String)
+ * @see #openFileHelper(Uri, String)
+ */
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
throw new FileNotFoundException("No files supported by provider at "
+ uri);
}
+
+ /**
+ * This is like {@link #openFile}, but can be implemented by providers
+ * that need to be able to return sub-sections of files, often assets
+ * inside of their .apk. Note that when implementing this your clients
+ * must be able to deal with such files, either directly with
+ * {@link ContentResolver#openAssetFileDescriptor
+ * ContentResolver.openAssetFileDescriptor}, or by using the higher-level
+ * {@link ContentResolver#openInputStream ContentResolver.openInputStream}
+ * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
+ * methods.
+ *
+ * Note: if you are implementing this to return a full file, you
+ * should create the AssetFileDescriptor with
+ * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
+ * applications that can not handle sub-sections of files.
- * A Uri object can be used to reference a resource in an APK file. The
- * Uri should be one of the following formats:
- * See {@link #openAssetFileDescriptor(Uri, String)} for more information
+ * on these schemes.
+ *
+ * @param uri The desired URI.
* @return InputStream
* @throws FileNotFoundException if the provided URI could not be opened.
+ * @see #openAssetFileDescriptor(Uri, String)
*/
public final InputStream openInputStream(Uri uri)
throws FileNotFoundException {
String scheme = uri.getScheme();
- if (SCHEME_CONTENT.equals(scheme)) {
- ParcelFileDescriptor fd = openFileDescriptor(uri, "r");
- return fd != null ? new ParcelFileDescriptor.AutoCloseInputStream(fd) : null;
- } else if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
- String authority = uri.getAuthority();
- Resources r;
- if (TextUtils.isEmpty(authority)) {
- throw new FileNotFoundException("No authority: " + uri);
- } else {
- try {
- r = mContext.getPackageManager().getResourcesForApplication(authority);
- } catch (NameNotFoundException ex) {
- throw new FileNotFoundException("No package found for authority: " + uri);
- }
- }
- List See {@link #openAssetFileDescriptor(Uri, String)} for more information
+ * on these schemes.
+ *
+ * @param uri The desired URI.
+ * @param mode May be "w", "wa", "rw", or "rwt".
* @return OutputStream
+ * @throws FileNotFoundException if the provided URI could not be opened.
+ * @see #openAssetFileDescriptor(Uri, String)
*/
- public final OutputStream openOutputStream(Uri uri)
+ public final OutputStream openOutputStream(Uri uri, String mode)
throws FileNotFoundException {
- String scheme = uri.getScheme();
- if (SCHEME_CONTENT.equals(scheme)) {
- ParcelFileDescriptor fd = openFileDescriptor(uri, "rw");
- return fd != null
- ? new ParcelFileDescriptor.AutoCloseOutputStream(fd) : null;
- } else {
- throw new FileNotFoundException("Unknown scheme: " + uri);
+ AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode);
+ try {
+ return fd != null ? fd.createOutputStream() : null;
+ } catch (IOException e) {
+ throw new FileNotFoundException("Unable to create stream");
}
}
/**
* Open a raw file descriptor to access data under a "content:" URI. This
- * interacts with the underlying {@link ContentProvider#openFile}
- * ContentProvider.openFile()} method of the provider associated with the
- * given URI, to retrieve any file stored there.
+ * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
+ * underlying {@link ContentProvider#openFile}
+ * ContentProvider.openFile()} method, so will not work with
+ * providers that return sub-sections of files. If at all possible,
+ * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
+ * will receive a FileNotFoundException exception if the provider returns a
+ * sub-section of a file.
*
* See {@link #openAssetFileDescriptor(Uri, String)} for more information
+ * on these schemes.
+ *
* @param uri The desired URI to open.
* @param mode The file mode to use, as per {@link ContentProvider#openFile
* ContentProvider.openFile}.
@@ -290,32 +273,189 @@ public abstract class ContentResolver {
* own this descriptor and are responsible for closing it when done.
* @throws FileNotFoundException Throws FileNotFoundException of no
* file exists under the URI or the mode is invalid.
+ * @see #openAssetFileDescriptor(Uri, String)
*/
public final ParcelFileDescriptor openFileDescriptor(Uri uri,
String mode) throws FileNotFoundException {
- IContentProvider provider = acquireProvider(uri);
- if (provider == null) {
- throw new FileNotFoundException("No content provider: " + uri);
+ AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode);
+ if (afd == null) {
+ return null;
+ }
+
+ if (afd.getDeclaredLength() < 0) {
+ // This is a full file!
+ return afd.getParcelFileDescriptor();
}
+
+ // Client can't handle a sub-section of a file, so close what
+ // we got and bail with an exception.
try {
- ParcelFileDescriptor fd = provider.openFile(uri, mode);
- if(fd == null) {
+ afd.close();
+ } catch (IOException e) {
+ }
+
+ throw new FileNotFoundException("Not a whole file");
+ }
+
+ /**
+ * Open a raw file descriptor to access data under a "content:" URI. This
+ * interacts with the underlying {@link ContentProvider#openAssetFile}
+ * ContentProvider.openAssetFile()} method of the provider associated with the
+ * given URI, to retrieve any file stored there.
+ *
+ *
+ * A Uri object can be used to reference a resource in an APK file. The
+ * Uri should be one of the following formats:
+ * Input: Nothing.
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SEARCH_LONG_PRESS = "android.intent.action.SEARCH_LONG_PRESS";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
@@ -1041,6 +1052,14 @@ public class Intent implements Parcelable {
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
+
+ /**
+ * Broadcast Action: Sent when the user is present after device wakes up (e.g when the
+ * keyguard is gone).
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_USER_PRESENT= "android.intent.action.USER_PRESENT";
+
/**
* Broadcast Action: The current time has changed. Sent every
* minute. You can not receive this through components declared
@@ -1109,6 +1128,12 @@ public class Intent implements Parcelable {
/**
* Broadcast Action: A new application package has been installed on the
* device. The data contains the name of the package.
+ * My include the following extras:
+ * Note: being removed in favor of more explicit categories such as
- * CATEGORY_GADGET
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_EMBED = "android.intent.category.EMBED";
@@ -1676,6 +1723,22 @@ public class Intent implements Parcelable {
*/
public static final String EXTRA_UID = "android.intent.extra.UID";
+ /**
+ * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
+ * intents to indicate whether this represents a full uninstall (removing
+ * both the code and its data) or a partial uninstall (leaving its data,
+ * implying that this is an update).
+ */
+ public static final String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
+
+ /**
+ * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED}
+ * intents to indicate that this is a replacement of the package, so this
+ * broadcast will immediately be followed by an add broadcast for a
+ * different version of the same package.
+ */
+ public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
+
/**
* Used as an int extra field in {@link android.app.AlarmManager} intents
* to tell the application being invoked how many pending alarms are being
@@ -1739,9 +1802,9 @@ public class Intent implements Parcelable {
* next task activity) defines an atomic group of activities that the
* user can move to. Tasks can be moved to the foreground and background;
* all of the activities inside of a particular task always remain in
- * the same order. See the
- * Application Model
- * documentation for more details on tasks.
+ * the same order. See
+ * Application Fundamentals:
+ * Activities and Tasks for more details on tasks.
*
* This flag is generally used by activities that want
* to present a "launcher" style behavior: they give the user a list of
@@ -1774,9 +1837,8 @@ public class Intent implements Parcelable {
* This flag is ignored if
* {@link #FLAG_ACTIVITY_NEW_TASK} is not set.
*
- * See the
- * Application Model
- * documentation for more details on tasks.
+ * See Application Fundamentals:
+ * Activities and Tasks for more details on tasks.
*/
public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;
/**
@@ -1791,8 +1853,8 @@ public class Intent implements Parcelable {
* Intent, resulting in the stack now being: A, B.
*
* The currently running instance of task B in the above example will
- * either receiving the new intent you are starting here in its
- * onNewIntent() method, or be itself finished and restarting with the
+ * either receive the new intent you are starting here in its
+ * onNewIntent() method, or be itself finished and restarted with the
* new intent. If it has declared its launch mode to be "multiple" (the
* default) it will be finished and re-created; for all other launch modes
* it will receive the Intent in the current instance.
@@ -1804,9 +1866,8 @@ public class Intent implements Parcelable {
* especially useful, for example, when launching an activity from the
* notification manager.
*
- * See the
- * Application Model
- * documentation for more details on tasks.
+ * See Application Fundamentals:
+ * Activities and Tasks for more details on tasks.
*/
public static final int FLAG_ACTIVITY_CLEAR_TOP = 0x04000000;
/**
@@ -1876,7 +1937,7 @@ public class Intent implements Parcelable {
*/
public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000;
/**
- * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaving}
+ * If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint}
* callback from occurring on the current frontmost activity before it is
* paused as the newly-started activity is brought to the front.
*
@@ -1892,12 +1953,39 @@ public class Intent implements Parcelable {
* activity does not think the user has acknowledged its notification.
*/
public static final int FLAG_ACTIVITY_NO_USER_ACTION = 0x00040000;
-
+ /**
+ * If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
+ * this flag will cause the launched activity to be brought to the front of its
+ * task's history stack if it is already running.
+ *
+ * For example, consider a task consisting of four activities: A, B, C, D.
+ * If D calls startActivity() with an Intent that resolves to the component
+ * of activity B, then B will be brought to the front of the history stack,
+ * with this resulting order: A, C, D, B.
+ *
+ * This flag will be ignored if {@link #FLAG_ACTIVITY_CLEAR_TOP} is also
+ * specified.
+ */
+ public static final int FLAG_ACTIVITY_REORDER_TO_FRONT = 0X00020000;
/**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
*/
public static final int FLAG_RECEIVER_REGISTERED_ONLY = 0x40000000;
+ /**
+ * If set, when sending a broadcast before boot has completed only
+ * registered receivers will be called -- no BroadcastReceiver components
+ * will be launched. Sticky intent state will be recorded properly even
+ * if no receivers wind up being called. If {@link #FLAG_RECEIVER_REGISTERED_ONLY}
+ * is specified in the broadcast intent, this flag is unnecessary.
+ *
+ * This flag is only for use by system sevices as a convenience to
+ * avoid having to implement a more complex mechanism around detection
+ * of boot completion.
+ *
+ * @hide
+ */
+ public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000;
// ---------------------------------------------------------------------
@@ -3865,8 +3953,8 @@ public class Intent implements Parcelable {
* FLAG_RECEIVER_* flags are all for use with
* {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}.
*
- * See the Application Model
- * documentation for important information on how some of these options impact
+ * See the Application Fundamentals:
+ * Activities and Tasks documentation for important information on how some of these options impact
* the behavior of your application.
*
* @param flags The desired flags.
@@ -4141,14 +4229,11 @@ public class Intent implements Parcelable {
@Override
public boolean equals(Object obj) {
- Intent other;
- try {
- other = ((FilterComparison)obj).mIntent;
- } catch (ClassCastException e) {
- return false;
+ if (obj instanceof FilterComparison) {
+ Intent other = ((FilterComparison)obj).mIntent;
+ return mIntent.filterEquals(other);
}
-
- return mIntent.filterEquals(other);
+ return false;
}
@Override
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 6bc3774859b0db9d31e9c6ba8f3e6c248d1edde6..96470c3c5756c941e3d5941e5f73d6780e565985 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -123,7 +123,7 @@ class SyncManager {
private static final String SYNC_WAKE_LOCK = "SyncManagerSyncWakeLock";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
-
+
private Context mContext;
private ContentResolver mContentResolver;
@@ -249,7 +249,7 @@ class SyncManager {
mSyncQueue = new SyncQueue(mSyncStorageEngine);
mContext = context;
-
+
mSyncThread = new HandlerThread("SyncHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
mSyncThread.start();
mSyncHandler = new SyncHandler(mSyncThread.getLooper());
@@ -489,7 +489,7 @@ class SyncManager {
// Require the precise value "yes" to discourage accidental activation.
return "yes".equals(SystemProperties.get("ro.config.sync"));
}
-
+
/**
* Initiate a sync. This can start a sync for all providers
* (pass null to url, set onlyTicklable to false), only those
@@ -515,7 +515,7 @@ class SyncManager {
* syncs of a specific provider. Can be null. Is ignored
* if the url is null.
* @param delay how many milliseconds in the future to wait before performing this
- * sync. -1 means to make this the next sync to perform.
+ * sync. -1 means to make this the next sync to perform.
*/
public void scheduleSync(Uri url, Bundle extras, long delay) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
@@ -694,7 +694,7 @@ class SyncManager {
class SyncHandlerMessagePayload {
public final ActiveSyncContext activeSyncContext;
public final SyncResult syncResult;
-
+
SyncHandlerMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult) {
this.activeSyncContext = syncContext;
this.syncResult = syncResult;
@@ -740,7 +740,7 @@ class SyncManager {
if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
}
-
+
SyncOperation rescheduledSyncOperation = new SyncOperation(syncOperation);
rescheduledSyncOperation.setDelay(newDelayInMs);
scheduleSyncOperation(rescheduledSyncOperation);
@@ -786,7 +786,7 @@ class SyncManager {
// key than the one we are scheduling.
if (!activeIsExpedited && !hasSameKey) {
rescheduleImmediately(activeSyncContext.mSyncOperation);
- sendSyncFinishedOrCanceledMessage(activeSyncContext,
+ sendSyncFinishedOrCanceledMessage(activeSyncContext,
null /* no result since this is a cancel */);
}
}
@@ -1323,7 +1323,7 @@ class SyncManager {
public SyncHandler(Looper looper) {
super(looper);
}
-
+
public void handleMessage(Message msg) {
handleSyncHandlerMessage(msg);
}
@@ -1462,6 +1462,9 @@ class SyncManager {
// start it, otherwise just get out.
SyncOperation syncOperation;
final Sync.Settings.QueryMap syncSettings = getSyncSettings();
+ final ConnectivityManager connManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final boolean backgroundDataSetting = connManager.getBackgroundDataSetting();
synchronized (mSyncQueue) {
while (true) {
syncOperation = mSyncQueue.head();
@@ -1484,10 +1487,10 @@ class SyncManager {
// skip the sync if it isn't a force and the settings are off for this provider
final boolean force = syncOperation.extras.getBoolean(
ContentResolver.SYNC_EXTRAS_FORCE, false);
- if (!force && (!syncSettings.getBackgroundData()
+ if (!force && (!backgroundDataSetting
|| !syncSettings.getListenForNetworkTickles()
|| !syncSettings.getSyncProviderAutomatically(
- syncOperation.authority))) {
+ syncOperation.authority))) {
if (isLoggable) {
Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation);
}
@@ -1669,7 +1672,7 @@ class SyncManager {
* @param syncResult the SyncResult from which to read
* @return the most "serious" error set in the SyncResult
* @throws IllegalStateException if the SyncResult does not indicate any errors.
- * If SyncResult.error() is true then it is safe to call this.
+ * If SyncResult.error() is true then it is safe to call this.
*/
private int syncResultToErrorNumber(SyncResult syncResult) {
if (syncResult.syncAlreadyInProgress) return History.ERROR_SYNC_ALREADY_IN_PROGRESS;
@@ -1679,7 +1682,8 @@ class SyncManager {
if (syncResult.stats.numConflictDetectedExceptions > 0) return History.ERROR_CONFLICT;
if (syncResult.tooManyDeletions) return History.ERROR_TOO_MANY_DELETIONS;
if (syncResult.tooManyRetries) return History.ERROR_TOO_MANY_RETRIES;
- throw new IllegalStateException("we are not in an error state, " + toString());
+ if (syncResult.databaseError) return History.ERROR_INTERNAL;
+ throw new IllegalStateException("we are not in an error state, " + syncResult);
}
private void manageSyncNotification() {
@@ -1717,7 +1721,7 @@ class SyncManager {
if (mSyncNotificationInfo.isActive) {
shouldInstall = shouldCancel;
} else {
- final boolean timeToShowNotification =
+ final boolean timeToShowNotification =
now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
final boolean syncIsForced = syncOperation.extras
.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
@@ -1769,7 +1773,7 @@ class SyncManager {
if (!mDataConnectionIsConnected) return;
if (mAccounts == null) return;
if (mStorageIsLow) return;
-
+
// Compute the alarm fire time:
// - not syncing: time of the next sync operation
// - syncing, no notification: time from sync start to notification create time
@@ -1850,12 +1854,12 @@ class SyncManager {
clickIntent.putExtra("account", account);
clickIntent.putExtra("provider", authority);
clickIntent.putExtra("numDeletes", numDeletes);
-
+
if (!isActivityAvailable(clickIntent)) {
Log.w(TAG, "No activity found to handle too many deletes.");
return;
}
-
+
final PendingIntent pendingIntent = PendingIntent
.getActivity(mContext, 0, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);
@@ -1877,7 +1881,7 @@ class SyncManager {
/**
* Checks whether an activity exists on the system image for the given intent.
- *
+ *
* @param intent The intent for an activity.
* @return Whether or not an activity exists.
*/
@@ -1892,10 +1896,10 @@ class SyncManager {
return true;
}
}
-
+
return false;
}
-
+
public long insertStartSyncEvent(SyncOperation syncOperation) {
final int source = syncOperation.syncSource;
final long now = System.currentTimeMillis();
diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java
index 78510aad987a0ac4420443ad7d307f8438ecadc3..eb3a5da4a5b12e4c300fb6abc38dd39d44c14855 100644
--- a/core/java/android/content/TempProviderSyncAdapter.java
+++ b/core/java/android/content/TempProviderSyncAdapter.java
@@ -1,11 +1,11 @@
package android.content;
-import com.google.android.net.NetStats;
-
import android.database.SQLException;
import android.os.Bundle;
import android.os.Debug;
+import android.os.NetStat;
import android.os.Parcelable;
+import android.os.Process;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Config;
@@ -177,7 +177,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
private final Bundle mExtras;
private final SyncContext mSyncContext;
private volatile boolean mIsCanceled = false;
- private long[] mNetStats;
+ private long mInitialTxBytes;
+ private long mInitialRxBytes;
private final SyncResult mResult;
SyncThread(SyncContext syncContext, String account, Bundle extras) {
@@ -193,15 +194,18 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
if (mAdapterSyncStarted) onSyncCanceled();
if (mProviderSyncStarted) mProvider.onSyncCanceled();
// We may lose the last few sync events when canceling. Oh well.
- long[] newNetStats = NetStats.getStats();
- logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult);
+ int uid = Process.myUid();
+ logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes,
+ NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult);
}
@Override
public void run() {
- android.os.Process.setThreadPriority(android.os.Process.myTid(),
- android.os.Process.THREAD_PRIORITY_BACKGROUND);
- mNetStats = NetStats.getStats();
+ Process.setThreadPriority(Process.myTid(),
+ Process.THREAD_PRIORITY_BACKGROUND);
+ int uid = Process.myUid();
+ mInitialTxBytes = NetStat.getUidTxBytes(uid);
+ mInitialRxBytes = NetStat.getUidRxBytes(uid);
try {
sync(mSyncContext, mAccount, mExtras);
} catch (SQLException e) {
@@ -210,8 +214,8 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
} finally {
mSyncThread = null;
if (!mIsCanceled) {
- long[] newNetStats = NetStats.getStats();
- logSyncDetails(newNetStats[0] - mNetStats[0], newNetStats[1] - mNetStats[1], mResult);
+ logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes,
+ NetStat.getUidRxBytes(uid) - mInitialRxBytes, mResult);
mSyncContext.onFinished(mResult);
}
}
diff --git a/core/java/android/content/package.html b/core/java/android/content/package.html
index 7b3e8cfae7b1eadc5a0a8057753001b3c58c0f80..dd5360f651b4631ab6d071852dd2bac0f9d711e6 100644
--- a/core/java/android/content/package.html
+++ b/core/java/android/content/package.html
@@ -50,9 +50,9 @@ an application's resources and transfer data between applications. This topic includes a terminology list associated with resources, and a series
of examples of using resources in code. For a complete guide on creating and
- using resources, see the document on Resources
+ using resources, see the document on Resources
and Internationalization. For a reference on the supported Android resource types,
- see Available Resource Types. The Android resource system keeps track of all non-code
assets associated with an application. You use the
{@link android.content.res.Resources Resources} class to access your
@@ -175,7 +175,8 @@ download files with new appearances. This section gives a few quick examples you can use to make your own resources.
For more details on how to define and use resources, see Resources. Throws {@link NameNotFoundException} if a package with the given
+ * name can not be found on the system.
+ *
+ * @param packageName The name of the package to inspect.
+ *
+ * @return Returns either a fully-qualified Intent that can be used to
+ * launch the main activity in the package, or null if the package does
+ * not contain such an activity.
+ */
+ public abstract Intent getLaunchIntentForPackage(String packageName)
+ throws NameNotFoundException;
+
/**
* Return an array of all of the secondary group-ids that have been
* assigned to a package.
@@ -851,6 +871,16 @@ public abstract class PackageManager {
* @see #GET_UNINSTALLED_PACKAGES
*/
public abstract List For an example, see the NotePadProvider class in the NotePad sample application,
+ * in the samples/ directory of the SDK.
- * The system will respond with an onActivityResult call with the following extras in
- * the intent:
- *
- * This method will only work when called from the uid that owns the gadget provider.
- *
- * @param gadgetId The gadget instance for which to set the RemoteViews.
- * @param views The RemoteViews object to show.
- */
- public void updateGadget(int gadgetId, RemoteViews views) {
- }
-
- /**
- * Return a list of the gadget providers that are currently installed.
- */
- public List This uses the World Magnetic Model produced by the United States National
+ * Geospatial-Intelligence Agency. More details about the model can be found at
+ * http://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml.
+ * This class currently uses WMM-2005 which is valid until 2010, but should
+ * produce acceptable results for several years after that.
+ */
+public class GeomagneticField {
+ // The magnetic field at a given point, in nonoteslas in geodetic
+ // coordinates.
+ private float mX;
+ private float mY;
+ private float mZ;
+
+ // Geocentric coordinates -- set by computeGeocentricCoordinates.
+ private float mGcLatitudeRad;
+ private float mGcLongitudeRad;
+ private float mGcRadiusKm;
+
+ // Constants from WGS84 (the coordinate system used by GPS)
+ static private final float EARTH_SEMI_MAJOR_AXIS_KM = 6378.137f;
+ static private final float EARTH_SEMI_MINOR_AXIS_KM = 6356.7523f;
+ static private final float EARTH_REFERENCE_RADIUS_KM = 6371.2f;
+
+ // These coefficients and the formulae used below are from:
+ // NOAA Technical Report: The US/UK World Magnetic Model for 2005-2010
+ static private final float[][] G_COEFF = new float[][] {
+ { 0f },
+ { -29556.8f, -1671.7f },
+ { -2340.6f, 3046.9f, 1657.0f },
+ { 1335.4f, -2305.1f, 1246.7f, 674.0f },
+ { 919.8f, 798.1f, 211.3f, -379.4f, 100.0f },
+ { -227.4f, 354.6f, 208.7f, -136.5f, -168.3f, -14.1f },
+ { 73.2f, 69.7f, 76.7f, -151.2f, -14.9f, 14.6f, -86.3f },
+ { 80.1f, -74.5f, -1.4f, 38.5f, 12.4f, 9.5f, 5.7f, 1.8f },
+ { 24.9f, 7.7f, -11.6f, -6.9f, -18.2f, 10.0f, 9.2f, -11.6f, -5.2f },
+ { 5.6f, 9.9f, 3.5f, -7.0f, 5.1f, -10.8f, -1.3f, 8.8f, -6.7f, -9.1f },
+ { -2.3f, -6.3f, 1.6f, -2.6f, 0.0f, 3.1f, 0.4f, 2.1f, 3.9f, -0.1f, -2.3f },
+ { 2.8f, -1.6f, -1.7f, 1.7f, -0.1f, 0.1f, -0.7f, 0.7f, 1.8f, 0.0f, 1.1f, 4.1f },
+ { -2.4f, -0.4f, 0.2f, 0.8f, -0.3f, 1.1f, -0.5f, 0.4f, -0.3f, -0.3f, -0.1f,
+ -0.3f, -0.1f } };
+
+ static private final float[][] H_COEFF = new float[][] {
+ { 0f },
+ { 0.0f, 5079.8f },
+ { 0.0f, -2594.7f, -516.7f },
+ { 0.0f, -199.9f, 269.3f, -524.2f },
+ { 0.0f, 281.5f, -226.0f, 145.8f, -304.7f },
+ { 0.0f, 42.4f, 179.8f, -123.0f, -19.5f, 103.6f },
+ { 0.0f, -20.3f, 54.7f, 63.6f, -63.4f, -0.1f, 50.4f },
+ { 0.0f, -61.5f, -22.4f, 7.2f, 25.4f, 11.0f, -26.4f, -5.1f },
+ { 0.0f, 11.2f, -21.0f, 9.6f, -19.8f, 16.1f, 7.7f, -12.9f, -0.2f },
+ { 0.0f, -20.1f, 12.9f, 12.6f, -6.7f, -8.1f, 8.0f, 2.9f, -7.9f, 6.0f },
+ { 0.0f, 2.4f, 0.2f, 4.4f, 4.8f, -6.5f, -1.1f, -3.4f, -0.8f, -2.3f, -7.9f },
+ { 0.0f, 0.3f, 1.2f, -0.8f, -2.5f, 0.9f, -0.6f, -2.7f, -0.9f, -1.3f, -2.0f, -1.2f },
+ { 0.0f, -0.4f, 0.3f, 2.4f, -2.6f, 0.6f, 0.3f, 0.0f, 0.0f, 0.3f, -0.9f, -0.4f,
+ 0.8f } };
+
+ static private final float[][] DELTA_G = new float[][] {
+ { 0f },
+ { 8.0f, 10.6f },
+ { -15.1f, -7.8f, -0.8f },
+ { 0.4f, -2.6f, -1.2f, -6.5f },
+ { -2.5f, 2.8f, -7.0f, 6.2f, -3.8f },
+ { -2.8f, 0.7f, -3.2f, -1.1f, 0.1f, -0.8f },
+ { -0.7f, 0.4f, -0.3f, 2.3f, -2.1f, -0.6f, 1.4f },
+ { 0.2f, -0.1f, -0.3f, 1.1f, 0.6f, 0.5f, -0.4f, 0.6f },
+ { 0.1f, 0.3f, -0.4f, 0.3f, -0.3f, 0.2f, 0.4f, -0.7f, 0.4f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
+
+ static private final float[][] DELTA_H = new float[][] {
+ { 0f },
+ { 0.0f, -20.9f },
+ { 0.0f, -23.2f, -14.6f },
+ { 0.0f, 5.0f, -7.0f, -0.6f },
+ { 0.0f, 2.2f, 1.6f, 5.8f, 0.1f },
+ { 0.0f, 0.0f, 1.7f, 2.1f, 4.8f, -1.1f },
+ { 0.0f, -0.6f, -1.9f, -0.4f, -0.5f, -0.3f, 0.7f },
+ { 0.0f, 0.6f, 0.4f, 0.2f, 0.3f, -0.8f, -0.2f, 0.1f },
+ { 0.0f, -0.2f, 0.1f, 0.3f, 0.4f, 0.1f, -0.2f, 0.4f, 0.4f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f } };
+
+ static private final long BASE_TIME =
+ new GregorianCalendar(2005, 1, 1).getTimeInMillis();
+
+ // The ratio between the Gauss-normalized associated Legendre functions and
+ // the Schmid quasi-normalized ones. Compute these once staticly since they
+ // don't depend on input variables at all.
+ static private final float[][] SCHMIDT_QUASI_NORM_FACTORS =
+ computeSchmidtQuasiNormFactors(G_COEFF.length);
+
+ /**
+ * Estimate the magnetic field at a given point and time.
+ *
+ * @param gdLatitudeDeg
+ * Latitude in WGS84 geodetic coordinates -- positive is east.
+ * @param gdLongitudeDeg
+ * Longitude in WGS84 geodetic coordinates -- positive is north.
+ * @param altitudeMeters
+ * Altitude in WGS84 geodetic coordinates, in meters.
+ * @param timeMillis
+ * Time at which to evaluate the declination, in milliseconds
+ * since January 1, 1970. (approximate is fine -- the declination
+ * changes very slowly).
+ */
+ public GeomagneticField(float gdLatitudeDeg,
+ float gdLongitudeDeg,
+ float altitudeMeters,
+ long timeMillis) {
+ final int MAX_N = G_COEFF.length; // Maximum degree of the coefficients.
+
+ // We don't handle the north and south poles correctly -- pretend that
+ // we're not quite at them to avoid crashing.
+ gdLatitudeDeg = Math.min(90.0f - 1e-5f,
+ Math.max(-90.0f + 1e-5f, gdLatitudeDeg));
+ computeGeocentricCoordinates(gdLatitudeDeg,
+ gdLongitudeDeg,
+ altitudeMeters);
+
+ assert G_COEFF.length == H_COEFF.length;
+
+ // Note: LegendreTable computes associated Legendre functions for
+ // cos(theta). We want the associated Legendre functions for
+ // sin(latitude), which is the same as cos(PI/2 - latitude), except the
+ // derivate will be negated.
+ LegendreTable legendre =
+ new LegendreTable(MAX_N - 1,
+ (float) (Math.PI / 2.0 - mGcLatitudeRad));
+
+ // Compute a table of (EARTH_REFERENCE_RADIUS_KM / radius)^n for i in
+ // 0..MAX_N-2 (this is much faster than calling Math.pow MAX_N+1 times).
+ float[] relativeRadiusPower = new float[MAX_N + 2];
+ relativeRadiusPower[0] = 1.0f;
+ relativeRadiusPower[1] = EARTH_REFERENCE_RADIUS_KM / mGcRadiusKm;
+ for (int i = 2; i < relativeRadiusPower.length; ++i) {
+ relativeRadiusPower[i] = relativeRadiusPower[i - 1] *
+ relativeRadiusPower[1];
+ }
+
+ // Compute tables of sin(lon * m) and cos(lon * m) for m = 0..MAX_N --
+ // this is much faster than calling Math.sin and Math.com MAX_N+1 times.
+ float[] sinMLon = new float[MAX_N];
+ float[] cosMLon = new float[MAX_N];
+ sinMLon[0] = 0.0f;
+ cosMLon[0] = 1.0f;
+ sinMLon[1] = (float) Math.sin(mGcLongitudeRad);
+ cosMLon[1] = (float) Math.cos(mGcLongitudeRad);
+
+ for (int m = 2; m < MAX_N; ++m) {
+ // Standard expansions for sin((m-x)*theta + x*theta) and
+ // cos((m-x)*theta + x*theta).
+ int x = m >> 1;
+ sinMLon[m] = sinMLon[m-x] * cosMLon[x] + cosMLon[m-x] * sinMLon[x];
+ cosMLon[m] = cosMLon[m-x] * cosMLon[x] - sinMLon[m-x] * sinMLon[x];
+ }
+
+ float inverseCosLatitude = 1.0f / (float) Math.cos(mGcLatitudeRad);
+ float yearsSinceBase =
+ (timeMillis - BASE_TIME) / (365f * 24f * 60f * 60f * 1000f);
+
+ // We now compute the magnetic field strength given the geocentric
+ // location. The magnetic field is the derivative of the potential
+ // function defined by the model. See NOAA Technical Report: The US/UK
+ // World Magnetic Model for 2005-2010 for the derivation.
+ float gcX = 0.0f; // Geocentric northwards component.
+ float gcY = 0.0f; // Geocentric eastwards component.
+ float gcZ = 0.0f; // Geocentric downwards component.
+
+ for (int n = 1; n < MAX_N; n++) {
+ for (int m = 0; m <= n; m++) {
+ // Adjust the coefficients for the current date.
+ float g = G_COEFF[n][m] + yearsSinceBase * DELTA_G[n][m];
+ float h = H_COEFF[n][m] + yearsSinceBase * DELTA_H[n][m];
+
+ // Negative derivative with respect to latitude, divided by
+ // radius. This looks like the negation of the version in the
+ // NOAA Techincal report because that report used
+ // P_n^m(sin(theta)) and we use P_n^m(cos(90 - theta)), so the
+ // derivative with respect to theta is negated.
+ gcX += relativeRadiusPower[n+2]
+ * (g * cosMLon[m] + h * sinMLon[m])
+ * legendre.mPDeriv[n][m]
+ * SCHMIDT_QUASI_NORM_FACTORS[n][m];
+
+ // Negative derivative with respect to longitude, divided by
+ // radius.
+ gcY += relativeRadiusPower[n+2] * m
+ * (g * sinMLon[m] - h * cosMLon[m])
+ * legendre.mP[n][m]
+ * SCHMIDT_QUASI_NORM_FACTORS[n][m]
+ * inverseCosLatitude;
+
+ // Negative derivative with respect to radius.
+ gcZ -= (n + 1) * relativeRadiusPower[n+2]
+ * (g * cosMLon[m] + h * sinMLon[m])
+ * legendre.mP[n][m]
+ * SCHMIDT_QUASI_NORM_FACTORS[n][m];
+ }
+ }
+
+ // Convert back to geodetic coordinates. This is basically just a
+ // rotation around the Y-axis by the difference in latitudes between the
+ // geocentric frame and the geodetic frame.
+ double latDiffRad = Math.toRadians(gdLatitudeDeg) - mGcLatitudeRad;
+ mX = (float) (gcX * Math.cos(latDiffRad)
+ + gcZ * Math.sin(latDiffRad));
+ mY = gcY;
+ mZ = (float) (- gcX * Math.sin(latDiffRad)
+ + gcZ * Math.cos(latDiffRad));
+ }
+
+ /**
+ * @return The X (northward) component of the magnetic field in nanoteslas.
+ */
+ public float getX() {
+ return mX;
+ }
+
+ /**
+ * @return The Y (eastward) component of the magnetic field in nanoteslas.
+ */
+ public float getY() {
+ return mY;
+ }
+
+ /**
+ * @return The Z (downward) component of the magnetic field in nanoteslas.
+ */
+ public float getZ() {
+ return mZ;
+ }
+
+ /**
+ * @return The declination of the horizontal component of the magnetic
+ * field from true north, in degrees (i.e. positive means the
+ * magnetic field is rotated east that much from true north).
+ */
+ public float getDeclination() {
+ return (float) Math.toDegrees(Math.atan2(mY, mX));
+ }
+
+ /**
+ * @return The inclination of the magnetic field in degrees -- positive
+ * means the magnetic field is rotated downwards.
+ */
+ public float getInclination() {
+ return (float) Math.toDegrees(Math.atan2(mZ,
+ getHorizontalStrength()));
+ }
+
+ /**
+ * @return Horizontal component of the field strength in nonoteslas.
+ */
+ public float getHorizontalStrength() {
+ return (float) Math.sqrt(mX * mX + mY * mY);
+ }
+
+ /**
+ * @return Total field strength in nanoteslas.
+ */
+ public float getFieldStrength() {
+ return (float) Math.sqrt(mX * mX + mY * mY + mZ * mZ);
+ }
+
+ /**
+ * @param gdLatitudeDeg
+ * Latitude in WGS84 geodetic coordinates.
+ * @param gdLongitudeDeg
+ * Longitude in WGS84 geodetic coordinates.
+ * @param altitudeMeters
+ * Altitude above sea level in WGS84 geodetic coordinates.
+ * @return Geocentric latitude (i.e. angle between closest point on the
+ * equator and this point, at the center of the earth.
+ */
+ private void computeGeocentricCoordinates(float gdLatitudeDeg,
+ float gdLongitudeDeg,
+ float altitudeMeters) {
+ float altitudeKm = altitudeMeters / 1000.0f;
+ float a2 = EARTH_SEMI_MAJOR_AXIS_KM * EARTH_SEMI_MAJOR_AXIS_KM;
+ float b2 = EARTH_SEMI_MINOR_AXIS_KM * EARTH_SEMI_MINOR_AXIS_KM;
+ double gdLatRad = Math.toRadians(gdLatitudeDeg);
+ float clat = (float) Math.cos(gdLatRad);
+ float slat = (float) Math.sin(gdLatRad);
+ float tlat = slat / clat;
+ float latRad =
+ (float) Math.sqrt(a2 * clat * clat + b2 * slat * slat);
+
+ mGcLatitudeRad = (float) Math.atan(tlat * (latRad * altitudeKm + b2)
+ / (latRad * altitudeKm + a2));
+
+ mGcLongitudeRad = (float) Math.toRadians(gdLongitudeDeg);
+
+ float radSq = altitudeKm * altitudeKm
+ + 2 * altitudeKm * (float) Math.sqrt(a2 * clat * clat +
+ b2 * slat * slat)
+ + (a2 * a2 * clat * clat + b2 * b2 * slat * slat)
+ / (a2 * clat * clat + b2 * slat * slat);
+ mGcRadiusKm = (float) Math.sqrt(radSq);
+ }
+
+
+ /**
+ * Utility class to compute a table of Gauss-normalized associated Legendre
+ * functions P_n^m(cos(theta))
+ */
+ static private class LegendreTable {
+ // These are the Gauss-normalized associated Legendre functions -- that
+ // is, they are normal Legendre functions multiplied by
+ // (n-m)!/(2n-1)!! (where (2n-1)!! = 1*3*5*...*2n-1)
+ public final float[][] mP;
+
+ // Derivative of mP, with respect to theta.
+ public final float[][] mPDeriv;
+
+ /**
+ * @param maxN
+ * The maximum n- and m-values to support
+ * @param thetaRad
+ * Returned functions will be Gauss-normalized
+ * P_n^m(cos(thetaRad)), with thetaRad in radians.
+ */
+ public LegendreTable(int maxN, float thetaRad) {
+ // Compute the table of Gauss-normalized associated Legendre
+ // functions using standard recursion relations. Also compute the
+ // table of derivatives using the derivative of the recursion
+ // relations.
+ float cos = (float) Math.cos(thetaRad);
+ float sin = (float) Math.sin(thetaRad);
+
+ mP = new float[maxN + 1][];
+ mPDeriv = new float[maxN + 1][];
+ mP[0] = new float[] { 1.0f };
+ mPDeriv[0] = new float[] { 0.0f };
+ for (int n = 1; n <= maxN; n++) {
+ mP[n] = new float[n + 1];
+ mPDeriv[n] = new float[n + 1];
+ for (int m = 0; m <= n; m++) {
+ if (n == m) {
+ mP[n][m] = sin * mP[n - 1][m - 1];
+ mPDeriv[n][m] = cos * mP[n - 1][m - 1]
+ + sin * mPDeriv[n - 1][m - 1];
+ } else if (n == 1 || m == n - 1) {
+ mP[n][m] = cos * mP[n - 1][m];
+ mPDeriv[n][m] = -sin * mP[n - 1][m]
+ + cos * mPDeriv[n - 1][m];
+ } else {
+ assert n > 1 && m < n - 1;
+ float k = ((n - 1) * (n - 1) - m * m)
+ / (float) ((2 * n - 1) * (2 * n - 3));
+ mP[n][m] = cos * mP[n - 1][m] - k * mP[n - 2][m];
+ mPDeriv[n][m] = -sin * mP[n - 1][m]
+ + cos * mPDeriv[n - 1][m] - k * mPDeriv[n - 2][m];
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute the ration between the Gauss-normalized associated Legendre
+ * functions and the Schmidt quasi-normalized version. This is equivalent to
+ * sqrt((m==0?1:2)*(n-m)!/(n+m!))*(2n-1)!!/(n-m)!
+ */
+ private static float[][] computeSchmidtQuasiNormFactors(int maxN) {
+ float[][] schmidtQuasiNorm = new float[maxN + 1][];
+ schmidtQuasiNorm[0] = new float[] { 1.0f };
+ for (int n = 1; n <= maxN; n++) {
+ schmidtQuasiNorm[n] = new float[n + 1];
+ schmidtQuasiNorm[n][0] =
+ schmidtQuasiNorm[n - 1][0] * (2 * n - 1) / (float) n;
+ for (int m = 1; m <= n; m++) {
+ schmidtQuasiNorm[n][m] = schmidtQuasiNorm[n][m - 1]
+ * (float) Math.sqrt((n - m + 1) * (m == 1 ? 2 : 1)
+ / (float) (n + m));
+ }
+ }
+ return schmidtQuasiNorm;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/ISensorService.aidl b/core/java/android/hardware/ISensorService.aidl
index 8aad9b465bda06c5c0278d02d5b72aa3995f4de8..04af2aec45239f6d160eab888b0ce152de6d7f11 100644
--- a/core/java/android/hardware/ISensorService.aidl
+++ b/core/java/android/hardware/ISensorService.aidl
@@ -25,5 +25,5 @@ import android.os.ParcelFileDescriptor;
interface ISensorService
{
ParcelFileDescriptor getDataChanel();
- boolean enableSensor(IBinder listener, int sensor, int enable);
+ boolean enableSensor(IBinder listener, String name, int sensor, int enable);
}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f02094eed63d11f8b98b5c4f477ac464fee952e0..e232c2cee04355f8b8ccbd237cbb81bf26db4a69 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -620,6 +620,9 @@ public class SensorManager extends IRotationWatcher.Stub
*/
@Deprecated
public boolean registerListener(SensorListener listener, int sensors, int rate) {
+ if (listener == null) {
+ return false;
+ }
boolean result = false;
result = registerLegacyListener(SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER,
listener, sensors, rate) || result;
@@ -638,6 +641,9 @@ public class SensorManager extends IRotationWatcher.Stub
private boolean registerLegacyListener(int legacyType, int type,
SensorListener listener, int sensors, int rate)
{
+ if (listener == null) {
+ return false;
+ }
boolean result = false;
// Are we activating this legacy sensor?
if ((sensors & legacyType) != 0) {
@@ -693,6 +699,9 @@ public class SensorManager extends IRotationWatcher.Stub
private void unregisterLegacyListener(int legacyType, int type,
SensorListener listener, int sensors)
{
+ if (listener == null) {
+ return;
+ }
// do we know about this listener?
LegacyListener legacyListener = null;
synchronized (mLegacyListenersMap) {
@@ -741,7 +750,7 @@ public class SensorManager extends IRotationWatcher.Stub
*/
@Deprecated
public void unregisterListener(SensorListener listener) {
- unregisterListener(listener, SENSOR_ALL);
+ unregisterListener(listener, SENSOR_ALL | SENSOR_ORIENTATION_RAW);
}
/**
@@ -800,6 +809,9 @@ public class SensorManager extends IRotationWatcher.Stub
*/
public boolean registerListener(SensorEventListener listener, Sensor sensor, int rate,
Handler handler) {
+ if (listener == null || sensor == null) {
+ return false;
+ }
boolean result;
int delay = -1;
switch (rate) {
@@ -829,9 +841,11 @@ public class SensorManager extends IRotationWatcher.Stub
}
}
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
if (l == null) {
l = new ListenerDelegate(listener, sensor, handler);
- result = mSensorService.enableSensor(l, sensor.getHandle(), delay);
+ result = mSensorService.enableSensor(l, name, handle, delay);
if (result) {
sListeners.add(l);
sListeners.notify();
@@ -840,7 +854,7 @@ public class SensorManager extends IRotationWatcher.Stub
sSensorThread.startLocked(mSensorService);
}
} else {
- result = mSensorService.enableSensor(l, sensor.getHandle(), delay);
+ result = mSensorService.enableSensor(l, name, handle, delay);
if (result) {
l.addSensor(sensor);
}
@@ -854,6 +868,9 @@ public class SensorManager extends IRotationWatcher.Stub
}
private void unregisterListener(Object listener, Sensor sensor) {
+ if (listener == null || sensor == null) {
+ return;
+ }
try {
synchronized (sListeners) {
final int size = sListeners.size();
@@ -861,8 +878,9 @@ public class SensorManager extends IRotationWatcher.Stub
ListenerDelegate l = sListeners.get(i);
if (l.getListener() == listener) {
// disable these sensors
+ String name = sensor.getName();
int handle = sensor.getHandle();
- mSensorService.enableSensor(l, handle, SENSOR_DISABLE);
+ mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE);
// if we have no more sensors enabled on this listener,
// take it off the list.
if (l.removeSensor(sensor) == 0) {
@@ -878,6 +896,9 @@ public class SensorManager extends IRotationWatcher.Stub
}
private void unregisterListener(Object listener) {
+ if (listener == null) {
+ return;
+ }
try {
synchronized (sListeners) {
final int size = sListeners.size();
@@ -886,7 +907,9 @@ public class SensorManager extends IRotationWatcher.Stub
if (l.getListener() == listener) {
// disable all sensors for this listener
for (Sensor sensor : l.getSensors()) {
- mSensorService.enableSensor(l, sensor.getHandle(), SENSOR_DISABLE);
+ String name = sensor.getName();
+ int handle = sensor.getHandle();
+ mSensorService.enableSensor(l, name, handle, SENSOR_DISABLE);
}
sListeners.remove(i);
break;
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 7d02f6588db4cdf9e0351315bc4e3edea0eaefc7..eedcc354e61337a931919511095620f58dbbd4f6 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -24,6 +24,9 @@ import android.view.MotionEvent;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* AbstractInputMethodService provides a abstract base class for input methods.
* Normal input method implementations will not derive from this directly,
@@ -156,6 +159,13 @@ public abstract class AbstractInputMethodService extends Service
*/
public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
+ /**
+ * Implement this to handle {@link android.os.Binder#dump Binder.dump()}
+ * calls on your input method.
+ */
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ }
+
@Override
final public IBinder onBind(Intent intent) {
if (mInputMethod == null) {
diff --git a/core/java/android/inputmethodservice/ExtractButton.java b/core/java/android/inputmethodservice/ExtractButton.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6fe38d3ea4a4b2502df2acc7ade068e7e69b057
--- /dev/null
+++ b/core/java/android/inputmethodservice/ExtractButton.java
@@ -0,0 +1,30 @@
+package android.inputmethodservice;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Button;
+
+/***
+ * Specialization of {@link Button} that ignores the window not being focused.
+ */
+class ExtractButton extends Button {
+ public ExtractButton(Context context) {
+ super(context, null);
+ }
+
+ public ExtractButton(Context context, AttributeSet attrs) {
+ super(context, attrs, com.android.internal.R.attr.buttonStyle);
+ }
+
+ public ExtractButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Pretend like the window this view is in always has focus, so it will
+ * highlight when selected.
+ */
+ @Override public boolean hasWindowFocus() {
+ return this.isEnabled() ? true : false;
+ }
+}
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index e59f38b368db04843ea52868959886b6fb206fa3..0295f695784f29258374accc31254f4926fae66e 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -2,6 +2,7 @@ package android.inputmethodservice;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.inputmethod.ExtractedText;
import android.widget.EditText;
/***
@@ -9,6 +10,9 @@ import android.widget.EditText;
* extracted text in a full-screen input method.
*/
public class ExtractEditText extends EditText {
+ private InputMethodService mIME;
+ private int mSettingExtractedText;
+
public ExtractEditText(Context context) {
super(context, null);
}
@@ -20,4 +24,107 @@ public class ExtractEditText extends EditText {
public ExtractEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
+
+ void setIME(InputMethodService ime) {
+ mIME = ime;
+ }
+
+ /**
+ * Start making changes that will not be reported to the client. That
+ * is, {@link #onSelectionChanged(int, int)} will not result in sending
+ * the new selection to the client
+ */
+ public void startInternalChanges() {
+ mSettingExtractedText += 1;
+ }
+
+ /**
+ * Finish making changes that will not be reported to the client. That
+ * is, {@link #onSelectionChanged(int, int)} will not result in sending
+ * the new selection to the client
+ */
+ public void finishInternalChanges() {
+ mSettingExtractedText -= 1;
+ }
+
+ /**
+ * Implement just to keep track of when we are setting text from the
+ * client (vs. seeing changes in ourself from the user).
+ */
+ @Override public void setExtractedText(ExtractedText text) {
+ try {
+ mSettingExtractedText++;
+ super.setExtractedText(text);
+ } finally {
+ mSettingExtractedText--;
+ }
+ }
+
+ /**
+ * Report to the underlying text editor about selection changes.
+ */
+ @Override protected void onSelectionChanged(int selStart, int selEnd) {
+ if (mSettingExtractedText == 0 && mIME != null && selStart >= 0 && selEnd >= 0) {
+ mIME.onExtractedSelectionChanged(selStart, selEnd);
+ }
+ }
+
+ /**
+ * Redirect clicks to the IME for handling there. First allows any
+ * on click handler to run, though.
+ */
+ @Override public boolean performClick() {
+ if (!super.performClick() && mIME != null) {
+ mIME.onExtractedTextClicked();
+ return true;
+ }
+ return false;
+ }
+
+ @Override public boolean onTextContextMenuItem(int id) {
+ if (mIME != null) {
+ if (mIME.onExtractTextContextMenuItem(id)) {
+ return true;
+ }
+ }
+ return super.onTextContextMenuItem(id);
+ }
+
+ /**
+ * We are always considered to be an input method target.
+ */
+ public boolean isInputMethodTarget() {
+ return true;
+ }
+
+ /**
+ * Return true if the edit text is currently showing a scroll bar.
+ */
+ public boolean hasVerticalScrollBar() {
+ return computeVerticalScrollRange() > computeVerticalScrollExtent();
+ }
+
+ /**
+ * Pretend like the window this view is in always has focus, so its
+ * highlight and cursor will be displayed.
+ */
+ @Override public boolean hasWindowFocus() {
+ return this.isEnabled() ? true : false;
+ }
+
+ /**
+ * Pretend like this view always has focus, so its
+ * highlight and cursor will be displayed.
+ */
+ @Override public boolean isFocused() {
+ return this.isEnabled() ? true : false;
+ }
+
+ /**
+ * Pretend like this view always has focus, so its
+ * highlight and cursor will be displayed.
+ */
+ @Override public boolean hasFocus() {
+ return this.isEnabled() ? true : false;
+ }
}
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 5a85c66f42904fb47679f5a11e9a15b7c1133f05..6cf90d673579717a9571f29d785d0e04bc9ddbf4 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -30,6 +30,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
private static final int DO_UPDATE_SELECTION = 90;
private static final int DO_UPDATE_CURSOR = 95;
private static final int DO_APP_PRIVATE_COMMAND = 100;
+ private static final int DO_TOGGLE_SOFT_INPUT = 105;
final HandlerCaller mCaller;
final InputMethodSession mInputMethodSession;
@@ -106,6 +107,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
mCaller.recycleArgs(args);
return;
}
+ case DO_TOGGLE_SOFT_INPUT: {
+ mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2);
+ return;
+ }
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -149,4 +154,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
public void appPrivateCommand(String action, Bundle data) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
}
+
+ public void toggleSoftInput(int showFlags, int hideFlags) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
+ }
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 9abc23ba670327f09c0b3cadac2612f5cfc49c0a..20a05a5aea426da303be6d108642fec4bd45612a 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -8,9 +8,12 @@ import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.InputConnectionWrapper;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
@@ -18,6 +21,11 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Implements the internal IInputMethod interface to convert incoming calls
* on to it back to calls on the public InputMethod interface, scheduling
@@ -28,6 +36,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final String TAG = "InputMethodWrapper";
private static final boolean DEBUG = false;
+ private static final int DO_DUMP = 1;
private static final int DO_ATTACH_TOKEN = 10;
private static final int DO_SET_INPUT_CONTEXT = 20;
private static final int DO_UNSET_INPUT_CONTEXT = 30;
@@ -39,9 +48,14 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_SHOW_SOFT_INPUT = 60;
private static final int DO_HIDE_SOFT_INPUT = 70;
+ final AbstractInputMethodService mTarget;
final HandlerCaller mCaller;
final InputMethod mInputMethod;
+ static class Notifier {
+ boolean notified;
+ }
+
// NOTE: we should have a cache of these.
static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
final Context mContext;
@@ -64,7 +78,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
}
- public IInputMethodWrapper(Context context, InputMethod inputMethod) {
+ public IInputMethodWrapper(AbstractInputMethodService context,
+ InputMethod inputMethod) {
+ mTarget = context;
mCaller = new HandlerCaller(context, this);
mInputMethod = inputMethod;
}
@@ -75,6 +91,20 @@ class IInputMethodWrapper extends IInputMethod.Stub
public void executeMessage(Message msg) {
switch (msg.what) {
+ case DO_DUMP: {
+ HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+ try {
+ mTarget.dump((FileDescriptor)args.arg1,
+ (PrintWriter)args.arg2, (String[])args.arg3);
+ } catch (RuntimeException e) {
+ ((PrintWriter)args.arg2).println("Exception: " + e);
+ }
+ synchronized (args.arg4) {
+ ((CountDownLatch)args.arg4).countDown();
+ }
+ return;
+ }
+
case DO_ATTACH_TOKEN: {
mInputMethod.attachToken((IBinder)msg.obj);
return;
@@ -86,12 +116,22 @@ class IInputMethodWrapper extends IInputMethod.Stub
case DO_UNSET_INPUT_CONTEXT:
mInputMethod.unbindInput();
return;
- case DO_START_INPUT:
- mInputMethod.startInput((EditorInfo)msg.obj);
+ case DO_START_INPUT: {
+ HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+ IInputContext inputContext = (IInputContext)args.arg1;
+ InputConnection ic = inputContext != null
+ ? new InputConnectionWrapper(inputContext) : null;
+ mInputMethod.startInput(ic, (EditorInfo)args.arg2);
return;
- case DO_RESTART_INPUT:
- mInputMethod.restartInput((EditorInfo)msg.obj);
+ }
+ case DO_RESTART_INPUT: {
+ HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+ IInputContext inputContext = (IInputContext)args.arg1;
+ InputConnection ic = inputContext != null
+ ? new InputConnectionWrapper(inputContext) : null;
+ mInputMethod.restartInput(ic, (EditorInfo)args.arg2);
return;
+ }
case DO_CREATE_SESSION: {
mInputMethod.createSession(new InputMethodSessionCallbackWrapper(
mCaller.mContext, (IInputMethodCallback)msg.obj));
@@ -105,16 +145,37 @@ class IInputMethodWrapper extends IInputMethod.Stub
mInputMethod.revokeSession((InputMethodSession)msg.obj);
return;
case DO_SHOW_SOFT_INPUT:
- mInputMethod.showSoftInput(
- msg.arg1 != 0 ? InputMethod.SHOW_EXPLICIT : 0);
+ mInputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return;
case DO_HIDE_SOFT_INPUT:
- mInputMethod.hideSoftInput();
+ mInputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return;
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ fout.println("Permission Denial: can't dump InputMethodManager from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ CountDownLatch latch = new CountDownLatch(1);
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOOOO(DO_DUMP,
+ fd, fout, args, latch));
+ try {
+ if (!latch.await(5, TimeUnit.SECONDS)) {
+ fout.println("Timeout waiting for dump");
+ }
+ } catch (InterruptedException e) {
+ fout.println("Interrupted waiting for dump");
+ }
+ }
+
public void attachToken(IBinder token) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
}
@@ -130,12 +191,14 @@ class IInputMethodWrapper extends IInputMethod.Stub
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
}
- public void startInput(EditorInfo attribute) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_START_INPUT, attribute));
+ public void startInput(IInputContext inputContext, EditorInfo attribute) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
+ inputContext, attribute));
}
- public void restartInput(EditorInfo attribute) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RESTART_INPUT, attribute));
+ public void restartInput(IInputContext inputContext, EditorInfo attribute) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT,
+ inputContext, attribute));
}
public void createSession(IInputMethodCallback callback) {
@@ -163,12 +226,13 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
}
- public void showSoftInput(boolean explicit) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_SHOW_SOFT_INPUT,
- explicit ? 1 : 0));
+ public void showSoftInput(int flags, ResultReceiver resultReceiver) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
+ flags, resultReceiver));
}
- public void hideSoftInput() {
- mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_HIDE_SOFT_INPUT));
+ public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
+ flags, resultReceiver));
}
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 3a9b26a9c6f5496fc22c9890136cf161cbfa17b4..32270c46d039a8cf8b7300428449a6ce1aa4a1e7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -26,7 +26,16 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.text.InputType;
+import android.text.Layout;
+import android.text.Spannable;
+import android.text.method.MovementMethod;
import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -43,7 +52,10 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
import android.widget.FrameLayout;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
* InputMethodService provides a standard implementation of an InputMethod,
@@ -51,6 +63,22 @@ import android.widget.FrameLayout;
* base class {@link AbstractInputMethodService} and the {@link InputMethod}
* interface for more information on the basics of writing input methods.
*
+ * In addition to the normal Service lifecycle methods, this class
+ * introduces some new specific callbacks that most subclasses will want
+ * to make use of: An input method has significant discretion in how it goes about its
* work: the {@link android.inputmethodservice.InputMethodService} provides
* a basic framework for standard UI elements (input view, candidates view,
@@ -181,9 +209,12 @@ public class InputMethodService extends AbstractInputMethodService {
static final String TAG = "InputMethodService";
static final boolean DEBUG = false;
+ InputMethodManager mImm;
+
LayoutInflater mInflater;
View mRootView;
SoftInputWindow mWindow;
+ boolean mInitialized;
boolean mWindowCreated;
boolean mWindowAdded;
boolean mWindowVisible;
@@ -196,16 +227,25 @@ public class InputMethodService extends AbstractInputMethodService {
InputBinding mInputBinding;
InputConnection mInputConnection;
boolean mInputStarted;
+ boolean mInputViewStarted;
+ boolean mCandidatesViewStarted;
+ InputConnection mStartedInputConnection;
EditorInfo mInputEditorInfo;
+ int mShowInputFlags;
boolean mShowInputRequested;
boolean mLastShowInputRequested;
- boolean mShowCandidatesRequested;
+ int mCandidatesVisibility;
+ CompletionInfo[] mCurCompletions;
+
+ boolean mShowInputForced;
boolean mFullscreenApplied;
boolean mIsFullscreen;
View mExtractView;
ExtractEditText mExtractEditText;
+ ViewGroup mExtractAccessories;
+ Button mExtractAction;
ExtractedText mExtractedText;
int mExtractedToken;
@@ -236,6 +276,21 @@ public class InputMethodService extends AbstractInputMethodService {
}
};
+ final View.OnClickListener mActionClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ final EditorInfo ei = getCurrentInputEditorInfo();
+ final InputConnection ic = getCurrentInputConnection();
+ if (ei != null && ic != null) {
+ if (ei.actionId != 0) {
+ ic.performEditorAction(ei.actionId);
+ } else if ((ei.imeOptions&EditorInfo.IME_MASK_ACTION)
+ != EditorInfo.IME_ACTION_NONE) {
+ ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
+ }
+ }
+ }
+ };
+
/**
* Concrete implementation of
* {@link AbstractInputMethodService.AbstractInputMethodImpl} that provides
@@ -262,6 +317,9 @@ public class InputMethodService extends AbstractInputMethodService {
mInputConnection = binding.getConnection();
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) ic.reportFullscreenMode(mIsFullscreen);
+ initialize();
onBindInput();
}
@@ -277,31 +335,50 @@ public class InputMethodService extends AbstractInputMethodService {
mInputConnection = null;
}
- public void startInput(EditorInfo attribute) {
+ public void startInput(InputConnection ic, EditorInfo attribute) {
if (DEBUG) Log.v(TAG, "startInput(): editor=" + attribute);
- doStartInput(attribute, false);
+ doStartInput(ic, attribute, false);
}
- public void restartInput(EditorInfo attribute) {
+ public void restartInput(InputConnection ic, EditorInfo attribute) {
if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute);
- doStartInput(attribute, true);
+ doStartInput(ic, attribute, true);
}
/**
* Handle a request by the system to hide the soft input area.
*/
- public void hideSoftInput() {
+ public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "hideSoftInput()");
+ boolean wasVis = isInputViewShown();
+ mShowInputFlags = 0;
mShowInputRequested = false;
+ mShowInputForced = false;
hideWindow();
+ if (resultReceiver != null) {
+ resultReceiver.send(wasVis != isInputViewShown()
+ ? InputMethodManager.RESULT_HIDDEN
+ : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
+ }
}
/**
* Handle a request by the system to show the soft input area.
*/
- public void showSoftInput(int flags) {
+ public void showSoftInput(int flags, ResultReceiver resultReceiver) {
if (DEBUG) Log.v(TAG, "showSoftInput()");
- onShowRequested(flags);
+ boolean wasVis = isInputViewShown();
+ mShowInputFlags = 0;
+ if (onShowInputRequested(flags, false)) {
+ showWindow(true);
+ }
+ if (resultReceiver != null) {
+ resultReceiver.send(wasVis != isInputViewShown()
+ ? InputMethodManager.RESULT_SHOWN
+ : (wasVis ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : InputMethodManager.RESULT_UNCHANGED_HIDDEN), null);
+ }
}
}
@@ -316,8 +393,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
if (DEBUG) Log.v(TAG, "finishInput() in " + this);
- onFinishInput();
- mInputStarted = false;
+ doFinishInput();
}
/**
@@ -328,6 +404,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (!isEnabled()) {
return;
}
+ mCurCompletions = completions;
onDisplayCompletions(completions);
}
@@ -377,6 +454,13 @@ public class InputMethodService extends AbstractInputMethodService {
}
InputMethodService.this.onAppPrivateCommand(action, data);
}
+
+ /**
+ *
+ */
+ public void toggleSoftInput(int showFlags, int hideFlags) {
+ InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
+ }
}
/**
@@ -391,7 +475,7 @@ public class InputMethodService extends AbstractInputMethodService {
* of the application behind. This value is relative to the top edge
* of the input method window.
*/
- int contentTopInsets;
+ public int contentTopInsets;
/**
* This is the top part of the UI that is visibly covering the
@@ -404,7 +488,7 @@ public class InputMethodService extends AbstractInputMethodService {
* needed to make the focus visible. This value is relative to the top edge
* of the input method window.
*/
- int visibleTopInsets;
+ public int visibleTopInsets;
/**
* Option for {@link #touchableInsets}: the entire window frame
@@ -437,6 +521,7 @@ public class InputMethodService extends AbstractInputMethodService {
@Override public void onCreate() {
super.onCreate();
+ mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
mInflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this);
@@ -444,20 +529,42 @@ public class InputMethodService extends AbstractInputMethodService {
mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT);
}
+ /**
+ * This is a hook that subclasses can use to perform initialization of
+ * their interface. It is called for you prior to any of your UI objects
+ * being created, both after the service is first created and after a
+ * configuration change happens.
+ */
+ public void onInitializeInterface() {
+ }
+
+ void initialize() {
+ if (!mInitialized) {
+ mInitialized = true;
+ onInitializeInterface();
+ }
+ }
+
void initViews() {
- mWindowVisible = false;
+ mInitialized = false;
mWindowCreated = false;
mShowInputRequested = false;
- mShowCandidatesRequested = false;
+ mShowInputForced = false;
mRootView = mInflater.inflate(
com.android.internal.R.layout.input_method, null);
mWindow.setContentView(mRootView);
mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
-
+ if (Settings.System.getInt(getContentResolver(),
+ Settings.System.FANCY_IME_ANIMATIONS, 0) != 0) {
+ mWindow.getWindow().setWindowAnimations(
+ com.android.internal.R.style.Animation_InputMethodFancy);
+ }
mExtractFrame = (FrameLayout)mRootView.findViewById(android.R.id.extractArea);
mExtractView = null;
mExtractEditText = null;
+ mExtractAccessories = null;
+ mExtractAction = null;
mFullscreenApplied = false;
mCandidatesFrame = (FrameLayout)mRootView.findViewById(android.R.id.candidatesArea);
@@ -466,7 +573,8 @@ public class InputMethodService extends AbstractInputMethodService {
mIsInputViewShown = false;
mExtractFrame.setVisibility(View.GONE);
- mCandidatesFrame.setVisibility(View.INVISIBLE);
+ mCandidatesVisibility = getCandidatesHiddenVisibility();
+ mCandidatesFrame.setVisibility(mCandidatesVisibility);
mInputFrame.setVisibility(View.GONE);
}
@@ -486,26 +594,48 @@ public class InputMethodService extends AbstractInputMethodService {
* regenerating the input method UI as a result of the configuration
* change, so you can rely on your {@link #onCreateInputView} and
* other methods being called as appropriate due to a configuration change.
+ *
+ * When a configuration change does happen,
+ * {@link #onInitializeInterface()} is guaranteed to be called the next
+ * time prior to any of the other input or UI creation callbacks. The
+ * following will be called immediately depending if appropriate for current
+ * state: {@link #onStartInput} if input is active, and
+ * {@link #onCreateInputView} and {@link #onStartInputView} and related
+ * appropriate functions if the UI is displayed.
*/
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
boolean visible = mWindowVisible;
+ int showFlags = mShowInputFlags;
boolean showingInput = mShowInputRequested;
- boolean showingCandidates = mShowCandidatesRequested;
+ CompletionInfo[] completions = mCurCompletions;
initViews();
+ mInputViewStarted = false;
+ mCandidatesViewStarted = false;
+ if (mInputStarted) {
+ doStartInput(getCurrentInputConnection(),
+ getCurrentInputEditorInfo(), true);
+ }
if (visible) {
- if (showingCandidates) {
- setCandidatesViewShown(true);
- }
if (showingInput) {
- // If we are showing the full soft keyboard, then go through
- // this path to take care of current decisions about fullscreen
- // etc.
- onShowRequested(InputMethod.SHOW_EXPLICIT);
- } else {
- // Otherwise just put it back for its candidates.
+ // If we were last showing the soft keyboard, try to do so again.
+ if (onShowInputRequested(showFlags, true)) {
+ showWindow(true);
+ if (completions != null) {
+ mCurCompletions = completions;
+ onDisplayCompletions(completions);
+ }
+ } else {
+ hideWindow();
+ }
+ } else if (mCandidatesVisibility == View.VISIBLE) {
+ // If the candidates are currently visible, make sure the
+ // window is shown for them.
showWindow(false);
+ } else {
+ // Otherwise hide the window.
+ hideWindow();
}
}
}
@@ -568,6 +698,10 @@ public class InputMethodService extends AbstractInputMethodService {
* the input method, or null if there is none.
*/
public InputConnection getCurrentInputConnection() {
+ InputConnection ic = mStartedInputConnection;
+ if (ic != null) {
+ return ic;
+ }
return mInputConnection;
}
@@ -593,7 +727,10 @@ public class InputMethodService extends AbstractInputMethodService {
if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
changed = true;
mIsFullscreen = isFullscreen;
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) ic.reportFullscreenMode(isFullscreen);
mFullscreenApplied = true;
+ initialize();
Drawable bg = onCreateBackgroundDrawable();
if (bg == null) {
// We need to give the window a real drawable, so that it
@@ -609,7 +746,7 @@ public class InputMethodService extends AbstractInputMethodService {
setExtractView(v);
}
}
- startExtractingText();
+ startExtractingText(false);
}
}
@@ -708,6 +845,7 @@ public class InputMethodService extends AbstractInputMethodService {
mIsInputViewShown = isShown;
mInputFrame.setVisibility(isShown ? View.VISIBLE : View.GONE);
if (mInputView == null) {
+ initialize();
View v = onCreateInputView();
if (v != null) {
setInputView(v);
@@ -716,13 +854,20 @@ public class InputMethodService extends AbstractInputMethodService {
}
}
+ /**
+ * Returns true if we have been asked to show our input view.
+ */
+ public boolean isShowInputRequested() {
+ return mShowInputRequested;
+ }
+
/**
* Return whether the soft input view is currently shown to the
* user. This is the state that was last determined and
* applied by {@link #updateInputViewShown()}.
*/
public boolean isInputViewShown() {
- return mIsInputViewShown;
+ return mIsInputViewShown && mWindowVisible;
}
/**
@@ -744,9 +889,10 @@ public class InputMethodService extends AbstractInputMethodService {
* it is hidden.
*/
public void setCandidatesViewShown(boolean shown) {
- if (mShowCandidatesRequested != shown) {
- mCandidatesFrame.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
- mShowCandidatesRequested = shown;
+ int vis = shown ? View.VISIBLE : getCandidatesHiddenVisibility();
+ if (mCandidatesVisibility != vis) {
+ mCandidatesFrame.setVisibility(vis);
+ mCandidatesVisibility = vis;
}
if (!mShowInputRequested && mWindowVisible != shown) {
// If we are being asked to show the candidates view while the app
@@ -760,11 +906,27 @@ public class InputMethodService extends AbstractInputMethodService {
}
}
- public void setStatusIcon(int iconResId) {
+ /**
+ * Returns the visibility mode (either {@link View#INVISIBLE View.INVISIBLE}
+ * or {@link View#GONE View.GONE}) of the candidates view when it is not
+ * shown. The default implementation returns GONE when in fullscreen mode,
+ * otherwise VISIBLE. Be careful if you change this to return GONE in
+ * other situations -- if showing or hiding the candidates view causes
+ * your window to resize, this can cause temporary drawing artifacts as
+ * the resize takes place.
+ */
+ public int getCandidatesHiddenVisibility() {
+ return isFullscreenMode() ? View.GONE : View.INVISIBLE;
+ }
+
+ public void showStatusIcon(int iconResId) {
mStatusIcon = iconResId;
- if (mInputConnection != null && mWindowVisible) {
- mInputConnection.showStatusIcon(getPackageName(), iconResId);
- }
+ mImm.showStatusIcon(mToken, getPackageName(), iconResId);
+ }
+
+ public void hideStatusIcon() {
+ mStatusIcon = 0;
+ mImm.hideStatusIcon(mToken);
}
/**
@@ -775,22 +937,30 @@ public class InputMethodService extends AbstractInputMethodService {
* @param id Unique identifier of the new input method ot start.
*/
public void switchInputMethod(String id) {
- ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE))
- .setInputMethod(mToken, id);
+ mImm.setInputMethod(mToken, id);
}
public void setExtractView(View view) {
mExtractFrame.removeAllViews();
mExtractFrame.addView(view, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ ViewGroup.LayoutParams.FILL_PARENT));
mExtractView = view;
if (view != null) {
mExtractEditText = (ExtractEditText)view.findViewById(
com.android.internal.R.id.inputExtractEditText);
- startExtractingText();
+ mExtractEditText.setIME(this);
+ mExtractAction = (Button)view.findViewById(
+ com.android.internal.R.id.inputExtractAction);
+ if (mExtractAction != null) {
+ mExtractAccessories = (ViewGroup)view.findViewById(
+ com.android.internal.R.id.inputExtractAccessories);
+ }
+ startExtractingText(false);
} else {
mExtractEditText = null;
+ mExtractAccessories = null;
+ mExtractAction = null;
}
}
@@ -889,28 +1059,112 @@ public class InputMethodService extends AbstractInputMethodService {
public void onStartInputView(EditorInfo info, boolean restarting) {
}
+ /**
+ * Called when the input view is being hidden from the user. This will
+ * be called either prior to hiding the window, or prior to switching to
+ * another target for editing.
+ *
+ * The default
+ * implementation uses the InputConnection to clear any active composing
+ * text; you can override this (not calling the base class implementation)
+ * to perform whatever behavior you would like.
+ *
+ * @param finishingInput If true, {@link #onFinishInput} will be
+ * called immediately after.
+ */
+ public void onFinishInputView(boolean finishingInput) {
+ if (!finishingInput) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
+ }
+ }
+
+ /**
+ * Called when only the candidates view has been shown for showing
+ * processing as the user enters text through a hard keyboard.
+ * This will always be called after {@link #onStartInput},
+ * allowing you to do your general setup there and just view-specific
+ * setup here. You are guaranteed that {@link #onCreateCandidatesView()}
+ * will have been called some time before this function is called.
+ *
+ * Note that this will not be called when the input method
+ * is running in full editing mode, and thus receiving
+ * {@link #onStartInputView} to initiate that operation. This is only
+ * for the case when candidates are being shown while the input method
+ * editor is hidden but wants to show its candidates UI as text is
+ * entered through some other mechanism.
+ *
+ * @param info Description of the type of text being edited.
+ * @param restarting Set to true if we are restarting input on the
+ * same text field as before.
+ */
+ public void onStartCandidatesView(EditorInfo info, boolean restarting) {
+ }
+
+ /**
+ * Called when the candidates view is being hidden from the user. This will
+ * be called either prior to hiding the window, or prior to switching to
+ * another target for editing.
+ *
+ * The default
+ * implementation uses the InputConnection to clear any active composing
+ * text; you can override this (not calling the base class implementation)
+ * to perform whatever behavior you would like.
+ *
+ * @param finishingInput If true, {@link #onFinishInput} will be
+ * called immediately after.
+ */
+ public void onFinishCandidatesView(boolean finishingInput) {
+ if (!finishingInput) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
+ }
+ }
+
/**
* The system has decided that it may be time to show your input method.
* This is called due to a corresponding call to your
- * {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)}
- * method. The default implementation simply calls
- * {@link #showWindow(boolean)}, except if the
- * {@link InputMethod#SHOW_EXPLICIT InputMethod.SHOW_EXPLICIT} flag is
- * not set and the input method is running in fullscreen mode.
+ * {@link InputMethod#showSoftInput InputMethod.showSoftInput()}
+ * method. The default implementation uses
+ * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
+ * and the current configuration to decide whether the input view should
+ * be shown at this point.
*
* @param flags Provides additional information about the show request,
- * as per {@link InputMethod#showSoftInput(int) InputMethod.showSoftInput(int)}.
+ * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
+ * @param configChange This is true if we are re-showing due to a
+ * configuration change.
+ * @return Returns true to indicate that the window should be shown.
*/
- public void onShowRequested(int flags) {
+ public boolean onShowInputRequested(int flags, boolean configChange) {
if (!onEvaluateInputViewShown()) {
- return;
+ return false;
}
- if ((flags&InputMethod.SHOW_EXPLICIT) == 0 && onEvaluateFullscreenMode()) {
- // Don't show if this is not explicit requested by the user and
- // the input method is fullscreen. That would be too disruptive.
- return;
+ if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
+ if (!configChange && onEvaluateFullscreenMode()) {
+ // Don't show if this is not explicitly requested by the user and
+ // the input method is fullscreen. That would be too disruptive.
+ // However, we skip this change for a config change, since if
+ // the IME is already shown we do want to go into fullscreen
+ // mode at this point.
+ return false;
+ }
+ Configuration config = getResources().getConfiguration();
+ if (config.keyboard != Configuration.KEYBOARD_NOKEYS) {
+ // And if the device has a hard keyboard, even if it is
+ // currently hidden, don't show the input method implicitly.
+ // These kinds of devices don't need it that much.
+ return false;
+ }
}
- showWindow(true);
+ if ((flags&InputMethod.SHOW_FORCED) != 0) {
+ mShowInputForced = true;
+ }
+ return true;
}
public void showWindow(boolean showInput) {
@@ -935,45 +1189,76 @@ public class InputMethodService extends AbstractInputMethodService {
}
if (DEBUG) Log.v(TAG, "showWindow: updating UI");
+ initialize();
updateFullscreenMode();
updateInputViewShown();
if (!mWindowAdded || !mWindowCreated) {
mWindowAdded = true;
mWindowCreated = true;
+ initialize();
+ if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
View v = onCreateCandidatesView();
if (DEBUG) Log.v(TAG, "showWindow: candidates=" + v);
if (v != null) {
setCandidatesView(v);
}
}
- if (doShowInput) {
- if (mInputStarted) {
- if (DEBUG) Log.v(TAG, "showWindow: starting input view");
+ if (mShowInputRequested) {
+ if (!mInputViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
+ mInputViewStarted = true;
onStartInputView(mInputEditorInfo, false);
}
- startExtractingText();
+ } else if (!mCandidatesViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
+ mCandidatesViewStarted = true;
+ onStartCandidatesView(mInputEditorInfo, false);
+ }
+
+ if (doShowInput) {
+ startExtractingText(false);
}
if (!wasVisible) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
+ onWindowShown();
mWindow.show();
- if (mInputConnection != null) {
- mInputConnection.showStatusIcon(getPackageName(), mStatusIcon);
- }
}
}
public void hideWindow() {
+ if (mInputViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
+ onFinishInputView(false);
+ } else if (mCandidatesViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
+ onFinishCandidatesView(false);
+ }
+ mInputViewStarted = false;
+ mCandidatesViewStarted = false;
if (mWindowVisible) {
mWindow.hide();
mWindowVisible = false;
- if (mInputConnection != null) {
- mInputConnection.hideStatusIcon();
- }
+ onWindowHidden();
}
}
+ /**
+ * Called when the input method window has been shown to the user, after
+ * previously not being visible. This is done after all of the UI setup
+ * for the window has occurred (creating its views etc).
+ */
+ public void onWindowShown() {
+ }
+
+ /**
+ * Called when the input method window has been hidden from the user,
+ * after previously being visible.
+ */
+ public void onWindowHidden() {
+ }
+
/**
* Called when a new client has bound to the input method. This
* may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
@@ -1008,18 +1293,46 @@ public class InputMethodService extends AbstractInputMethodService {
public void onStartInput(EditorInfo attribute, boolean restarting) {
}
- void doStartInput(EditorInfo attribute, boolean restarting) {
- if (mInputStarted && !restarting) {
+ void doFinishInput() {
+ if (mInputViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishInputView");
+ onFinishInputView(true);
+ } else if (mCandidatesViewStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishCandidatesView");
+ onFinishCandidatesView(true);
+ }
+ mInputViewStarted = false;
+ mCandidatesViewStarted = false;
+ if (mInputStarted) {
+ if (DEBUG) Log.v(TAG, "CALL: onFinishInput");
onFinishInput();
}
+ mInputStarted = false;
+ mStartedInputConnection = null;
+ mCurCompletions = null;
+ }
+
+ void doStartInput(InputConnection ic, EditorInfo attribute, boolean restarting) {
+ if (!restarting) {
+ doFinishInput();
+ }
mInputStarted = true;
+ mStartedInputConnection = ic;
mInputEditorInfo = attribute;
+ initialize();
+ if (DEBUG) Log.v(TAG, "CALL: onStartInput");
onStartInput(attribute, restarting);
if (mWindowVisible) {
if (mShowInputRequested) {
+ if (DEBUG) Log.v(TAG, "CALL: onStartInputView");
+ mInputViewStarted = true;
onStartInputView(mInputEditorInfo, restarting);
+ startExtractingText(true);
+ } else if (mCandidatesVisibility == View.VISIBLE) {
+ if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
+ mCandidatesViewStarted = true;
+ onStartCandidatesView(mInputEditorInfo, restarting);
}
- startExtractingText();
}
}
@@ -1029,8 +1342,17 @@ public class InputMethodService extends AbstractInputMethodService {
* {@link #onStartInput(EditorInfo, boolean)} to perform input in a
* new editor, or the input method may be left idle. This method is
* not called when input restarts in the same editor.
+ *
+ * The default
+ * implementation uses the InputConnection to clear any active composing
+ * text; you can override this (not calling the base class implementation)
+ * to perform whatever behavior you would like.
*/
public void onFinishInput() {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
}
/**
@@ -1055,9 +1377,11 @@ public class InputMethodService extends AbstractInputMethodService {
if (mExtractedToken != token) {
return;
}
- if (mExtractEditText != null && text != null) {
- mExtractedText = text;
- mExtractEditText.setExtractedText(text);
+ if (text != null) {
+ if (mExtractEditText != null) {
+ mExtractedText = text;
+ mExtractEditText.setExtractedText(text);
+ }
}
}
@@ -1073,9 +1397,19 @@ public class InputMethodService extends AbstractInputMethodService {
public void onUpdateSelection(int oldSelStart, int oldSelEnd,
int newSelStart, int newSelEnd,
int candidatesStart, int candidatesEnd) {
- if (mExtractEditText != null && mExtractedText != null) {
+ final ExtractEditText eet = mExtractEditText;
+ if (eet != null && isFullscreenMode() && mExtractedText != null) {
final int off = mExtractedText.startOffset;
- mExtractEditText.setSelection(newSelStart-off, newSelEnd-off);
+ eet.startInternalChanges();
+ newSelStart -= off;
+ newSelEnd -= off;
+ final int len = eet.getText().length();
+ if (newSelStart < 0) newSelStart = 0;
+ else if (newSelStart > len) newSelStart = len;
+ if (newSelEnd < 0) newSelEnd = 0;
+ else if (newSelEnd > len) newSelEnd = len;
+ eet.setSelection(newSelStart, newSelEnd);
+ eet.finishInternalChanges();
}
}
@@ -1095,38 +1429,91 @@ public class InputMethodService extends AbstractInputMethodService {
* 0 or have the {@link InputMethodManager#HIDE_IMPLICIT_ONLY
* InputMethodManager.HIDE_IMPLICIT_ONLY} bit set.
*/
- public void dismissSoftInput(int flags) {
- ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE))
- .hideSoftInputFromInputMethod(mToken, flags);
+ public void requestHideSelf(int flags) {
+ mImm.hideSoftInputFromInputMethod(mToken, flags);
+ }
+
+ /**
+ * Show the input method. This is a call back to the
+ * IMF to handle showing the input method.
+ * Close this input method's soft input area, removing it from the display.
+ * The input method will continue running, but the user can no longer use
+ * it to generate input by touching the screen.
+ * @param flags Provides additional operating flags. Currently may be
+ * 0 or have the {@link InputMethodManager#SHOW_FORCED
+ * InputMethodManager.} bit set.
+ */
+ private void requestShowSelf(int flags) {
+ mImm.showSoftInputFromInputMethod(mToken, flags);
}
+ /**
+ * Override this to intercept key down events before they are processed by the
+ * application. If you return true, the application will not itself
+ * process the event. If you return true, the normal application processing
+ * will occur as if the IME had not seen the event at all.
+ *
+ * The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
+ * KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
+ * additional, in fullscreen mode only, it will consume DPAD movement
+ * events to move the cursor in the extracted text view, not allowing
+ * them to perform navigation in the underlying application.
+ */
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0) {
if (mShowInputRequested) {
// If the soft input area is shown, back closes it and we
// consume the back key.
- dismissSoftInput(0);
+ requestHideSelf(0);
return true;
- }
- if (mShowCandidatesRequested) {
- // If the candidates are shown, we just want to make sure
- // they are now hidden but otherwise let the app execute
- // the back.
- // XXX this needs better interaction with the soft input
- // implementation.
- //setCandidatesViewShown(false);
+ } else if (mWindowVisible) {
+ if (mCandidatesVisibility == View.VISIBLE) {
+ // If we are showing candidates even if no input area, then
+ // hide them.
+ setCandidatesViewShown(false);
+ return true;
+ } else {
+ // If we have the window visible for some other reason --
+ // most likely to show candidates -- then just get rid
+ // of it. This really shouldn't happen, but just in case...
+ hideWindow();
+ return true;
+ }
}
}
- return false;
+ return doMovementKey(keyCode, event, MOVEMENT_DOWN);
}
+ /**
+ * Override this to intercept special key multiple events before they are
+ * processed by the
+ * application. If you return true, the application will not itself
+ * process the event. If you return true, the normal application processing
+ * will occur as if the IME had not seen the event at all.
+ *
+ * The default implementation always returns false, except when
+ * in fullscreen mode, where it will consume DPAD movement
+ * events to move the cursor in the extracted text view, not allowing
+ * them to perform navigation in the underlying application.
+ */
public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
- return false;
+ return doMovementKey(keyCode, event, count);
}
+ /**
+ * Override this to intercept key up events before they are processed by the
+ * application. If you return true, the application will not itself
+ * process the event. If you return true, the normal application processing
+ * will occur as if the IME had not seen the event at all.
+ *
+ * The default implementation always returns false, except when
+ * in fullscreen mode, where it will consume DPAD movement
+ * events to move the cursor in the extracted text view, not allowing
+ * them to perform navigation in the underlying application.
+ */
public boolean onKeyUp(int keyCode, KeyEvent event) {
- return false;
+ return doMovementKey(keyCode, event, MOVEMENT_UP);
}
public boolean onTrackballEvent(MotionEvent event) {
@@ -1136,21 +1523,415 @@ public class InputMethodService extends AbstractInputMethodService {
public void onAppPrivateCommand(String action, Bundle data) {
}
- void startExtractingText() {
- if (mExtractEditText != null && getCurrentInputStarted()
+ /**
+ * Handle a request by the system to toggle the soft input area.
+ */
+ private void onToggleSoftInput(int showFlags, int hideFlags) {
+ if (DEBUG) Log.v(TAG, "toggleSoftInput()");
+ if (isInputViewShown()) {
+ requestHideSelf(hideFlags);
+ } else {
+ requestShowSelf(showFlags);
+ }
+ }
+
+ static final int MOVEMENT_DOWN = -1;
+ static final int MOVEMENT_UP = -2;
+
+ void reportExtractedMovement(int keyCode, int count) {
+ int dx = 0, dy = 0;
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ dx = -count;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ dx = count;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ dy = -count;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ dy = count;
+ break;
+ }
+ onExtractedCursorMovement(dx, dy);
+ }
+
+ boolean doMovementKey(int keyCode, KeyEvent event, int count) {
+ final ExtractEditText eet = mExtractEditText;
+ if (isFullscreenMode() && isInputViewShown() && eet != null) {
+ // If we are in fullscreen mode, the cursor will move around
+ // the extract edit text, but should NOT cause focus to move
+ // to other fields.
+ MovementMethod movement = eet.getMovementMethod();
+ Layout layout = eet.getLayout();
+ if (movement != null && layout != null) {
+ // We want our own movement method to handle the key, so the
+ // cursor will properly move in our own word wrapping.
+ if (count == MOVEMENT_DOWN) {
+ if (movement.onKeyDown(eet,
+ (Spannable)eet.getText(), keyCode, event)) {
+ reportExtractedMovement(keyCode, 1);
+ return true;
+ }
+ } else if (count == MOVEMENT_UP) {
+ if (movement.onKeyUp(eet,
+ (Spannable)eet.getText(), keyCode, event)) {
+ return true;
+ }
+ } else {
+ if (movement.onKeyOther(eet, (Spannable)eet.getText(), event)) {
+ reportExtractedMovement(keyCode, count);
+ } else {
+ KeyEvent down = new KeyEvent(event, KeyEvent.ACTION_DOWN);
+ if (movement.onKeyDown(eet,
+ (Spannable)eet.getText(), keyCode, down)) {
+ KeyEvent up = new KeyEvent(event, KeyEvent.ACTION_UP);
+ movement.onKeyUp(eet,
+ (Spannable)eet.getText(), keyCode, up);
+ while (--count > 0) {
+ movement.onKeyDown(eet,
+ (Spannable)eet.getText(), keyCode, down);
+ movement.onKeyUp(eet,
+ (Spannable)eet.getText(), keyCode, up);
+ }
+ reportExtractedMovement(keyCode, count);
+ }
+ }
+ }
+ }
+ // Regardless of whether the movement method handled the key,
+ // we never allow DPAD navigation to the application.
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Send the given key event code (as defined by {@link KeyEvent}) to the
+ * current input connection is a key down + key up event pair. The sent
+ * events have {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD}
+ * set, so that the recipient can identify them as coming from a software
+ * input method, and
+ * {@link KeyEvent#FLAG_KEEP_TOUCH_MODE KeyEvent.FLAG_KEEP_TOUCH_MODE}, so
+ * that they don't impact the current touch mode of the UI.
+ *
+ * @param keyEventCode The raw key code to send, as defined by
+ * {@link KeyEvent}.
+ */
+ public void sendDownUpKeyEvents(int keyEventCode) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+ long eventTime = SystemClock.uptimeMillis();
+ ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
+ KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
+ ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+ KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
+ }
+
+ /**
+ * Ask the input target to execute its default action via
+ * {@link InputConnection#performEditorAction
+ * InputConnection.performEditorAction()}.
+ *
+ * @param fromEnterKey If true, this will be executed as if the user had
+ * pressed an enter key on the keyboard, that is it will not
+ * be done if the editor has set {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION
+ * EditorInfo.IME_FLAG_NO_ENTER_ACTION}. If false, the action will be
+ * sent regardless of how the editor has set that flag.
+ *
+ * @return Returns a boolean indicating whether an action has been sent.
+ * If false, either the editor did not specify a default action or it
+ * does not want an action from the enter key. If true, the action was
+ * sent (or there was no input connection at all).
+ */
+ public boolean sendDefaultEditorAction(boolean fromEnterKey) {
+ EditorInfo ei = getCurrentInputEditorInfo();
+ if (ei != null &&
+ (!fromEnterKey || (ei.imeOptions &
+ EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0) &&
+ (ei.imeOptions & EditorInfo.IME_MASK_ACTION) !=
+ EditorInfo.IME_ACTION_NONE) {
+ // If the enter key was pressed, and the editor has a default
+ // action associated with pressing enter, then send it that
+ // explicit action instead of the key event.
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.performEditorAction(ei.imeOptions&EditorInfo.IME_MASK_ACTION);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Send the given UTF-16 character to the current input connection. Most
+ * characters will be delivered simply by calling
+ * {@link InputConnection#commitText InputConnection.commitText()} with
+ * the character; some, however, may be handled different. In particular,
+ * the enter character ('\n') will either be delivered as an action code
+ * or a raw key event, as appropriate.
+ *
+ * @param charCode The UTF-16 character code to send.
+ */
+ public void sendKeyChar(char charCode) {
+ switch (charCode) {
+ case '\n': // Apps may be listening to an enter key to perform an action
+ if (!sendDefaultEditorAction(true)) {
+ sendDownUpKeyEvents(KeyEvent.KEYCODE_ENTER);
+ }
+ break;
+ default:
+ // Make sure that digits go through any text watcher on the client side.
+ if (charCode >= '0' && charCode <= '9') {
+ sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
+ } else {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.commitText(String.valueOf((char) charCode), 1);
+ }
+ }
+ break;
+ }
+ }
+
+ /**
+ * This is called when the user has moved the cursor in the extracted
+ * text view, when running in fullsreen mode. The default implementation
+ * performs the corresponding selection change on the underlying text
+ * editor.
+ */
+ public void onExtractedSelectionChanged(int start, int end) {
+ InputConnection conn = getCurrentInputConnection();
+ if (conn != null) {
+ conn.setSelection(start, end);
+ }
+ }
+
+ /**
+ * This is called when the user has clicked on the extracted text view,
+ * when running in fullscreen mode. The default implementation hides
+ * the candidates view when this happens, but only if the extracted text
+ * editor has a vertical scroll bar because its text doesn't fit.
+ * Re-implement this to provide whatever behavior you want.
+ */
+ public void onExtractedTextClicked() {
+ if (mExtractEditText == null) {
+ return;
+ }
+ if (mExtractEditText.hasVerticalScrollBar()) {
+ setCandidatesViewShown(false);
+ }
+ }
+
+ /**
+ * This is called when the user has performed a cursor movement in the
+ * extracted text view, when it is running in fullscreen mode. The default
+ * implementation hides the candidates view when a vertical movement
+ * happens, but only if the extracted text editor has a vertical scroll bar
+ * because its text doesn't fit.
+ * Re-implement this to provide whatever behavior you want.
+ * @param dx The amount of cursor movement in the x dimension.
+ * @param dy The amount of cursor movement in the y dimension.
+ */
+ public void onExtractedCursorMovement(int dx, int dy) {
+ if (mExtractEditText == null || dy == 0) {
+ return;
+ }
+ if (mExtractEditText.hasVerticalScrollBar()) {
+ setCandidatesViewShown(false);
+ }
+ }
+
+ /**
+ * This is called when the user has selected a context menu item from the
+ * extracted text view, when running in fullscreen mode. The default
+ * implementation sends this action to the current InputConnection's
+ * {@link InputConnection#performContextMenuAction(int)}, for it
+ * to be processed in underlying "real" editor. Re-implement this to
+ * provide whatever behavior you want.
+ */
+ public boolean onExtractTextContextMenuItem(int id) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.performContextMenuAction(id);
+ }
+ return true;
+ }
+
+ /**
+ * Return text that can be used as a button label for the given
+ * {@link EditorInfo#imeOptions EditorInfo.imeOptions}. Returns null
+ * if there is no action requested. Note that there is no guarantee that
+ * the returned text will be relatively short, so you probably do not
+ * want to use it as text on a soft keyboard key label.
+ *
+ * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
+ *
+ * @return Returns a label to use, or null if there is no action.
+ */
+ public CharSequence getTextForImeAction(int imeOptions) {
+ switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
+ case EditorInfo.IME_ACTION_NONE:
+ return null;
+ case EditorInfo.IME_ACTION_GO:
+ return getText(com.android.internal.R.string.ime_action_go);
+ case EditorInfo.IME_ACTION_SEARCH:
+ return getText(com.android.internal.R.string.ime_action_search);
+ case EditorInfo.IME_ACTION_SEND:
+ return getText(com.android.internal.R.string.ime_action_send);
+ case EditorInfo.IME_ACTION_NEXT:
+ return getText(com.android.internal.R.string.ime_action_next);
+ case EditorInfo.IME_ACTION_DONE:
+ return getText(com.android.internal.R.string.ime_action_done);
+ default:
+ return getText(com.android.internal.R.string.ime_action_default);
+ }
+ }
+
+ /**
+ * Called when it is time to update the actions available from a full-screen
+ * IME. You do not need to deal with this if you are using the standard
+ * full screen extract UI. If replacing it, you will need to re-implement
+ * this to put the action in your own UI and handle it.
+ */
+ public void onUpdateExtractingAccessories(EditorInfo ei) {
+ if (mExtractAccessories == null) {
+ return;
+ }
+ final boolean hasAction = ei.actionLabel != null || (
+ (ei.imeOptions&EditorInfo.IME_MASK_ACTION) != EditorInfo.IME_ACTION_NONE &&
+ (ei.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0);
+ if (hasAction) {
+ mExtractAccessories.setVisibility(View.VISIBLE);
+ if (ei.actionLabel != null) {
+ mExtractAction.setText(ei.actionLabel);
+ } else {
+ mExtractAction.setText(getTextForImeAction(ei.imeOptions));
+ }
+ mExtractAction.setOnClickListener(mActionClickListener);
+ } else {
+ mExtractAccessories.setVisibility(View.GONE);
+ mExtractAction.setOnClickListener(null);
+ }
+ }
+
+ /**
+ * This is called when, while currently displayed in extract mode, the
+ * current input target changes. The default implementation will
+ * auto-hide the IME if the new target is not a full editor, since this
+ * can be an confusing experience for the user.
+ */
+ public void onExtractingInputChanged(EditorInfo ei) {
+ if (ei.inputType == InputType.TYPE_NULL) {
+ requestHideSelf(InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+
+ void startExtractingText(boolean inputChanged) {
+ final ExtractEditText eet = mExtractEditText;
+ if (eet != null && getCurrentInputStarted()
&& isFullscreenMode()) {
mExtractedToken++;
ExtractedTextRequest req = new ExtractedTextRequest();
req.token = mExtractedToken;
+ req.flags = InputConnection.GET_TEXT_WITH_STYLES;
req.hintMaxLines = 10;
req.hintMaxChars = 10000;
- mExtractedText = mInputConnection.getExtractedText(req,
- InputConnection.EXTRACTED_TEXT_MONITOR);
- if (mExtractedText != null) {
- mExtractEditText.setExtractedText(mExtractedText);
+ mExtractedText = getCurrentInputConnection().getExtractedText(req,
+ InputConnection.GET_EXTRACTED_TEXT_MONITOR);
+
+ final EditorInfo ei = getCurrentInputEditorInfo();
+
+ try {
+ eet.startInternalChanges();
+ onUpdateExtractingAccessories(ei);
+ int inputType = ei.inputType;
+ if ((inputType&EditorInfo.TYPE_MASK_CLASS)
+ == EditorInfo.TYPE_CLASS_TEXT) {
+ if ((inputType&EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0) {
+ inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ }
+ }
+ eet.setInputType(inputType);
+ eet.setHint(ei.hintText);
+ if (mExtractedText != null) {
+ eet.setEnabled(true);
+ eet.setExtractedText(mExtractedText);
+ } else {
+ eet.setEnabled(false);
+ eet.setText("");
+ }
+ } finally {
+ eet.finishInternalChanges();
+ }
+
+ if (inputChanged) {
+ onExtractingInputChanged(ei);
}
- mExtractEditText.setInputType(getCurrentInputEditorInfo().inputType);
- mExtractEditText.setHint(mInputEditorInfo.hintText);
}
}
+
+ /**
+ * Performs a dump of the InputMethodService's internal state. Override
+ * to add your own information to the dump.
+ */
+ @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ final Printer p = new PrintWriterPrinter(fout);
+ p.println("Input method service state for " + this + ":");
+ p.println(" mWindowCreated=" + mWindowCreated
+ + " mWindowAdded=" + mWindowAdded
+ + " mWindowVisible=" + mWindowVisible);
+ p.println(" Configuration=" + getResources().getConfiguration());
+ p.println(" mToken=" + mToken);
+ p.println(" mInputBinding=" + mInputBinding);
+ p.println(" mInputConnection=" + mInputConnection);
+ p.println(" mStartedInputConnection=" + mStartedInputConnection);
+ p.println(" mInputStarted=" + mInputStarted
+ + " mInputViewStarted=" + mInputViewStarted
+ + " mCandidatesViewStarted=" + mCandidatesViewStarted);
+
+ if (mInputEditorInfo != null) {
+ p.println(" mInputEditorInfo:");
+ mInputEditorInfo.dump(p, " ");
+ } else {
+ p.println(" mInputEditorInfo: null");
+ }
+
+ p.println(" mShowInputRequested=" + mShowInputRequested
+ + " mLastShowInputRequested=" + mLastShowInputRequested
+ + " mShowInputForced=" + mShowInputForced
+ + " mShowInputFlags=0x" + Integer.toHexString(mShowInputFlags));
+ p.println(" mCandidatesVisibility=" + mCandidatesVisibility
+ + " mFullscreenApplied=" + mFullscreenApplied
+ + " mIsFullscreen=" + mIsFullscreen);
+
+ if (mExtractedText != null) {
+ p.println(" mExtractedText:");
+ p.println(" text=" + mExtractedText.text.length() + " chars"
+ + " startOffset=" + mExtractedText.startOffset);
+ p.println(" selectionStart=" + mExtractedText.selectionStart
+ + " selectionEnd=" + mExtractedText.selectionEnd
+ + " flags=0x" + Integer.toHexString(mExtractedText.flags));
+ } else {
+ p.println(" mExtractedText: null");
+ }
+ p.println(" mExtractedToken=" + mExtractedToken);
+ p.println(" mIsInputViewShown=" + mIsInputViewShown
+ + " mStatusIcon=" + mStatusIcon);
+ p.println("Last computed insets:");
+ p.println(" contentTopInsets=" + mTmpInsets.contentTopInsets
+ + " visibleTopInsets=" + mTmpInsets.visibleTopInsets
+ + " touchableInsets=" + mTmpInsets.touchableInsets);
+ }
}
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index cfd3188bec4df45cab11cae9e8213334eac3b565..6a560ce0150bedff3917561567d6fb565ecd15ee 100755
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -132,7 +132,19 @@ public class Keyboard {
/** Keyboard mode, or zero, if none. */
private int mKeyboardMode;
+
+ // Variables for pre-computing nearest keys.
+ private static final int GRID_WIDTH = 10;
+ private static final int GRID_HEIGHT = 5;
+ private static final int GRID_SIZE = GRID_WIDTH * GRID_HEIGHT;
+ private int mCellWidth;
+ private int mCellHeight;
+ private int[][] mGridNeighbors;
+ private int mProximityThreshold;
+ /** Number of key widths from current touch point to search for nearest keys. */
+ private static float SEARCH_DISTANCE = 1.4f;
+
/**
* Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
* Some of the key size defaults can be overridden per row from what the {@link Keyboard}
@@ -177,13 +189,13 @@ public class Keyboard {
parent.mDisplayWidth, parent.mDefaultWidth);
defaultHeight = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_keyHeight,
- parent.mDisplayWidth, parent.mDefaultHeight);
- defaultHorizontalGap = getDimensionOrFraction(a,
+ parent.mDisplayHeight, parent.mDefaultHeight);
+ defaultHorizontalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_horizontalGap,
parent.mDisplayWidth, parent.mDefaultHorizontalGap);
verticalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_verticalGap,
- parent.mDisplayWidth, parent.mDefaultVerticalGap);
+ parent.mDisplayHeight, parent.mDefaultVerticalGap);
a.recycle();
a = res.obtainAttributes(Xml.asAttributeSet(parser),
com.android.internal.R.styleable.Keyboard_Row);
@@ -540,7 +552,6 @@ public class Keyboard {
row.defaultHorizontalGap = mDefaultHorizontalGap;
row.verticalGap = mDefaultVerticalGap;
row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM;
-
final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
for (int i = 0; i < characters.length(); i++) {
char c = characters.charAt(i);
@@ -638,6 +649,52 @@ public class Keyboard {
public int getShiftKeyIndex() {
return mShiftKeyIndex;
}
+
+ private void computeNearestNeighbors() {
+ // Round-up so we don't have any pixels outside the grid
+ mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH;
+ mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT;
+ mGridNeighbors = new int[GRID_SIZE][];
+ int[] indices = new int[mKeys.size()];
+ final int gridWidth = GRID_WIDTH * mCellWidth;
+ final int gridHeight = GRID_HEIGHT * mCellHeight;
+ for (int x = 0; x < gridWidth; x += mCellWidth) {
+ for (int y = 0; y < gridHeight; y += mCellHeight) {
+ int count = 0;
+ for (int i = 0; i < mKeys.size(); i++) {
+ final Key key = mKeys.get(i);
+ if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
+ key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
+ key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
+ < mProximityThreshold ||
+ key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold) {
+ indices[count++] = i;
+ }
+ }
+ int [] cell = new int[count];
+ System.arraycopy(indices, 0, cell, 0, count);
+ mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell;
+ }
+ }
+ }
+
+ /**
+ * Returns the indices of the keys that are closest to the given point.
+ * @param x the x-coordinate of the point
+ * @param y the y-coordinate of the point
+ * @return the array of integer indices for the nearest keys to the given point. If the given
+ * point is out of range, then an array of size zero is returned.
+ */
+ public int[] getNearestKeys(int x, int y) {
+ if (mGridNeighbors == null) computeNearestNeighbors();
+ if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
+ int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth);
+ if (index < GRID_SIZE) {
+ return mGridNeighbors[index];
+ }
+ }
+ return new int[0];
+ }
protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
return new Row(res, this, parser);
@@ -739,6 +796,8 @@ public class Keyboard {
mDefaultVerticalGap = getDimensionOrFraction(a,
com.android.internal.R.styleable.Keyboard_verticalGap,
mDisplayHeight, 0);
+ mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
+ mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison
a.recycle();
}
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 2f3b54bee71613f51777c614ca23fd1e7ccfa7c3..7a63c0c02161bd81f27508b9b1f7c22f0e45f206 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -16,34 +16,33 @@
package android.inputmethodservice;
-import com.android.internal.R;
-
import android.content.Context;
-import android.content.SharedPreferences;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
+import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard.Key;
import android.os.Handler;
import android.os.Message;
-import android.os.Vibrator;
-import android.preference.PreferenceManager;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup.LayoutParams;
-import android.widget.Button;
import android.widget.PopupWindow;
import android.widget.TextView;
+import com.android.internal.R;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -74,7 +73,6 @@ public class KeyboardView extends View implements View.OnClickListener {
* For keys that repeat, this is only called once.
* @param primaryCode the unicode of the key being pressed. If the touch is not on a valid
* key, the value will be zero.
- * @hide Pending API Council approval
*/
void onPress(int primaryCode);
@@ -82,7 +80,6 @@ public class KeyboardView extends View implements View.OnClickListener {
* Called when the user releases a key. This is sent after the {@link #onKey} is called.
* For keys that repeat, this is only called once.
* @param primaryCode the code of the key that was released
- * @hide Pending API Council approval
*/
void onRelease(int primaryCode);
@@ -98,6 +95,12 @@ public class KeyboardView extends View implements View.OnClickListener {
*/
void onKey(int primaryCode, int[] keyCodes);
+ /**
+ * Sends a sequence of characters to the listener.
+ * @param text the sequence of characters to be displayed.
+ */
+ void onText(CharSequence text);
+
/**
* Called when the user quickly moves the finger from right to left.
*/
@@ -149,6 +152,7 @@ public class KeyboardView extends View implements View.OnClickListener {
private int mMiniKeyboardOffsetY;
private Map
+ * If an application uses the network in the background, it should listen
+ * for this broadcast and stop using the background data if the value is
+ * false.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
+ "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+
public static final int TYPE_MOBILE = 0;
public static final int TYPE_WIFI = 1;
@@ -223,6 +237,43 @@ public class ConnectivityManager
}
}
+ /**
+ * Returns the value of the setting for background data usage. If false,
+ * applications should not use the network if the application is not in the
+ * foreground. Developers should respect this setting, and check the value
+ * of this before performing any background data operations.
+ *
+ * All applications that have background services that use the network
+ * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
+ *
+ * @return Whether background data usage is allowed.
+ */
+ public boolean getBackgroundDataSetting() {
+ try {
+ return mService.getBackgroundDataSetting();
+ } catch (RemoteException e) {
+ // Err on the side of safety
+ return false;
+ }
+ }
+
+ /**
+ * Sets the value of the setting for background data usage.
+ *
+ * @param allowBackgroundData Whether an application should use data while
+ * it is in the background.
+ *
+ * @attr ref android.Manifest.permission#CHANGE_BACKGROUND_DATA_SETTING
+ * @see #getBackgroundDataSetting()
+ * @hide
+ */
+ public void setBackgroundDataSetting(boolean allowBackgroundData) {
+ try {
+ mService.setBackgroundDataSetting(allowBackgroundData);
+ } catch (RemoteException e) {
+ }
+ }
+
/**
* Don't allow use of default constructor.
*/
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e1d921f4d9a0cd7f01dcaa115236b283c6a3312f..de6859809d82e20ee41fa5d3a0327f729249395c 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -44,4 +44,8 @@ interface IConnectivityManager
int stopUsingNetworkFeature(int networkType, in String feature);
boolean requestRouteToHost(int networkType, int hostAddress);
+
+ boolean getBackgroundDataSetting();
+
+ void setBackgroundDataSetting(boolean allowBackgroundData);
}
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index f816caa6da2f77eac5fbabe78d5290b1387716d3..deaa3c35f99bca3238bcb5f733d5823121cc14cf 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -16,38 +16,45 @@
package android.net;
-import android.util.Log;
-import android.util.Config;
import android.net.http.DomainNameChecker;
import android.os.SystemProperties;
-
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
+import android.util.Config;
+import android.util.Log;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
-import java.security.NoSuchAlgorithmException;
+import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
-import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
+import org.apache.harmony.xnet.provider.jsse.SSLContextImpl;
+
+/**
+ * SSLSocketFactory that provides optional (on debug devices, only) skipping of ssl certificfate
+ * chain validation and custom read timeouts used just when connecting to the server/negotiating
+ * an ssl session.
+ *
+ * You can skip the ssl certificate checking at runtime by setting socket.relaxsslcheck=yes on
+ * devices that do not have have ro.secure set.
+ */
public class SSLCertificateSocketFactory extends SSLSocketFactory {
- private static final boolean DBG = true;
private static final String LOG_TAG = "SSLCertificateSocketFactory";
-
- private static X509TrustManager sDefaultTrustManager;
- private final int socketReadTimeoutForSslHandshake;
+ private static X509TrustManager sDefaultTrustManager;
static {
try {
@@ -83,27 +90,56 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
};
- private SSLSocketFactory factory;
+ private final SSLSocketFactory mFactory;
+
+ private final int mSocketReadTimeoutForSslHandshake;
+ /**
+ * Do not use this constructor (will be deprecated). Use {@link #getDefault(int)} instead.
+ */
public SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake)
- throws NoSuchAlgorithmException, KeyManagementException {
- SSLContext context = SSLContext.getInstance("TLS");
- context.init(null, TRUST_MANAGER, new java.security.SecureRandom());
- factory = (SSLSocketFactory) context.getSocketFactory();
- this.socketReadTimeoutForSslHandshake = socketReadTimeoutForSslHandshake;
+ throws NoSuchAlgorithmException, KeyManagementException {
+ this(socketReadTimeoutForSslHandshake, null /* cache */);
+ }
+
+ private SSLCertificateSocketFactory(int socketReadTimeoutForSslHandshake,
+ SSLClientSessionCache cache) throws NoSuchAlgorithmException, KeyManagementException {
+ SSLContextImpl sslContext = new SSLContextImpl();
+ sslContext.engineInit(null /* kms */,
+ TRUST_MANAGER, new java.security.SecureRandom(),
+ cache /* client cache */, null /* server cache */);
+ this.mFactory = sslContext.engineGetSocketFactory();
+ this.mSocketReadTimeoutForSslHandshake = socketReadTimeoutForSslHandshake;
}
/**
- * Returns a default instantiation of a new socket factory which
- * only allows SSL connections with valid certificates.
+ * Returns a new instance of a socket factory using the specified socket read
+ * timeout while connecting with the server/negotiating an ssl session.
*
* @param socketReadTimeoutForSslHandshake the socket read timeout used for performing
* ssl handshake. The socket read timeout is set back to 0 after the handshake.
* @return a new SocketFactory, or null on error
*/
public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake) {
+ return getDefault(socketReadTimeoutForSslHandshake, null /* cache */);
+ }
+
+ /**
+ * Returns a new instance of a socket factory using the specified socket read
+ * timeout while connecting with the server/negotiating an ssl session.
+ * Persists ssl sessions using the provided {@link SSLClientSessionCache}.
+ *
+ * @param socketReadTimeoutForSslHandshake the socket read timeout used for performing
+ * ssl handshake. The socket read timeout is set back to 0 after the handshake.
+ * @param cache The {@link SSLClientSessionCache} to use, if any.
+ * @return a new SocketFactory, or null on error
+ *
+ * @hide
+ */
+ public static SocketFactory getDefault(int socketReadTimeoutForSslHandshake,
+ SSLClientSessionCache cache) {
try {
- return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake);
+ return new SSLCertificateSocketFactory(socketReadTimeoutForSslHandshake, cache);
} catch (NoSuchAlgorithmException e) {
Log.e(LOG_TAG,
"SSLCertifcateSocketFactory.getDefault" +
@@ -217,10 +253,10 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
public Socket createSocket(String s, int i, InetAddress inaddr, int j) throws IOException {
- SSLSocket sslSock = (SSLSocket) factory.createSocket(s, i, inaddr, j);
+ SSLSocket sslSock = (SSLSocket) mFactory.createSocket(s, i, inaddr, j);
- if (socketReadTimeoutForSslHandshake >= 0) {
- sslSock.setSoTimeout(socketReadTimeoutForSslHandshake);
+ if (mSocketReadTimeoutForSslHandshake >= 0) {
+ sslSock.setSoTimeout(mSocketReadTimeoutForSslHandshake);
}
validateSocket(sslSock,s);
@@ -230,10 +266,10 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
public Socket createSocket(String s, int i) throws IOException {
- SSLSocket sslSock = (SSLSocket) factory.createSocket(s, i);
+ SSLSocket sslSock = (SSLSocket) mFactory.createSocket(s, i);
- if (socketReadTimeoutForSslHandshake >= 0) {
- sslSock.setSoTimeout(socketReadTimeoutForSslHandshake);
+ if (mSocketReadTimeoutForSslHandshake >= 0) {
+ sslSock.setSoTimeout(mSocketReadTimeoutForSslHandshake);
}
validateSocket(sslSock,s);
@@ -243,11 +279,11 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
public String[] getDefaultCipherSuites() {
- return factory.getSupportedCipherSuites();
+ return mFactory.getSupportedCipherSuites();
}
public String[] getSupportedCipherSuites() {
- return factory.getSupportedCipherSuites();
+ return mFactory.getSupportedCipherSuites();
}
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 32a26e42d6157731851c443e750a51abed8d9183..c23df217a856d3e3a58517af4cf30ff61dcc251e 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -2235,12 +2235,13 @@ public abstract class Uri implements Parcelable, Comparable
@@ -361,7 +361,7 @@ public class UrlQuerySanitizer {
}
return value.substring(start, end + 1);
}
-
+
/**
* Check if c is whitespace.
* @param c character to test
@@ -380,7 +380,7 @@ public class UrlQuerySanitizer {
return false;
}
}
-
+
/**
* Check whether an individual character is legal. Uses the
* flag bit-set passed into the constructor.
@@ -400,11 +400,11 @@ public class UrlQuerySanitizer {
case '%' : return (mFlags & PCT_OK) != 0;
case '\0': return (mFlags & NUL_OK) != 0;
default : return (c >= 32 && c < 127) ||
- (c >= 128 && c <= 255 && ((mFlags & NON_7_BIT_ASCII_OK) != 0));
- }
+ ((c >= 128) && ((mFlags & NON_7_BIT_ASCII_OK) != 0));
+ }
}
}
-
+
/**
* Get the current value sanitizer used when processing
* unregistered parameter values.
@@ -412,14 +412,14 @@ public class UrlQuerySanitizer {
* Note: The default unregistered parameter value sanitizer is
* one that doesn't allow any special characters, similar to what
* is returned by calling createAllIllegal.
- *
+ *
* @return the current ValueSanitizer used to sanitize unregistered
* parameter values.
*/
public ValueSanitizer getUnregisteredParameterValueSanitizer() {
return mUnregisteredParameterValueSanitizer;
}
-
+
/**
* Set the value sanitizer used when processing unregistered
* parameter values.
@@ -430,46 +430,46 @@ public class UrlQuerySanitizer {
ValueSanitizer sanitizer) {
mUnregisteredParameterValueSanitizer = sanitizer;
}
-
-
+
+
// Private fields for singleton sanitizers:
-
+
private static final ValueSanitizer sAllIllegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.ALL_ILLEGAL);
-
+
private static final ValueSanitizer sAllButNulLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.ALL_BUT_NUL_LEGAL);
-
+
private static final ValueSanitizer sAllButWhitespaceLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.ALL_BUT_WHITESPACE_LEGAL);
-
+
private static final ValueSanitizer sURLLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.URL_LEGAL);
-
+
private static final ValueSanitizer sUrlAndSpaceLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.URL_AND_SPACE_LEGAL);
-
+
private static final ValueSanitizer sAmpLegal =
new IllegalCharacterValueSanitizer(
- IllegalCharacterValueSanitizer.AMP_LEGAL);
-
+ IllegalCharacterValueSanitizer.AMP_LEGAL);
+
private static final ValueSanitizer sAmpAndSpaceLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.AMP_AND_SPACE_LEGAL);
-
+
private static final ValueSanitizer sSpaceLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.SPACE_LEGAL);
-
+
private static final ValueSanitizer sAllButNulAndAngleBracketsLegal =
new IllegalCharacterValueSanitizer(
IllegalCharacterValueSanitizer.ALL_BUT_NUL_AND_ANGLE_BRACKETS_LEGAL);
-
+
/**
* Return a value sanitizer that does not allow any special characters,
* and also does not allow script URLs.
@@ -478,7 +478,7 @@ public class UrlQuerySanitizer {
public static final ValueSanitizer getAllIllegal() {
return sAllIllegal;
}
-
+
/**
* Return a value sanitizer that allows everything except Nul ('\0')
* characters. Script URLs are allowed.
@@ -547,7 +547,7 @@ public class UrlQuerySanitizer {
public static final ValueSanitizer getAllButNulAndAngleBracketsLegal() {
return sAllButNulAndAngleBracketsLegal;
}
-
+
/**
* Constructs a UrlQuerySanitizer.
*
@@ -560,7 +560,7 @@ public class UrlQuerySanitizer {
*/
public UrlQuerySanitizer() {
}
-
+
/**
* Constructs a UrlQuerySanitizer and parse a URL.
* This constructor is provided for convenience when the
@@ -585,7 +585,7 @@ public class UrlQuerySanitizer {
setAllowUnregisteredParamaters(true);
parseUrl(url);
}
-
+
/**
* Parse the query parameters out of an encoded URL.
* Works by extracting the query portion from the URL and then
@@ -604,7 +604,7 @@ public class UrlQuerySanitizer {
}
parseQuery(query);
}
-
+
/**
* Parse a query. A query string is any number of parameter-value clauses
* separated by any non-zero number of ampersands. A parameter-value clause
@@ -631,7 +631,7 @@ public class UrlQuerySanitizer {
}
}
}
-
+
/**
* Get a set of all of the parameters found in the sanitized query.
*
@@ -641,7 +641,7 @@ public class UrlQuerySanitizer {
public Set
* The preferred parameter is the one that is returned when getParameter
@@ -746,7 +746,7 @@ public class UrlQuerySanitizer {
boolean preferFirstRepeatedParameter) {
mPreferFirstRepeatedParameter = preferFirstRepeatedParameter;
}
-
+
/**
* Get whether or not the first occurrence of a repeated parameter is
* preferred.
@@ -757,10 +757,10 @@ public class UrlQuerySanitizer {
public boolean getPreferFirstRepeatedParameter() {
return mPreferFirstRepeatedParameter;
}
-
+
/**
* Parse an escaped parameter-value pair. The default implementation
- * unescapes both the parameter and the value, then looks up the
+ * unescapes both the parameter and the value, then looks up the
* effective value sanitizer for the parameter and uses it to sanitize
* the value. If all goes well then addSanitizedValue is called with
* the unescaped parameter and the sanitized unescaped value.
@@ -779,7 +779,7 @@ public class UrlQuerySanitizer {
String sanitizedValue = valueSanitizer.sanitize(unescapedValue);
addSanitizedEntry(unescapedParameter, sanitizedValue);
}
-
+
/**
* Record a sanitized parameter-value pair. Override if you want to
* do additional filtering or validation.
@@ -796,7 +796,7 @@ public class UrlQuerySanitizer {
}
mEntries.put(parameter, value);
}
-
+
/**
* Get the value sanitizer for a parameter. Returns null if there
* is no value sanitizer registered for the parameter.
@@ -807,7 +807,7 @@ public class UrlQuerySanitizer {
public ValueSanitizer getValueSanitizer(String parameter) {
return mSanitizers.get(parameter);
}
-
+
/**
* Get the effective value sanitizer for a parameter. Like getValueSanitizer,
* except if there is no value sanitizer registered for a parameter, and
@@ -823,7 +823,7 @@ public class UrlQuerySanitizer {
}
return sanitizer;
}
-
+
/**
* Unescape an escaped string.
* Most developers will not implement this class directly, instead using the
- * aidl tool to describe the desired
+ * aidl tool to describe the desired
* interface, having it generate the appropriate Binder subclass. You can,
* however, derive directly from Binder to implement your own custom RPC
* protocol or simply instantiate a raw Binder object directly to use as a
@@ -194,18 +194,15 @@ public class Binder implements IBinder {
return true;
} else if (code == DUMP_TRANSACTION) {
ParcelFileDescriptor fd = data.readFileDescriptor();
- FileOutputStream fout = fd != null
- ? new FileOutputStream(fd.getFileDescriptor()) : null;
- PrintWriter pw = fout != null ? new PrintWriter(fout) : null;
- if (pw != null) {
- String[] args = data.readStringArray();
- dump(fd.getFileDescriptor(), pw, args);
- pw.flush();
- }
+ String[] args = data.readStringArray();
if (fd != null) {
try {
- fd.close();
- } catch (IOException e) {
+ dump(fd.getFileDescriptor(), args);
+ } finally {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
}
}
return true;
@@ -213,6 +210,20 @@ public class Binder implements IBinder {
return false;
}
+ /**
+ * Implemented to call the more convenient version
+ * {@link #dump(FileDescriptor, PrintWriter, String[])}.
+ */
+ public void dump(FileDescriptor fd, String[] args) {
+ FileOutputStream fout = new FileOutputStream(fd);
+ PrintWriter pw = new PrintWriter(fout);
+ try {
+ dump(fd, pw, args);
+ } finally {
+ pw.flush();
+ }
+ }
+
/**
* Print the object's state into the given stream.
*
@@ -302,6 +313,17 @@ final class BinderProxy implements IBinder {
throws RemoteException;
public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeFileDescriptor(fd);
+ data.writeStringArray(args);
+ try {
+ transact(DUMP_TRANSACTION, data, null, 0);
+ } finally {
+ data.recycle();
+ }
+ }
+
BinderProxy() {
mSelf = new WeakReference(this);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index cdf907b1ad05da5435a86b2d7c134507045c2a83..467c17f7d3a196fd5f408281f6bb5c0b8bafe889 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -26,6 +26,9 @@ public class Build {
/** Either a changelist number, or a label like "M4-rc20". */
public static final String ID = getString("ro.build.id");
+ /** A build ID string meant for displaying to the user */
+ public static final String DISPLAY = getString("ro.build.display.id");
+
/** The name of the overall product. */
public static final String PRODUCT = getString("ro.product.name");
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 5f7f91f4b9b07ed7e27991634e2c729c5b8a80c3..950bb09d03c4cf02654673e971a2d81747bb92da 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -17,6 +17,7 @@
package android.os;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
@@ -28,12 +29,13 @@ import dalvik.bytecode.Opcodes;
import dalvik.system.VMDebug;
-/** Provides various debugging functions for Android applications, including
+/**
+ * Provides various debugging functions for Android applications, including
* tracing and allocation counts.
* Logging Trace Files Debug can create log files that give details about an application, such as
* a call stack and start/stop times for any running methods. See Running the Traceview Debugging Program for
+href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading trace files. To start logging trace files, call one
* of the startMethodTracing() methods. To stop tracing, call
* {@link #stopMethodTracing()}.
@@ -285,7 +287,7 @@ public final class Debug
/**
* Start method tracing with default log name and buffer size. See Running the Traceview Debugging Program for
+href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading these files. Call stopMethodTracing() to stop
* tracing.
*/
@@ -297,7 +299,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra
* Start method tracing, specifying the trace log file name. The trace
* file will be put under "/sdcard" unless an absolute path is given.
* See Running the Traceview Debugging Program for
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading trace files.
*
* @param traceName Name for the trace log file to create.
@@ -313,7 +315,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra
* Start method tracing, specifying the trace log file name and the
* buffer size. The trace files will be put under "/sdcard" unless an
* absolute path is given. See Running the Traceview Debugging Program for
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading trace files.
* @param traceName Name for the trace log file to create.
* If no name argument is given, this value defaults to "/sdcard/dmtrace.trace".
@@ -330,7 +332,7 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra
* Start method tracing, specifying the trace log file name and the
* buffer size. The trace files will be put under "/sdcard" unless an
* absolute path is given. See Running the Traceview Debugging Program for
+ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Log Viewer for
* information about reading trace files.
*
*
@@ -580,6 +582,18 @@ href="{@docRoot}reference/traceview.html">Running the Traceview Debugging Progra
return VMDebug.getLoadedClassCount();
}
+ /**
+ * Dump "hprof" data to the specified file. This will cause a GC.
+ *
+ * @param fileName Full pathname of output file (e.g. "/sdcard/dump.hprof").
+ * @throws UnsupportedOperationException if the VM was built without
+ * HPROF support.
+ * @throws IOException if an error occurs while opening or writing files.
+ */
+ public static void dumpHprofData(String fileName) throws IOException {
+ VMDebug.dumpHprofData(fileName);
+ }
+
/**
* Returns the number of sent transactions from this process.
* @return The number of sent transactions or -1 if it could not read t.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e37b551903adc420ab7d2f9508e959f651880f52..f761e8e3d7138c65e5972a27d2c4ba561ea6bdbf 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -74,6 +74,18 @@ public class Environment {
*/
public static final String MEDIA_UNMOUNTED = "unmounted";
+ /**
+ * getExternalStorageState() returns MEDIA_CHECKING if the media is present
+ * and being disk-checked
+ */
+ public static final String MEDIA_CHECKING = "checking";
+
+ /**
+ * getExternalStorageState() returns MEDIA_NOFS if the media is present
+ * but is blank or is using an unsupported filesystem
+ */
+ public static final String MEDIA_NOFS = "nofs";
+
/**
* getExternalStorageState() returns MEDIA_MOUNTED if the media is present
* and mounted at its mount point with read/write access.
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 3ec0e9b4db675eb6613e695e27ca7e95bf658d57..5c40c9a041ad8554c0101ebc4942305e387db6b6 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -16,6 +16,9 @@
package android.os;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* Base interface for a remotable object, the core part of a lightweight
* remote procedure call mechanism designed for high performance when
@@ -144,6 +147,14 @@ public interface IBinder {
*/
public IInterface queryLocalInterface(String descriptor);
+ /**
+ * Print the object's state into the given stream.
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param args additional arguments to the dump request.
+ */
+ public void dump(FileDescriptor fd, String[] args) throws RemoteException;
+
/**
* Perform a generic operation with the object.
*
diff --git a/core/java/android/os/ICheckinService.aidl b/core/java/android/os/ICheckinService.aidl
index 70ad28e4e469888de8d96cc7548e8ac6d684cf8a..e56b55d78cd518b0152ffa937d8faa44aa78009a 100644
--- a/core/java/android/os/ICheckinService.aidl
+++ b/core/java/android/os/ICheckinService.aidl
@@ -26,6 +26,15 @@ import android.os.IParentalControlCallback;
* {@hide}
*/
interface ICheckinService {
+ /** Synchronously attempt a checkin with the server, return true
+ * on success.
+ * @throws IllegalStateException whenever an error occurs. The
+ * cause of the exception will be the real exception:
+ * IOException for network errors, JSONException for invalid
+ * server responses, etc.
+ */
+ boolean checkin();
+
/** Direct submission of crash data; returns after writing the crash. */
void reportCrashSync(in byte[] crashData);
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index 0397446155bb1553043f43ebb61dbc0f32d95a28..96d44b6441dd87d0bfb714d65362b71ae807a3c0 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -48,4 +48,31 @@ interface IMountService
* Safely unmount external storage at given mount point.
*/
void unmountMedia(String mountPoint);
+
+ /**
+ * Format external storage given a mount point.
+ */
+ void formatMedia(String mountPoint);
+
+ /**
+ * Returns true if media notification sounds are enabled.
+ */
+ boolean getPlayNotificationSounds();
+
+ /**
+ * Sets whether or not media notification sounds are played.
+ */
+ void setPlayNotificationSounds(boolean value);
+
+ /**
+ * Returns true if USB Mass Storage is automatically started
+ * when a UMS host is detected.
+ */
+ boolean getAutoStartUms();
+
+ /**
+ * Sets whether or not USB Mass Storage is automatically started
+ * when a UMS host is detected.
+ */
+ void setAutoStartUms(boolean value);
}
diff --git a/core/java/android/os/INetStatService.aidl b/core/java/android/os/INetStatService.aidl
index fb840d84fab34bab52655d03c7859870f18e747e..a8f3de0e044c6eb352b5e071bdf4f2d039471951 100644
--- a/core/java/android/os/INetStatService.aidl
+++ b/core/java/android/os/INetStatService.aidl
@@ -17,14 +17,19 @@
package android.os;
/**
- * Retrieves packet and byte counts for the phone data interface.
+ * Retrieves packet and byte counts for the phone data interface,
+ * and for all interfaces.
* Used for the data activity icon and the phone status in Settings.
*
* {@hide}
*/
interface INetStatService {
- int getTxPackets();
- int getRxPackets();
- int getTxBytes();
- int getRxBytes();
+ long getMobileTxPackets();
+ long getMobileRxPackets();
+ long getMobileTxBytes();
+ long getMobileRxBytes();
+ long getTotalTxPackets();
+ long getTotalRxPackets();
+ long getTotalTxBytes();
+ long getTotalRxBytes();
}
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index e48f1523a6beb529cdb9c82343fb351d0877ff48..5486920249cfad61bb70257e9022b49eec3930ce 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -29,4 +29,5 @@ interface IPowerManager
void setStayOnSetting(int val);
long getScreenOnTime();
void preventScreenOn(boolean prevent);
+ void setScreenBrightnessOverride(int brightness);
}
diff --git a/core/java/android/os/NetStat.java b/core/java/android/os/NetStat.java
index 7312236d743eb7cc875d17c6838cf942327653a0..e294cdf11c50d560b5c1f6ddf34f5c57cd49f75e 100644
--- a/core/java/android/os/NetStat.java
+++ b/core/java/android/os/NetStat.java
@@ -16,36 +16,232 @@
package android.os;
+import android.util.Log;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.io.IOException;
+
/** @hide */
-public class NetStat{
+public class NetStat {
+
+ // Logging tag.
+ private final static String TAG = "netstat";
+
+ // We pre-create all the File objects so we don't spend a lot of
+ // CPU at runtime converting from Java Strings to byte[] for the
+ // kernel calls.
+ private final static File[] MOBILE_TX_PACKETS = mobileFiles("tx_packets");
+ private final static File[] MOBILE_RX_PACKETS = mobileFiles("rx_packets");
+ private final static File[] MOBILE_TX_BYTES = mobileFiles("tx_bytes");
+ private final static File[] MOBILE_RX_BYTES = mobileFiles("rx_bytes");
+ private final static File SYS_CLASS_NET_DIR = new File("/sys/class/net");
/**
- * Get total number of tx packets sent through ppp0
+ * Get total number of tx packets sent through rmnet0 or ppp0
*
- * @return number of Tx packets through ppp0
+ * @return number of Tx packets through rmnet0 or ppp0
*/
+ public static long getMobileTxPkts() {
+ return getMobileStat(MOBILE_TX_PACKETS);
+ }
- public native static int netStatGetTxPkts();
+ /**
+ * Get total number of rx packets received through rmnet0 or ppp0
+ *
+ * @return number of Rx packets through rmnet0 or ppp0
+ */
+ public static long getMobileRxPkts() {
+ return getMobileStat(MOBILE_RX_PACKETS);
+ }
/**
- * Get total number of rx packets received through ppp0
+ * Get total number of tx bytes received through rmnet0 or ppp0
*
- * @return number of Rx packets through ppp0
+ * @return number of Tx bytes through rmnet0 or ppp0
*/
- public native static int netStatGetRxPkts();
+ public static long getMobileTxBytes() {
+ return getMobileStat(MOBILE_TX_BYTES);
+ }
- /**
- * Get total number of tx bytes received through ppp0
+ /**
+ * Get total number of rx bytes received through rmnet0 or ppp0
*
- * @return number of Tx bytes through ppp0
+ * @return number of Rx bytes through rmnet0 or ppp0
*/
- public native static int netStatGetTxBytes();
+ public static long getMobileRxBytes() {
+ return getMobileStat(MOBILE_RX_BYTES);
+ }
/**
- * Get total number of rx bytes received through ppp0
+ * Get the total number of packets sent through all network interfaces.
*
- * @return number of Rx bytes through ppp0
+ * @return the number of packets sent through all network interfaces
*/
- public native static int netStatGetRxBytes();
+ public static long getTotalTxPkts() {
+ return getTotalStat("tx_packets");
+ }
+
+ /**
+ * Get the total number of packets received through all network interfaces.
+ *
+ * @return the number of packets received through all network interfaces
+ */
+ public static long getTotalRxPkts() {
+ return getTotalStat("rx_packets");
+ }
+
+ /**
+ * Get the total number of bytes sent through all network interfaces.
+ *
+ * @return the number of bytes sent through all network interfaces
+ */
+ public static long getTotalTxBytes() {
+ return getTotalStat("tx_bytes");
+ }
+
+ /**
+ * Get the total number of bytes received through all network interfaces.
+ *
+ * @return the number of bytes received through all network interfaces
+ */
+ public static long getTotalRxBytes() {
+ return getTotalStat("rx_bytes");
+ }
+
+ /**
+ * Gets network bytes sent for this UID.
+ * The statistics are across all interfaces.
+ * The statistics come from /proc/uid_stat.
+ *
+ * {@see android.os.Process#myUid()}.
+ *
+ * @param uid
+ * @return byte count
+ */
+ public static long getUidTxBytes(int uid) {
+ return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_snd");
+ }
+
+ /**
+ * Gets network bytes received for this UID.
+ * The statistics are across all interfaces.
+ * The statistics come from /proc/uid_stat.
+ *
+ * {@see android.os.Process#myUid()}.
+ *
+ * @param uid
+ * @return byte count
+ */
+ public static long getUidRxBytes(int uid) {
+ return getNumberFromFilePath("/proc/uid_stat/" + uid + "/tcp_rcv");
+ }
+
+ /**
+ * Returns the array of two possible File locations for a given
+ * statistic.
+ */
+ private static File[] mobileFiles(String whatStat) {
+ // Note that we stat them at runtime to see which is
+ // available, rather than here, to guard against the files
+ // coming & going later as modules shut down (e.g. airplane
+ // mode) and whatnot. The runtime stat() isn't expensive compared
+ // to the previous charset conversion that happened before we
+ // were reusing File instances.
+ File[] files = new File[2];
+ files[0] = new File("/sys/class/net/rmnet0/statistics/" + whatStat);
+ files[1] = new File("/sys/class/net/ppp0/statistics/" + whatStat);
+ return files;
+ }
+
+ private static long getTotalStat(String whatStat) {
+ File netdir = new File("/sys/class/net");
+
+ File[] nets = SYS_CLASS_NET_DIR.listFiles();
+ if (nets == null) {
+ return 0;
+ }
+ long total = 0;
+ StringBuffer strbuf = new StringBuffer();
+ for (File net : nets) {
+ strbuf.append(net.getPath()).append(File.separator).append("statistics")
+ .append(File.separator).append(whatStat);
+ total += getNumberFromFilePath(strbuf.toString());
+ strbuf.setLength(0);
+ }
+ return total;
+ }
+
+ private static long getMobileStat(File[] files) {
+ for (int i = 0; i < files.length; i++) {
+ File file = files[i];
+ if (!file.exists()) {
+ continue;
+ }
+ try {
+ RandomAccessFile raf = new RandomAccessFile(file, "r");
+ return getNumberFromFile(raf, file.getAbsolutePath());
+ } catch (IOException e) {
+ Log.w(TAG,
+ "Exception opening TCP statistics file " + file.getAbsolutePath(),
+ e);
+ }
+ }
+ return 0L;
+ }
+
+ // File will have format
* <?xml version="1.0" encoding="UTF-8"?>
- * <LinearLayout
+ * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
* android:orientation="vertical"
* android:layout_width="fill_parent"
* android:layout_height="fill_parent"
- * android:paddingLeft="8"
- * android:paddingRight="8">
+ * android:paddingLeft="8dp"
+ * android:paddingRight="8dp">
*
- * <ExpandableListView id="android:list"
+ * <ExpandableListView android:id="@id/android:list"
* android:layout_width="fill_parent"
* android:layout_height="fill_parent"
* android:background="#00FF00"
* android:layout_weight="1"
* android:drawSelectorOnTop="false"/>
*
- * <TextView id="android:empty"
+ * <TextView android:id="@id/android:empty"
* android:layout_width="fill_parent"
* android:layout_height="fill_parent"
* android:background="#FF0000"
@@ -113,19 +113,19 @@ import java.util.Map;
*
*
* <?xml version="1.0" encoding="utf-8"?>
- * <LinearLayout
+ * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"
* android:orientation="vertical">
*
- * <TextView id="text1"
- * android:textSize="16"
+ * <TextView android:id="@+id/text1"
+ * android:textSize="16sp"
* android:textStyle="bold"
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"/>
*
- * <TextView id="text2"
- * android:textSize="16"
+ * <TextView android:id="@+id/text2"
+ * android:textSize="16sp"
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"/>
* </LinearLayout>
@@ -162,7 +162,8 @@ public class ExpandableListActivity extends Activity implements
/**
* Override this to populate the context menu when an item is long pressed. menuInfo
- * will contain a {@link AdapterContextMenuInfo} whose position is a packed position
+ * will contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo}
+ * whose packedPosition is a packed position
* that should be used with {@link ExpandableListView#getPackedPositionType(long)} and
* the other similar methods.
*
- * <?xml version="1.0" encoding="UTF-8"?>
- * <LinearLayout
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
* android:orientation="vertical"
* android:layout_width="fill_parent"
* android:layout_height="fill_parent"
- * android:paddingLeft="8"
- * android:paddingRight="8">
+ * android:paddingLeft="8dp"
+ * android:paddingRight="8dp">
*
- * <ListView id="android:list"
+ * <ListView android:id="@id/android:list"
* android:layout_width="fill_parent"
* android:layout_height="fill_parent"
* android:background="#00FF00"
* android:layout_weight="1"
* android:drawSelectorOnTop="false"/>
*
- * <TextView id="android:empty"
+ * <TextView id="@id/android:empty"
* android:layout_width="fill_parent"
* android:layout_height="fill_parent"
* android:background="#FF0000"
@@ -99,19 +99,19 @@ import android.widget.ListView;
*
*
* <?xml version="1.0" encoding="utf-8"?>
- * <LinearLayout
+ * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"
* android:orientation="vertical">
*
- * <TextView id="text1"
- * android:textSize="16"
+ * <TextView android:id="@+id/text1"
+ * android:textSize="16sp"
* android:textStyle="bold"
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"/>
*
- * <TextView id="text2"
- * android:textSize="16"
+ * <TextView android:id="@+id/text2"
+ * android:textSize="16sp"
* android:layout_width="fill_parent"
* android:layout_height="wrap_content"/>
* </LinearLayout>
@@ -142,8 +142,8 @@ import android.widget.ListView;
* public class MyListAdapter extends ListActivity {
*
* @Override
- * protected void onCreate(Bundle icicle){
- * super.onCreate(icicle);
+ * protected void onCreate(Bundle savedInstanceState){
+ * super.onCreate(savedInstanceState);
*
* // We'll define a custom screen layout here (the one shown above), but
* // typically, you could just use the standard ListActivity layout.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ea67cdbd474385a4c9b7d5ed0dfd09eabdac9378..51fddb1145cbbd50122aa7479b667419e235e0f9 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -90,8 +90,9 @@ public class Notification implements Parcelable
* The intent to execute when the expanded status entry is clicked. If
* this is an activity, it must include the
* {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
- * that you take care of task management as described in the
- * application model document.
+ * that you take care of task management as described in the Activities and Tasks
+ * section of the Application
+ * Fundamentals document.
*/
public PendingIntent contentIntent;
@@ -420,8 +421,8 @@ public class Notification implements Parcelable
* @param contentIntent The intent to launch when the user clicks the expanded notification.
* If this is an activity, it must include the
* {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
- * that you take care of task management as described in the
- * application model document.
+ * that you take care of task management as described in
+ * Application Fundamentals: Activities and Tasks.
*/
public void setLatestEventInfo(Context context,
CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index afb3827878edeb64903895c8279edcb9d0d1d43a..39edab70d141584cdb1638e67db0cf955e7a5209 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -82,9 +82,7 @@ public class NotificationManager
* @param id An 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. If you
- * pass null, there will be no persistent notification and no
- * flashing, vibration, etc.
+ * notify the user, other than the view you're providing. Must not be null.
*/
public void notify(int id, Notification notification)
{
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index b59e9dc435499eaa465abe931764e93d4b051b53..1bed706763a1a0c11a1ca803fd1c754643a8a1af 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -426,13 +426,9 @@ public final class PendingIntent implements Parcelable {
*/
@Override
public boolean equals(Object otherObj) {
- if (otherObj == null) {
- return false;
- }
- try {
+ if (otherObj instanceof PendingIntent) {
return mTarget.asBinder().equals(((PendingIntent)otherObj)
.mTarget.asBinder());
- } catch (ClassCastException e) {
}
return false;
}
@@ -442,6 +438,13 @@ public final class PendingIntent implements Parcelable {
return mTarget.asBinder().hashCode();
}
+ @Override
+ public String toString() {
+ return "PendingIntent{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " target " + (mTarget != null ? mTarget.asBinder() : null) + "}";
+ }
+
public int describeContents() {
return 0;
}
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 2e2a1a17a52e8519db444f178b9279abbe556f0b..64288d2e0af3b0e1f23ef1dcccec4ebc1aed0db9 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -16,11 +16,14 @@
package android.app;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -29,11 +32,11 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.server.search.SearchableInfo;
+import android.speech.RecognizerIntent;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
@@ -50,6 +53,7 @@ import android.widget.AdapterView;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.CursorAdapter;
+import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
@@ -94,6 +98,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
private TextView mBadgeLabel;
private AutoCompleteTextView mSearchTextField;
private Button mGoButton;
+ private ImageButton mVoiceButton;
// interaction with searchable application
private ComponentName mLaunchComponent;
@@ -115,10 +120,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
private Uri mSuggestionData = null;
private String mSuggestionQuery = null;
+ // For voice searching
+ private Intent mVoiceWebSearchIntent;
+ private Intent mVoiceAppSearchIntent;
+
// support for AutoCompleteTextView suggestions display
private SuggestionsAdapter mSuggestionsAdapter;
-
/**
* Constructor - fires it up and makes it look like the search UI.
*
@@ -153,12 +161,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mSearchTextField = (AutoCompleteTextView)
findViewById(com.android.internal.R.id.search_src_text);
mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
+ mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
// attach listeners
mSearchTextField.addTextChangedListener(mTextWatcher);
mSearchTextField.setOnKeyListener(mTextKeyListener);
mGoButton.setOnClickListener(mGoButtonClickListener);
mGoButton.setOnKeyListener(mButtonsKeyListener);
+ mVoiceButton.setOnClickListener(mVoiceButtonClickListener);
+ mVoiceButton.setOnKeyListener(mButtonsKeyListener);
// pre-hide all the extraneous elements
mBadgeLabel.setVisibility(View.GONE);
@@ -169,13 +180,19 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
setCanceledOnTouchOutside(true);
// Set up broadcast filters
- mCloseDialogsFilter = new
- IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ mCloseDialogsFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mPackageFilter = new IntentFilter();
mPackageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
mPackageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
mPackageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
mPackageFilter.addDataScheme("package");
+
+ // Save voice intent for later queries/launching
+ mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
+ mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
+
+ mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
}
/**
@@ -236,7 +253,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mSearchTextField.setAdapter(mSuggestionsAdapter);
mSearchTextField.setText(initialQuery);
} else {
- mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable);
+ mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable,
+ mSearchTextField);
mSearchTextField.setAdapter(mSuggestionsAdapter);
// finally, load the user's initial text (which may trigger suggestions)
@@ -261,16 +279,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
/**
- * Dismiss the search dialog.
+ * The search dialog is being dismissed, so handle all of the local shutdown operations.
*
- * This function is designed to be idempotent so it can be safely called at any time
+ * This function is designed to be idempotent so that dismiss() can be safely called at any time
* (even if already closed) and more likely to really dump any memory. No leaks!
*/
@Override
- public void dismiss() {
- if (isShowing()) {
- super.dismiss();
- }
+ public void onStop() {
+ super.onStop();
+
setOnCancelListener(null);
setOnDismissListener(null);
@@ -281,6 +298,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// This is OK - it just means we didn't have any registered
}
+ // close any leftover cursor
+ if (mSuggestionsAdapter != null) {
+ mSuggestionsAdapter.changeCursor(null);
+ }
+
// dump extra memory we're hanging on to
mLaunchComponent = null;
mAppSearchData = null;
@@ -408,6 +430,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
updateSearchButton();
updateSearchBadge();
updateQueryHint();
+ updateVoiceButton();
// In order to properly configure the input method (if one is being used), we
// need to let it know if we'll be providing suggestions. Although it would be
@@ -426,6 +449,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
}
mSearchTextField.setInputType(inputType);
+ mSearchTextField.setImeOptions(mSearchable.getImeOptions());
}
}
@@ -499,6 +523,30 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
}
+ /**
+ * Update the visibility of the voice button. There are actually two voice search modes,
+ * either of which will activate the button.
+ */
+ private void updateVoiceButton() {
+ int visibility = View.GONE;
+ if (mSearchable.getVoiceSearchEnabled()) {
+ Intent testIntent = null;
+ if (mSearchable.getVoiceSearchLaunchWebSearch()) {
+ testIntent = mVoiceWebSearchIntent;
+ } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
+ testIntent = mVoiceAppSearchIntent;
+ }
+ if (testIntent != null) {
+ ResolveInfo ri = getContext().getPackageManager().
+ resolveActivity(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (ri != null) {
+ visibility = View.VISIBLE;
+ }
+ }
+ }
+ mVoiceButton.setVisibility(visibility);
+ }
+
/**
* Listeners of various types
*/
@@ -641,12 +689,98 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
};
+ /**
+ * React to a click in the voice search button.
+ */
+ View.OnClickListener mVoiceButtonClickListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ try {
+ if (mSearchable.getVoiceSearchLaunchWebSearch()) {
+ getContext().startActivity(mVoiceWebSearchIntent);
+ dismiss();
+ } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
+ Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent);
+ getContext().startActivity(appSearchIntent);
+ dismiss();
+ }
+ } catch (ActivityNotFoundException e) {
+ // Should not happen, since we check the availability of
+ // voice search before showing the button. But just in case...
+ Log.w(LOG_TAG, "Could not find voice search activity");
+ }
+ }
+ };
+
+ /**
+ * Create and return an Intent that can launch the voice search activity, perform a specific
+ * voice transcription, and forward the results to the searchable activity.
+ *
+ * @param baseIntent The voice app search intent to start from
+ * @return A completely-configured intent ready to send to the voice search activity
+ */
+ private Intent createVoiceAppSearchIntent(Intent baseIntent) {
+ // create the necessary intent to set up a search-and-forward operation
+ // in the voice search system. We have to keep the bundle separate,
+ // because it becomes immutable once it enters the PendingIntent
+ Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
+ queryIntent.setComponent(mSearchable.mSearchActivity);
+ PendingIntent pending = PendingIntent.getActivity(
+ getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
+
+ // Now set up the bundle that will be inserted into the pending intent
+ // when it's time to do the search. We always build it here (even if empty)
+ // because the voice search activity will always need to insert "QUERY" into
+ // it anyway.
+ Bundle queryExtras = new Bundle();
+ if (mAppSearchData != null) {
+ queryExtras.putBundle(SearchManager.APP_DATA, mAppSearchData);
+ }
+
+ // Now build the intent to launch the voice search. Add all necessary
+ // extras to launch the voice recognizer, and then all the necessary extras
+ // to forward the results to the searchable activity
+ Intent voiceIntent = new Intent(baseIntent);
+
+ // Add all of the configuration options supplied by the searchable's metadata
+ String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
+ String prompt = null;
+ String language = null;
+ int maxResults = 1;
+ Resources resources = mActivityContext.getResources();
+ if (mSearchable.getVoiceLanguageModeId() != 0) {
+ languageModel = resources.getString(mSearchable.getVoiceLanguageModeId());
+ }
+ if (mSearchable.getVoicePromptTextId() != 0) {
+ prompt = resources.getString(mSearchable.getVoicePromptTextId());
+ }
+ if (mSearchable.getVoiceLanguageId() != 0) {
+ language = resources.getString(mSearchable.getVoiceLanguageId());
+ }
+ if (mSearchable.getVoiceMaxResults() != 0) {
+ maxResults = mSearchable.getVoiceMaxResults();
+ }
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
+
+ // Add the values that configure forwarding the results
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
+ voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);
+
+ return voiceIntent;
+ }
+
/**
* React to the user typing "enter" or other hardwired keys while typing in the search box.
* This handles these special keys while the edit box has focus.
*/
View.OnKeyListener mTextKeyListener = new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ cancel();
+ return true;
+ }
// also guard against possible race conditions (late arrival after dismiss)
if (mSearchable != null &&
TextUtils.getTrimmedLength(mSearchTextField.getText()) > 0) {
@@ -661,7 +795,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// otherwise, dispatch an "edit view" key
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_DPAD_CENTER:
if (event.getAction() == KeyEvent.ACTION_UP) {
v.cancelLongPress();
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
@@ -700,9 +833,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// also guard against possible race conditions (late arrival after dismiss)
if (mSearchable != null) {
handled = doSuggestionsKey(v, keyCode, event);
- if (!handled) {
- handled = refocusingKeyListener(v, keyCode, event);
- }
}
return handled;
}
@@ -792,24 +922,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
};
- /**
- * UI-thread handling of dialog dismiss. Called by mBroadcastReceiver.onReceive().
- *
- * TODO: This is a really heavyweight solution for something that should be so simple.
- * For example, we already have a handler, in our superclass, why aren't we sharing that?
- * I think we need to investigate simplifying this entire methodology, or perhaps boosting
- * it up into the Dialog class.
- */
- private static final int MESSAGE_DISMISS = 0;
- private Handler mDismissHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MESSAGE_DISMISS) {
- dismiss();
- }
- }
- };
-
/**
* Various ways to launch searches
*/
@@ -907,6 +1019,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @param jamQuery True means to set the query, false means to reset it to the user's choice
*/
private void jamSuggestionQuery(boolean jamQuery, AdapterView> parent, int position) {
+ // quick check against race conditions
+ if (mSearchable == null) {
+ return;
+ }
+
mSuggestionsAdapter.setNonUserQuery(true); // disables any suggestions processing
if (jamQuery) {
CursorAdapter ca = getSuggestionsAdapter(parent);
@@ -1180,10 +1297,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// These private variables are shared by the filter thread and must be protected
private WeakReference
*
*
*
*
* <!-- Search Activity - searchable -->
* <activity android:name="MySearchActivity"
@@ -746,6 +747,14 @@ import android.view.KeyEvent;
* inputType attribute.
*
No
*
+ *
*
*
*
@@ -765,9 +774,8 @@ import android.view.KeyEvent;
* android:imeOptions
+ * If provided, supplies additional options for the input method.
+ * For most searches, in which free form text is expected, this attribute
+ * need not be provided, and will default to "actionSearch".
+ * Suitable values for this attribute are described in the
+ * imeOptions attribute.
+ * No
+ *
* <!-- Content provider for search suggestions -->
@@ -832,7 +840,7 @@ import android.view.KeyEvent;
*
*
*
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 6c08e750c7d7af28409ef4d4d38b208eed103fb1..a6a436f4fd09747787e26cd0f60649bc72fedfb1 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -42,12 +42,12 @@ import java.io.PrintWriter;
* thread of their hosting process. This means that, if your service is going
* to do any CPU intensive (such as MP3 playback) or blocking (such as
* networking) operations, it should spawn its own thread in which to do that
- * work. More information on this can be found in the
- * Threading section of the
- * Application Model overview.
+ * work. More information on this can be found in
+ * Application Fundamentals:
+ * Processes and Threads.
*
* android:searchSuggestAuthority
* This value must match the authority string provided in the provider section
- * of your manifest.
+ * of your manifest.
* Yes
*
@@ -79,7 +79,7 @@ import java.io.PrintWriter;
* to the service. The service will remain running as long as the connection
* is established (whether or not the client retains a reference on the
* service's IBinder). Usually the IBinder returned is for a complex
- * interface that has been written
+ * interface that has been written
* in aidl.
*
*
+ *
+ */
+ public static void deleteAllHosts() {
+ try {
+ sService.deleteAllHosts();
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ public final AppWidgetHostView createView(Context context, int appWidgetId,
+ AppWidgetProviderInfo appWidget) {
+ AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget);
+ view.setAppWidget(appWidgetId, appWidget);
+ synchronized (mViews) {
+ mViews.put(appWidgetId, view);
+ }
+ RemoteViews views = null;
+ try {
+ views = sService.getAppWidgetViews(appWidgetId);
+ } catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ view.updateAppWidget(views);
+ return view;
+ }
+
+ /**
+ * Called to create the AppWidgetHostView. Override to return a custom subclass if you
+ * need it. {@more}
+ */
+ protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+ AppWidgetProviderInfo appWidget) {
+ return new AppWidgetHostView(context);
+ }
+
+ /**
+ * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
+ */
+ protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
+ }
+
+ void updateAppWidgetView(int appWidgetId, RemoteViews views) {
+ AppWidgetHostView v;
+ synchronized (mViews) {
+ v = mViews.get(appWidgetId);
+ }
+ if (v != null) {
+ v.updateAppWidget(views);
+ }
+ }
+}
+
+
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
new file mode 100644
index 0000000000000000000000000000000000000000..be0f96e8ab6a6d6cef4dcdf4ba305c9604589fa5
--- /dev/null
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appwidget;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Config;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.widget.FrameLayout;
+import android.widget.RemoteViews;
+import android.widget.TextView;
+
+/**
+ * Provides the glue to show AppWidget views. This class offers automatic animation
+ * between updates, and will try recycling old views for each incoming
+ * {@link RemoteViews}.
+ */
+public class AppWidgetHostView extends FrameLayout {
+ static final String TAG = "AppWidgetHostView";
+ static final boolean LOGD = false;
+ static final boolean CROSSFADE = false;
+
+ static final int VIEW_MODE_NOINIT = 0;
+ static final int VIEW_MODE_CONTENT = 1;
+ static final int VIEW_MODE_ERROR = 2;
+ static final int VIEW_MODE_DEFAULT = 3;
+
+ static final int FADE_DURATION = 1000;
+
+ // When we're inflating the initialLayout for a AppWidget, we only allow
+ // views that are allowed in RemoteViews.
+ static final LayoutInflater.Filter sInflaterFilter = new LayoutInflater.Filter() {
+ public boolean onLoadClass(Class clazz) {
+ return clazz.isAnnotationPresent(RemoteViews.RemoteView.class);
+ }
+ };
+
+ Context mContext;
+
+ int mAppWidgetId;
+ AppWidgetProviderInfo mInfo;
+ View mView;
+ int mViewMode = VIEW_MODE_NOINIT;
+ int mLayoutId = -1;
+ long mFadeStartTime = -1;
+ Bitmap mOld;
+ Paint mOldPaint = new Paint();
+
+ /**
+ * Create a host view. Uses default fade animations.
+ */
+ public AppWidgetHostView(Context context) {
+ this(context, android.R.anim.fade_in, android.R.anim.fade_out);
+ }
+
+ /**
+ * Create a host view. Uses specified animations when pushing
+ * {@link #updateAppWidget(RemoteViews)}.
+ *
+ * @param animationIn Resource ID of in animation to use
+ * @param animationOut Resource ID of out animation to use
+ */
+ public AppWidgetHostView(Context context, int animationIn, int animationOut) {
+ super(context);
+ mContext = context;
+ }
+
+ /**
+ * Set the AppWidget that will be displayed by this view.
+ */
+ public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
+ mAppWidgetId = appWidgetId;
+ mInfo = info;
+ }
+
+ public int getAppWidgetId() {
+ return mAppWidgetId;
+ }
+
+ public AppWidgetProviderInfo getAppWidgetInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Process a set of {@link RemoteViews} coming in as an update from the
+ * AppWidget provider. Will animate into these new views as needed.
+ */
+ public void updateAppWidget(RemoteViews remoteViews) {
+ if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld);
+
+ boolean recycled = false;
+ View content = null;
+ Exception exception = null;
+
+ // Capture the old view into a bitmap so we can do the crossfade.
+ if (CROSSFADE) {
+ if (mFadeStartTime < 0) {
+ if (mView != null) {
+ final int width = mView.getWidth();
+ final int height = mView.getHeight();
+ try {
+ mOld = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ } catch (OutOfMemoryError e) {
+ // we just won't do the fade
+ mOld = null;
+ }
+ if (mOld != null) {
+ //mView.drawIntoBitmap(mOld);
+ }
+ }
+ }
+ }
+
+ if (remoteViews == null) {
+ if (mViewMode == VIEW_MODE_DEFAULT) {
+ // We've already done this -- nothing to do.
+ return;
+ }
+ content = getDefaultView();
+ mLayoutId = -1;
+ mViewMode = VIEW_MODE_DEFAULT;
+ } else {
+ int layoutId = remoteViews.getLayoutId();
+
+ // If our stale view has been prepared to match active, and the new
+ // layout matches, try recycling it
+ if (content == null && layoutId == mLayoutId) {
+ try {
+ remoteViews.reapply(mContext, mView);
+ content = mView;
+ recycled = true;
+ if (LOGD) Log.d(TAG, "was able to recycled existing layout");
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+ }
+
+ // Try normal RemoteView inflation
+ if (content == null) {
+ try {
+ content = remoteViews.apply(mContext, this);
+ if (LOGD) Log.d(TAG, "had to inflate new layout");
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+ }
+
+ mLayoutId = layoutId;
+ mViewMode = VIEW_MODE_CONTENT;
+ }
+
+ if (content == null) {
+ if (mViewMode == VIEW_MODE_ERROR) {
+ // We've already done this -- nothing to do.
+ return ;
+ }
+ Log.w(TAG, "updateAppWidget couldn't find any view, using error view", exception);
+ content = getErrorView();
+ mViewMode = VIEW_MODE_ERROR;
+ }
+
+ if (!recycled) {
+ prepareView(content);
+ addView(content);
+ }
+
+ if (mView != content) {
+ removeView(mView);
+ mView = content;
+ }
+
+ if (CROSSFADE) {
+ if (mFadeStartTime < 0) {
+ // if there is already an animation in progress, don't do anything --
+ // the new view will pop in on top of the old one during the cross fade,
+ // and that looks okay.
+ mFadeStartTime = SystemClock.uptimeMillis();
+ invalidate();
+ }
+ }
+ }
+
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ if (CROSSFADE) {
+ int alpha;
+ int l = child.getLeft();
+ int t = child.getTop();
+ if (mFadeStartTime > 0) {
+ alpha = (int)(((drawingTime-mFadeStartTime)*255)/FADE_DURATION);
+ if (alpha > 255) {
+ alpha = 255;
+ }
+ Log.d(TAG, "drawChild alpha=" + alpha + " l=" + l + " t=" + t
+ + " w=" + child.getWidth());
+ if (alpha != 255 && mOld != null) {
+ mOldPaint.setAlpha(255-alpha);
+ //canvas.drawBitmap(mOld, l, t, mOldPaint);
+ }
+ } else {
+ alpha = 255;
+ }
+ int restoreTo = canvas.saveLayerAlpha(l, t, child.getWidth(), child.getHeight(), alpha,
+ Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ boolean rv = super.drawChild(canvas, child, drawingTime);
+ canvas.restoreToCount(restoreTo);
+ if (alpha < 255) {
+ invalidate();
+ } else {
+ mFadeStartTime = -1;
+ if (mOld != null) {
+ mOld.recycle();
+ mOld = null;
+ }
+ }
+ return rv;
+ } else {
+ return super.drawChild(canvas, child, drawingTime);
+ }
+ }
+
+ /**
+ * Prepare the given view to be shown. This might include adjusting
+ * {@link FrameLayout.LayoutParams} before inserting.
+ */
+ protected void prepareView(View view) {
+ // Take requested dimensions from parent, but apply default gravity.
+ ViewGroup.LayoutParams requested = view.getLayoutParams();
+ if (requested == null) {
+ requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT,
+ LayoutParams.FILL_PARENT);
+ }
+
+ FrameLayout.LayoutParams params =
+ new FrameLayout.LayoutParams(requested.width, requested.height);
+ params.gravity = Gravity.CENTER;
+ view.setLayoutParams(params);
+ }
+
+ /**
+ * Inflate and return the default layout requested by AppWidget provider.
+ */
+ protected View getDefaultView() {
+ View defaultView = null;
+ Exception exception = null;
+
+ try {
+ if (mInfo != null) {
+ Context theirContext = mContext.createPackageContext(
+ mInfo.provider.getPackageName(), 0 /* no flags */);
+ LayoutInflater inflater = (LayoutInflater)
+ theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater = inflater.cloneInContext(theirContext);
+ inflater.setFilter(sInflaterFilter);
+ defaultView = inflater.inflate(mInfo.initialLayout, this, false);
+ } else {
+ Log.w(TAG, "can't inflate defaultView because mInfo is missing");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ exception = e;
+ } catch (RuntimeException e) {
+ exception = e;
+ }
+
+ if (exception != null && LOGD) {
+ Log.w(TAG, "Error inflating AppWidget " + mInfo, exception);
+ }
+
+ if (defaultView == null) {
+ if (LOGD) Log.d(TAG, "getDefaultView couldn't find any view, so inflating error");
+ defaultView = getErrorView();
+ }
+
+ return defaultView;
+ }
+
+ /**
+ * Inflate and return a view that represents an error state.
+ */
+ protected View getErrorView() {
+ TextView tv = new TextView(mContext);
+ tv.setText(com.android.internal.R.string.gadget_host_error_inflating);
+ // TODO: get this color from somewhere.
+ tv.setBackgroundColor(Color.argb(127, 0, 0, 0));
+ return tv;
+ }
+}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b10ed282d1c7a59647cad5b7c706d2636ae7a44
--- /dev/null
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appwidget;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import com.android.internal.appwidget.IAppWidgetService;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.WeakHashMap;
+
+/**
+ * Updates AppWidget state; gets information about installed AppWidget providers and other
+ * AppWidget related state.
+ */
+public class AppWidgetManager {
+ static final String TAG = "AppWidgetManager";
+
+ /**
+ * Send this from your {@link AppWidgetHost} activity when you want to pick an AppWidget to display.
+ * The AppWidget picker activity will be launched.
+ *
+ *
+ *
+ *
+ *
+ * {@link #EXTRA_APPWIDGET_ID}
+ * A newly allocated appWidgetId, which will be bound to the AppWidget provider
+ * once the user has selected one.
+ *
+ *
+ *
+ *
+ * {@link #EXTRA_APPWIDGET_ID}
+ * The appWidgetId that you supplied in the original intent.
+ *
+ *
+ *
+ *
+ *
+ * {@link #EXTRA_APPWIDGET_ID}
+ * The appWidgetId to configure.
+ *
+ *
+ *
+ * @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
+ */
+ public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
+
+ /**
+ * Sent when an instance of an AppWidget is deleted from its host.
+ *
+ * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds)
+ */
+ public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
+
+ /**
+ * Sent when an instance of an AppWidget is removed from the last host.
+ *
+ * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
+ */
+ public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
+
+ /**
+ * Sent when an instance of an AppWidget is added to a host for the first time.
+ * This broadcast is sent at boot time if there is a AppWidgetHost installed with
+ * an instance for this provider.
+ *
+ * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
+ */
+ public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
+
+ /**
+ * Field for the manifest meta-data tag.
+ *
+ * @see AppWidgetProviderInfo
+ */
+ public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
+
+ static WeakHashMap
+ *
+ * {@link #EXTRA_APPWIDGET_IDS}
+ * The appWidgetIds to update. This may be all of the AppWidgets created for this
+ * provider, or just a subset. The system tries to send updates for as few AppWidget
+ * instances as possible.
+ * Sample Code
+ * For an example of how to write a AppWidget provider, see the
+ * android.appwidget
+ * package overview.
+ */
+public class AppWidgetProvider extends BroadcastReceiver {
+ /**
+ * Constructor to initialize AppWidgetProvider.
+ */
+ public AppWidgetProvider() {
+ }
+
+ /**
+ * Implements {@link BroadcastReceiver#onReceive} to dispatch calls to the various
+ * other methods on AppWidgetProvider.
+ *
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
+ // BEGIN_INCLUDE(onReceive)
+ public void onReceive(Context context, Intent intent) {
+ // Protect against rogue update broadcasts (not really a security issue,
+ // just filter bad broacasts out so subclasses are less likely to crash).
+ String action = intent.getAction();
+ if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+ if (appWidgetIds != null && appWidgetIds.length > 0) {
+ this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
+ }
+ }
+ }
+ else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+ if (appWidgetIds != null && appWidgetIds.length > 0) {
+ this.onDeleted(context, appWidgetIds);
+ }
+ }
+ }
+ else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
+ this.onEnabled(context);
+ }
+ else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
+ this.onDisabled(context);
+ }
+ }
+ // END_INCLUDE(onReceive)
+
+ /**
+ * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} broadcast when
+ * this AppWidget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
+ * for a set of AppWidgets. Override this method to implement your own AppWidget functionality.
+ *
+ * {@more}
+ *
+ * @param context The {@link android.content.Context Context} in which this receiver is
+ * running.
+ * @param appWidgetManager A {@link AppWidgetManager} object you can call {@link
+ * AppWidgetManager#updateAppWidget} on.
+ * @param appWidgetIds The appWidgetIds for which an update is needed. Note that this
+ * may be all of the AppWidget instances for this provider, or just
+ * a subset of them.
+ *
+ * @see AppWidgetManager#ACTION_APPWIDGET_UPDATE
+ */
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ }
+
+ /**
+ * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_DELETED} broadcast when
+ * one or more AppWidget instances have been deleted. Override this method to implement
+ * your own AppWidget functionality.
+ *
+ * {@more}
+ *
+ * @param context The {@link android.content.Context Context} in which this receiver is
+ * running.
+ * @param appWidgetIds The appWidgetIds that have been deleted from their host.
+ *
+ * @see AppWidgetManager#ACTION_APPWIDGET_DELETED
+ */
+ public void onDeleted(Context context, int[] appWidgetIds) {
+ }
+
+ /**
+ * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_ENABLED} broadcast when
+ * the a AppWidget for this provider is instantiated. Override this method to implement your
+ * own AppWidget functionality.
+ *
+ * {@more}
+ * When the last AppWidget for this provider is deleted,
+ * {@link AppWidgetManager#ACTION_APPWIDGET_DISABLED} is sent by the AppWidget manager, and
+ * {@link #onDisabled} is called. If after that, an AppWidget for this provider is created
+ * again, onEnabled() will be called again.
+ *
+ * @param context The {@link android.content.Context Context} in which this receiver is
+ * running.
+ *
+ * @see AppWidgetManager#ACTION_APPWIDGET_ENABLED
+ */
+ public void onEnabled(Context context) {
+ }
+
+ /**
+ * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_DISABLED} broadcast, which
+ * is sent when the last AppWidget instance for this provider is deleted. Override this method
+ * to implement your own AppWidget functionality.
+ *
+ * {@more}
+ *
+ * @param context The {@link android.content.Context Context} in which this receiver is
+ * running.
+ *
+ * @see AppWidgetManager#ACTION_APPWIDGET_DISABLED
+ */
+ public void onDisabled(Context context) {
+ }
+}
diff --git a/core/java/android/os/HandlerInterface.java b/core/java/android/appwidget/AppWidgetProviderInfo.aidl
similarity index 57%
rename from core/java/android/os/HandlerInterface.java
rename to core/java/android/appwidget/AppWidgetProviderInfo.aidl
index 62dc273e32c2e47fe225485ab1c3fcee26129f4b..82b3adac9e775c3cf66a005c60c0b21bf27e76b0 100644
--- a/core/java/android/os/HandlerInterface.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.aidl
@@ -1,27 +1,19 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2007, 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
+ * 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
+ * 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
+ * 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.os;
-
-/**
- * @hide
- * @deprecated
- */
-public interface HandlerInterface
-{
- void handleMessage(Message msg);
-}
+package android.appwidget;
+parcelable AppWidgetProviderInfo;
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..8530c355b93512042f4a93a448f40433c7b7647f
--- /dev/null
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appwidget;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.content.ComponentName;
+
+/**
+ * Describes the meta data for an installed AppWidget provider. The fields in this class
+ * correspond to the fields in the <appwidget-provider>
xml tag.
+ */
+public class AppWidgetProviderInfo implements Parcelable {
+ /**
+ * Identity of this AppWidget component. This component should be a {@link
+ * android.content.BroadcastReceiver}, and it will be sent the AppWidget intents
+ * {@link android.appwidget as described in the AppWidget package documentation}.
+ *
+ * android:name
attribute in
+ * the <receiver>
element in the AndroidManifest.xml file.
+ */
+ public ComponentName provider;
+
+ /**
+ * Minimum width of the AppWidget, in dp.
+ *
+ * android:minWidth
attribute in
+ * the AppWidget meta-data file.
+ */
+ public int minWidth;
+
+ /**
+ * Minimum height of the AppWidget, in dp.
+ *
+ * android:minHeight
attribute in
+ * the AppWidget meta-data file.
+ */
+ public int minHeight;
+
+ /**
+ * How often, in milliseconds, that this AppWidget wants to be updated.
+ * The AppWidget manager may place a limit on how often a AppWidget is updated.
+ *
+ * android:updatePeriodMillis
attribute in
+ * the AppWidget meta-data file.
+ */
+ public int updatePeriodMillis;
+
+ /**
+ * The resource id of the initial layout for this AppWidget. This should be
+ * displayed until the RemoteViews for the AppWidget is available.
+ *
+ * android:initialLayout
attribute in
+ * the AppWidget meta-data file.
+ */
+ public int initialLayout;
+
+ /**
+ * The activity to launch that will configure the AppWidget.
+ *
+ * android:configure
attribute in
+ * the AppWidget meta-data file. The package name always corresponds to the package containing
+ * the AppWidget provider.
+ */
+ public ComponentName configure;
+
+ /**
+ * The label to display to the user in the AppWidget picker. If not supplied in the
+ * xml, the application label will be used.
+ *
+ * android:label
attribute in
+ * the <receiver>
element in the AndroidManifest.xml file.
+ */
+ public String label;
+
+ /**
+ * The icon to display for this AppWidget in the AppWidget picker. If not supplied in the
+ * xml, the application icon will be used.
+ *
+ * android:icon
attribute in
+ * the <receiver>
element in the AndroidManifest.xml file.
+ */
+ public int icon;
+
+ public AppWidgetProviderInfo() {
+ }
+
+ /**
+ * Unflatten the AppWidgetProviderInfo from a parcel.
+ */
+ public AppWidgetProviderInfo(Parcel in) {
+ if (0 != in.readInt()) {
+ this.provider = new ComponentName(in);
+ }
+ this.minWidth = in.readInt();
+ this.minHeight = in.readInt();
+ this.updatePeriodMillis = in.readInt();
+ this.initialLayout = in.readInt();
+ if (0 != in.readInt()) {
+ this.configure = new ComponentName(in);
+ }
+ this.label = in.readString();
+ this.icon = in.readInt();
+ }
+
+
+ public void writeToParcel(android.os.Parcel out, int flags) {
+ if (this.provider != null) {
+ out.writeInt(1);
+ this.provider.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+ out.writeInt(this.minWidth);
+ out.writeInt(this.minHeight);
+ out.writeInt(this.updatePeriodMillis);
+ out.writeInt(this.initialLayout);
+ if (this.configure != null) {
+ out.writeInt(1);
+ this.configure.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+ out.writeString(this.label);
+ out.writeInt(this.icon);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Parcelable.Creator that instantiates AppWidgetProviderInfo objects
+ */
+ public static final Parcelable.CreatorAppWidget Providers
+
+
+AppWidget Hosts
+
+
+{@more}
+
+
+AppWidget Providers
+Declaring a widget in the AndroidManifest
+
+AndroidManifest.xml
file.
+
+{@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/AndroidManifest.xml AppWidgetProvider}
+
+<receiver>
element has the following attributes:
+
+
+
+android:name
- which specifies the
+ {@link android.content.BroadcastReceiver} or {@link android.appwidget.AppWidgetProvider}
+ class.android:label
- which specifies the string resource that
+ will be shown by the widget picker as the label.android:icon
- which specifies the drawable resource that
+ will be shown by the widget picker as the icon.<intent-filter>
element tells the {@link android.content.pm.PackageManager}
+that this {@link android.content.BroadcastReceiver} receives the {@link
+android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE AppWidgetManager.ACTION_APPWIDGET_UPDATE} broadcast.
+The widget manager will send other broadcasts directly to your widget provider as required.
+It is only necessary to explicitly declare that you accept the {@link
+android.appwidget.AppWidgetManager#ACTION_APPWIDGET_UPDATE AppWidgetManager.ACTION_APPWIDGET_UPDATE} broadcast.
+
+<meta-data>
element tells the widget manager which xml resource to
+read to find the {@link android.appwidget.AppWidgetProviderInfo} for your widget provider. It has the following
+attributes:
+
+
+
+
+android:name="android.appwidget.provider"
- identifies this meta-data
+ as the {@link android.appwidget.AppWidgetProviderInfo} descriptor.android:resource
- is the xml resource to use as that descriptor.Adding the {@link android.appwidget.AppWidgetProviderInfo AppWidgetProviderInfo} meta-data
+
+android:resource="@xml/appwidget_info"
. That XML file would go in your application's
+directory at res/xml/appwidget_info.xml
. Here is a simple example.
+
+{@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/res/xml/appwidget_info.xml AppWidgetProviderInfo}
+
+Using the {@link android.appwidget.AppWidgetProvider AppWidgetProvider} class
+
+src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.java
+sample class in ApiDemos for an example.
+
+AppWidget Configuration UI
+
+android:configure
attribute.
+
+src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java
+sample class in ApiDemos for an example.
+
+
+
+AppWidget Broadcast Intents
+
+
+
+
+AppWidget Hosts
+
@@ -131,7 +135,7 @@ import android.util.Log;
* tag in their
AndroidManifest.xml
) will be able to send an
* Intent to the receiver.
*
- * // Request a specific record.
@@ -330,9 +338,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
*
vnd.android.cursor.item
for a single record,
* or vnd.android.cursor.dir/
for multiple items.
* This method can be called from multiple
- * threads, as described in the
- * Threading section of
- * the Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
*
* @param uri the URI to query.
* @return a MIME type string, or null if there is no type.
@@ -344,9 +352,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after inserting.
* This method can be called from multiple
- * threads, as described in the
- * Threading section of the
- * Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
* @param uri The content:// URI of the insertion request.
* @param values A set of column_name/value pairs to add to the database.
* @return The URI for the newly inserted item.
@@ -359,9 +367,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after inserting.
* This method can be called from multiple
- * threads, as described in the
- * Threading section of
- * the Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
*
* @param uri The content:// URI of the insertion request.
* @param values An array of sets of column_name/value pairs to add to the database.
@@ -382,9 +390,9 @@ public abstract class ContentProvider implements ComponentCallbacks {
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()}
* after deleting.
* This method can be called from multiple
- * threads, as described in the
- * Threading section of the
- * Application Model overview.
+ * threads, as described in
+ * Application Fundamentals:
+ * Processes and Threads.
*
* The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme
- *
- *
- * @param uri The desired "content:" URI.
+ *
+ * android.resource://package_name/id_number
- * package_name
is your package name as listed in your AndroidManifest.xml.
- * For example com.example.myapp
- * id_number
is the int form of the ID.
- * The easiest way to construct this form is
- * Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");
- * android.resource://package_name/type/name
- * package_name
is your package name as listed in your AndroidManifest.xml.
- * For example com.example.myapp
- * type
is the string form of the resource type. For example, raw
- * or drawable
.
- * name
is the string form of the resource name. That is, whatever the file
- * name was in your res directory, without the type extension.
- * The easiest way to construct this form is
- * Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");
- * Accepts the following URI schemes:
*
*
*
- * @param uri The desired "content:" URI.
+ * Accepts the following URI schemes:
*
*
*
+ * Accepts the following URI schemes:
+ *
+ *
+ * The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme
+ *
+ *
+ *
+ * @param uri The desired URI to open.
+ * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
+ * ContentProvider.openAssetFile}.
+ * @return Returns a new ParcelFileDescriptor pointing to the file. You
+ * own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException of no
+ * file exists under the URI or the mode is invalid.
+ */
+ public final AssetFileDescriptor openAssetFileDescriptor(Uri uri,
+ String mode) throws FileNotFoundException {
+ String scheme = uri.getScheme();
+ if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
+ if (!"r".equals(mode)) {
+ throw new FileNotFoundException("Can't write resources: " + uri);
+ }
+ OpenResourceIdResult r = getResourceId(uri);
+ try {
+ return r.r.openRawResourceFd(r.id);
+ } catch (Resources.NotFoundException ex) {
+ throw new FileNotFoundException("Resource does not exist: " + uri);
+ }
+ } else if (SCHEME_FILE.equals(scheme)) {
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+ new File(uri.getPath()), modeToMode(uri, mode));
+ return new AssetFileDescriptor(pfd, 0, -1);
+ } else {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new FileNotFoundException("No content provider: " + uri);
+ }
+ try {
+ AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
+ if(fd == null) {
+ releaseProvider(provider);
+ return null;
+ }
+ ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+ fd.getParcelFileDescriptor(), provider);
+ return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+ fd.getDeclaredLength());
+ } catch (RemoteException e) {
releaseProvider(provider);
- return null;
+ throw new FileNotFoundException("Dead content provider: " + uri);
+ } catch (FileNotFoundException e) {
+ releaseProvider(provider);
+ throw e;
+ } catch (RuntimeException e) {
+ releaseProvider(provider);
+ throw e;
}
- return new ParcelFileDescriptorInner(fd, provider);
- } catch (RemoteException e) {
- releaseProvider(provider);
- throw new FileNotFoundException("Dead content provider: " + uri);
- } catch (FileNotFoundException e) {
- releaseProvider(provider);
- throw e;
- } catch (RuntimeException e) {
- releaseProvider(provider);
- throw e;
}
}
+ class OpenResourceIdResult {
+ Resources r;
+ int id;
+ }
+
+ OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
+ String authority = uri.getAuthority();
+ Resources r;
+ if (TextUtils.isEmpty(authority)) {
+ throw new FileNotFoundException("No authority: " + uri);
+ } else {
+ try {
+ r = mContext.getPackageManager().getResourcesForApplication(authority);
+ } catch (NameNotFoundException ex) {
+ throw new FileNotFoundException("No package found for authority: " + uri);
+ }
+ }
+ Listandroid.resource://package_name/id_number
+ * package_name
is your package name as listed in your AndroidManifest.xml.
+ * For example com.example.myapp
+ * id_number
is the int form of the ID.
+ * The easiest way to construct this form is
+ * Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");
+ * android.resource://package_name/type/name
+ * package_name
is your package name as listed in your AndroidManifest.xml.
+ * For example com.example.myapp
+ * type
is the string form of the resource type. For example, raw
+ * or drawable
.
+ * name
is the string form of the resource name. That is, whatever the file
+ * name was in your res directory, without the type extension.
+ * The easiest way to construct this form is
+ * Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");
+ *
+ *
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
@@ -1116,22 +1141,48 @@ public class Intent implements Parcelable {
* Broadcast Action: An existing application package has been removed from
* the device. The data contains the name of the package. The package
* that is being installed does not receive this Intent.
+ *
+ *
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
/**
* Broadcast Action: An existing application package has been changed (e.g. a component has been
* enabled or disabled. The data contains the name of the package.
+ *
+ *
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
/**
- * Broadcast Action: The user has restarted a package, all runtime state
+ * Broadcast Action: The user has restarted a package, and all of its
+ * processes have been killed. All runtime state
* associated with it (processes, alarms, notifications, etc) should
- * be remove. The data contains the name of the package.
+ * be removed. The data contains the name of the package.
+ *
+ *
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
+ /**
+ * Broadcast Action: The user has cleared the data of a package. This should
+ * be preceded by {@link #ACTION_PACKAGE_RESTARTED}, after which all of
+ * its persistent data is erased and this broadcast sent. The data contains
+ * the name of the package.
+ *
+ *
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
/**
* Broadcast Action: A user ID has been removed from the system. The user
* ID number is stored in the extra data under {@link #EXTRA_UID}.
@@ -1248,7 +1299,6 @@ public class Intent implements Parcelable {
/**
* Broadcast Action: External media is present, and being disk-checked
* The path to the mount point for the checking media is contained in the Intent.mData field.
- * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
@@ -1256,7 +1306,6 @@ public class Intent implements Parcelable {
/**
* Broadcast Action: External media is present, but is using an incompatible fs (or is blank)
* The path to the mount point for the checking media is contained in the Intent.mData field.
- * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MEDIA_NOFS = "android.intent.action.MEDIA_NOFS";
@@ -1524,16 +1573,17 @@ public class Intent implements Parcelable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_TAB = "android.intent.category.TAB";
/**
- * This activity can be embedded inside of another activity that is hosting
- * gadgets.
+ * Should be displayed in the top-level launcher.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
- public static final String CATEGORY_GADGET = "android.intent.category.GADGET";
+ public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER";
/**
- * Should be displayed in the top-level launcher.
+ * Provides information about the package it is in; typically used if
+ * a package does not contain a {@link #CATEGORY_LAUNCHER} to provide
+ * a front-door to the user without having to be shown in the all apps list.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
- public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER";
+ public static final String CATEGORY_INFO = "android.intent.category.INFO";
/**
* This is the home activity, that is the first activity that is displayed
* when the device boots.
@@ -1552,9 +1602,6 @@ public class Intent implements Parcelable {
public static final String CATEGORY_DEVELOPMENT_PREFERENCE = "android.intent.category.DEVELOPMENT_PREFERENCE";
/**
* Capable of running inside a parent activity container.
- *
- * Using System Resources
diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java
index 9115225c4415ea41ffbc21643d1b17ba7f712008..dcc746331b0b89a7ea4aba3882c66ef929c455c5 100755
--- a/core/java/android/content/pm/ConfigurationInfo.java
+++ b/core/java/android/content/pm/ConfigurationInfo.java
@@ -16,7 +16,6 @@
package android.content.pm;
-import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -60,12 +59,12 @@ public class ConfigurationInfo implements Parcelable {
/**
* Value for {@link #reqInputFeatures}: if set, indicates that the application
- * requires a hard keyboard
+ * requires a five way navigation device
*/
public static final int INPUT_FEATURE_FIVE_WAY_NAV = 0x00000002;
/**
- * Flags associated with the application. Any combination of
+ * Flags associated with the input features. Any combination of
* {@link #INPUT_FEATURE_HARD_KEYBOARD},
* {@link #INPUT_FEATURE_FIVE_WAY_NAV}
*/
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ea86188d2bf9b021dec895f06ea59822124af8a0..d3f6f3c564c89e68ef0d194c62e7f479565540f6 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -255,8 +255,15 @@ interface IPackageManager {
* retrieval of information is complete.
*/
void getPackageSizeInfo(in String packageName, IPackageStatsObserver observer);
+
+ /**
+ * Get a list of shared libraries that are available on the
+ * system.
+ */
+ String[] getSystemSharedLibraryNames();
void enterSafeMode();
+ boolean isSafeMode();
void systemReady();
boolean hasSystemUidErrors();
}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 994afc8381ff1effccadaf2f57c251a05152ca56..d9326f211b5ba39d3a454d96f8c401bae07280df 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -28,6 +28,20 @@ public class PackageInfo implements Parcelable {
*/
public String versionName;
+ /**
+ * The shared user ID name of this package, as specified by the <manifest>
+ * tag's {@link android.R.styleable#AndroidManifest_sharedUserId sharedUserId}
+ * attribute.
+ */
+ public String sharedUserId;
+
+ /**
+ * The shared user ID label of this package, as specified by the <manifest>
+ * tag's {@link android.R.styleable#AndroidManifest_sharedUserLabel sharedUserLabel}
+ * attribute.
+ */
+ public int sharedUserLabel;
+
/**
* Information collected from the <application> tag, or null if
* there was none.
@@ -130,6 +144,8 @@ public class PackageInfo implements Parcelable {
dest.writeString(packageName);
dest.writeInt(versionCode);
dest.writeString(versionName);
+ dest.writeString(sharedUserId);
+ dest.writeInt(sharedUserLabel);
if (applicationInfo != null) {
dest.writeInt(1);
applicationInfo.writeToParcel(dest, parcelableFlags);
@@ -163,6 +179,8 @@ public class PackageInfo implements Parcelable {
packageName = source.readString();
versionCode = source.readInt();
versionName = source.readString();
+ sharedUserId = source.readString();
+ sharedUserLabel = source.readInt();
int hasApp = source.readInt();
if (hasApp != 0) {
applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4b902e9fe944f121c84e905125c84d64bf06c41c..7287d9cd58e9306732c74de2bebfa01f7540de97 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -479,6 +479,26 @@ public abstract class PackageManager {
public abstract PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException;
+ /**
+ * Return a "good" intent to launch a front-door activity in a package,
+ * for use for example to implement an "open" button when browsing through
+ * packages. The current implementation will look first for a main
+ * activity in the category {@link Intent#CATEGORY_INFO}, next for a
+ * main activity in the category {@link Intent#CATEGORY_LAUNCHER}, or return
+ * null if neither are found.
+ *
+ *
- *
- * TODO: Add constants for these.
- * TODO: Where does this go?
- */
- public static final String GADGET_PICK_ACTION = "android.gadget.action.PICK_GADGET";
-
- public static final String EXTRA_GADGET_ID = "gadgetId";
-
- /**
- * Sent when it is time to update your gadget.
- */
- public static final String GADGET_UPDATE_ACTION = "android.gadget.action.GADGET_UPDATE";
-
- /**
- * Sent when the gadget is added to a host for the first time. TODO: Maybe we don't want this.
- */
- public static final String GADGET_ENABLE_ACTION = "android.gadget.action.GADGET_ENABLE";
-
- /**
- * Sent when the gadget is removed from the last host. TODO: Maybe we don't want this.
- */
- public static final String GADGET_DISABLE_ACTION = "android.gadget.action.GADGET_DISABLE";
-
- /**
- * Field for the manifest meta-data tag.
- */
- public static final String GADGET_PROVIDER_META_DATA = "android.gadget.provider";
-
- static WeakHashMap
+ *
+ *
*
* UrlQuerySanitizer sanitizer = new UrlQuerySanitizer();
@@ -32,7 +32,7 @@ import java.util.StringTokenizer;
* String name = sanitizer.getValue("name"));
* // name now contains "Joe_User"
*
- *
+ *
* Register ValueSanitizers to customize the way individual
* parameters are sanitized:
*
@@ -46,7 +46,7 @@ import java.util.StringTokenizer;
* unregistered parameter sanitizer does not allow any special characters,
* and ' ' is a special character.)
*
- *
+ *
* There are several ways to create ValueSanitizers. In order of increasing
* sophistication:
*
@@ -56,7 +56,7 @@ import java.util.StringTokenizer;
*
- *
+ *
*/
public class UrlQuerySanitizer {
@@ -84,7 +84,7 @@ public class UrlQuerySanitizer {
*/
public String mValue;
}
-
+
final private HashMap
*
@@ -867,7 +867,7 @@ public class UrlQuerySanitizer {
}
return stringBuilder.toString();
}
-
+
/**
* Test if a character is a hexidecimal digit. Both upper case and lower
* case hex digits are allowed.
@@ -877,7 +877,7 @@ public class UrlQuerySanitizer {
protected boolean isHexDigit(char c) {
return decodeHexDigit(c) >= 0;
}
-
+
/**
* Convert a character that represents a hexidecimal digit into an integer.
* If the character is not a hexidecimal digit, then -1 is returned.
@@ -885,7 +885,7 @@ public class UrlQuerySanitizer {
* @param c the hexidecimal digit.
* @return the integer value of the hexidecimal digit.
*/
-
+
protected int decodeHexDigit(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
@@ -900,7 +900,7 @@ public class UrlQuerySanitizer {
return -1;
}
}
-
+
/**
* Clear the existing entries. Called to get ready to parse a new
* query string.
diff --git a/core/java/android/net/http/AndroidHttpClient.java b/core/java/android/net/http/AndroidHttpClient.java
index 01442aecaac13c7907d98352b259886bf4e6e3d0..c2013d5bfcd4d97934281973acdcc17df51cc875 100644
--- a/core/java/android/net/http/AndroidHttpClient.java
+++ b/core/java/android/net/http/AndroidHttpClient.java
@@ -26,7 +26,6 @@ import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.ClientProtocolException;
@@ -48,6 +47,8 @@ import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.BasicHttpContext;
+import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
+import org.apache.harmony.xnet.provider.jsse.SSLContextImpl;
import java.io.IOException;
import java.io.InputStream;
@@ -56,12 +57,13 @@ import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.net.URI;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.security.KeyManagementException;
import android.util.Log;
import android.content.ContentResolver;
import android.provider.Settings;
import android.text.TextUtils;
+import android.os.SystemProperties;
/**
* Subclass of the Apache {@link DefaultHttpClient} that is configured with
@@ -100,10 +102,13 @@ public final class AndroidHttpClient implements HttpClient {
/**
* Create a new HttpClient with reasonable defaults (which you can update).
+ *
* @param userAgent to report in your HTTP requests.
+ * @param sessionCache persistent session cache
* @return AndroidHttpClient for you to use for all your requests.
*/
- public static AndroidHttpClient newInstance(String userAgent) {
+ public static AndroidHttpClient newInstance(String userAgent,
+ SSLClientSessionCache sessionCache) {
HttpParams params = new BasicHttpParams();
// Turn off stale checking. Our connections break all the time anyway,
@@ -125,7 +130,8 @@ public final class AndroidHttpClient implements HttpClient {
schemeRegistry.register(new Scheme("http",
PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https",
- SSLSocketFactory.getSocketFactory(), 443));
+ socketFactoryWithCache(sessionCache), 443));
+
ClientConnectionManager manager =
new ThreadSafeClientConnManager(params, schemeRegistry);
@@ -134,6 +140,41 @@ public final class AndroidHttpClient implements HttpClient {
return new AndroidHttpClient(manager, params);
}
+ /**
+ * Returns a socket factory backed by the given persistent session cache.
+ *
+ * @param sessionCache to retrieve sessions from, null for no cache
+ */
+ private static SSLSocketFactory socketFactoryWithCache(
+ SSLClientSessionCache sessionCache) {
+ if (sessionCache == null) {
+ // Use the default factory which doesn't support persistent
+ // caching.
+ return SSLSocketFactory.getSocketFactory();
+ }
+
+ // Create a new SSL context backed by the cache.
+ // TODO: Keep a weak *identity* hash map of caches to engines. In the
+ // mean time, if we have two engines for the same cache, they'll still
+ // share sessions but will have to do so through the persistent cache.
+ SSLContextImpl sslContext = new SSLContextImpl();
+ try {
+ sslContext.engineInit(null, null, null, sessionCache, null);
+ } catch (KeyManagementException e) {
+ throw new AssertionError(e);
+ }
+ return new SSLSocketFactory(sslContext.engineGetSocketFactory());
+ }
+
+ /**
+ * Create a new HttpClient with reasonable defaults (which you can update).
+ * @param userAgent to report in your HTTP requests.
+ * @return AndroidHttpClient for you to use for all your requests.
+ */
+ public static AndroidHttpClient newInstance(String userAgent) {
+ return newInstance(userAgent, null /* session cache */);
+ }
+
private final HttpClient delegate;
private RuntimeException mLeakedException = new IllegalStateException(
@@ -346,6 +387,15 @@ public final class AndroidHttpClient implements HttpClient {
return Log.isLoggable(tag, level);
}
+ /**
+ * Returns true if auth logging is turned on for this configuration. Can only be set on
+ * insecure devices.
+ */
+ private boolean isAuthLoggable() {
+ String secure = SystemProperties.get("ro.secure");
+ return "0".equals(secure) && Log.isLoggable(tag + "-auth", level);
+ }
+
/**
* Prints a message using this configuration.
*/
@@ -392,7 +442,8 @@ public final class AndroidHttpClient implements HttpClient {
if (configuration != null
&& configuration.isLoggable()
&& request instanceof HttpUriRequest) {
- configuration.println(toCurl((HttpUriRequest) request));
+ configuration.println(toCurl((HttpUriRequest) request,
+ configuration.isAuthLoggable()));
}
}
}
@@ -400,12 +451,17 @@ public final class AndroidHttpClient implements HttpClient {
/**
* Generates a cURL command equivalent to the given request.
*/
- private static String toCurl(HttpUriRequest request) throws IOException {
+ private static String toCurl(HttpUriRequest request, boolean logAuthToken) throws IOException {
StringBuilder builder = new StringBuilder();
builder.append("curl ");
for (Header header: request.getAllHeaders()) {
+ if (!logAuthToken
+ && (header.getName().equals("Authorization") ||
+ header.getName().equals("Cookie"))) {
+ continue;
+ }
builder.append("--header \"");
builder.append(header.toString().trim());
builder.append("\" ");
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index b7f7368ae24ebad039c4a46c9f2a6bdaad9be739..0edbe5b7e1a187f93ebfdc6ec0641497a612404e 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -16,8 +16,6 @@
package android.net.http;
-import android.os.SystemClock;
-
import java.io.IOException;
import java.security.cert.Certificate;
@@ -28,23 +26,13 @@ import java.security.cert.X509Certificate;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Enumeration;
-
-import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
-import org.apache.http.HttpHost;
-
-import org.bouncycastle.asn1.x509.X509Name;
-
/**
* Class responsible for all server certificate validation functionality
*
@@ -52,9 +40,6 @@ import org.bouncycastle.asn1.x509.X509Name;
*/
class CertificateChainValidator {
- private static long sTotal = 0;
- private static long sTotalReused = 0;
-
/**
* The singleton instance of the certificate chain validator
*/
@@ -110,91 +95,42 @@ class CertificateChainValidator {
* @return An SSL error object if there is an error and null otherwise
*/
public SslError doHandshakeAndValidateServerCertificates(
- HttpsConnection connection, SSLSocket sslSocket, String domain)
- throws SSLHandshakeException, IOException {
-
- ++sTotal;
-
- SSLContext sslContext = HttpsConnection.getContext();
- if (sslContext == null) {
- closeSocketThrowException(sslSocket, "SSL context is null");
- }
-
+ HttpsConnection connection, SSLSocket sslSocket, String domain)
+ throws IOException {
X509Certificate[] serverCertificates = null;
- long sessionBeforeHandshakeLastAccessedTime = 0;
- byte[] sessionBeforeHandshakeId = null;
-
- SSLSession sessionAfterHandshake = null;
-
- synchronized(sslContext) {
- // get SSL session before the handshake
- SSLSession sessionBeforeHandshake =
- getSSLSession(sslContext, connection.getHost());
- if (sessionBeforeHandshake != null) {
- sessionBeforeHandshakeLastAccessedTime =
- sessionBeforeHandshake.getLastAccessedTime();
+ // start handshake, close the socket if we fail
+ try {
+ sslSocket.setUseClientMode(true);
+ sslSocket.startHandshake();
+ } catch (IOException e) {
+ closeSocketThrowException(
+ sslSocket, e.getMessage(),
+ "failed to perform SSL handshake");
+ }
- sessionBeforeHandshakeId =
- sessionBeforeHandshake.getId();
- }
+ // retrieve the chain of the server peer certificates
+ Certificate[] peerCertificates =
+ sslSocket.getSession().getPeerCertificates();
- // start handshake, close the socket if we fail
- try {
- sslSocket.setUseClientMode(true);
- sslSocket.startHandshake();
- } catch (IOException e) {
- closeSocketThrowException(
- sslSocket, e.getMessage(),
- "failed to perform SSL handshake");
+ if (peerCertificates == null || peerCertificates.length <= 0) {
+ closeSocketThrowException(
+ sslSocket, "failed to retrieve peer certificates");
+ } else {
+ serverCertificates =
+ new X509Certificate[peerCertificates.length];
+ for (int i = 0; i < peerCertificates.length; ++i) {
+ serverCertificates[i] =
+ (X509Certificate)(peerCertificates[i]);
}
- // retrieve the chain of the server peer certificates
- Certificate[] peerCertificates =
- sslSocket.getSession().getPeerCertificates();
-
- if (peerCertificates == null || peerCertificates.length <= 0) {
- closeSocketThrowException(
- sslSocket, "failed to retrieve peer certificates");
- } else {
- serverCertificates =
- new X509Certificate[peerCertificates.length];
- for (int i = 0; i < peerCertificates.length; ++i) {
- serverCertificates[i] =
- (X509Certificate)(peerCertificates[i]);
- }
-
- // update the SSL certificate associated with the connection
- if (connection != null) {
- if (serverCertificates[0] != null) {
- connection.setCertificate(
- new SslCertificate(serverCertificates[0]));
- }
+ // update the SSL certificate associated with the connection
+ if (connection != null) {
+ if (serverCertificates[0] != null) {
+ connection.setCertificate(
+ new SslCertificate(serverCertificates[0]));
}
}
-
- // get SSL session after the handshake
- sessionAfterHandshake =
- getSSLSession(sslContext, connection.getHost());
- }
-
- if (sessionBeforeHandshakeLastAccessedTime != 0 &&
- sessionAfterHandshake != null &&
- Arrays.equals(
- sessionBeforeHandshakeId, sessionAfterHandshake.getId()) &&
- sessionBeforeHandshakeLastAccessedTime <
- sessionAfterHandshake.getLastAccessedTime()) {
-
- if (HttpLog.LOGV) {
- HttpLog.v("SSL session was reused: total reused: "
- + sTotalReused
- + " out of total of: " + sTotal);
-
- ++sTotalReused;
- }
-
- // no errors!!!
- return null;
}
// check if the first certificate in the chain is for this site
@@ -216,7 +152,6 @@ class CertificateChainValidator {
}
}
- //
// first, we validate the chain using the standard validation
// solution; if we do not find any errors, we are done; if we
// fail the standard validation, we re-validate again below,
@@ -393,14 +328,14 @@ class CertificateChainValidator {
}
private void closeSocketThrowException(
- SSLSocket socket, String errorMessage, String defaultErrorMessage)
- throws SSLHandshakeException, IOException {
+ SSLSocket socket, String errorMessage, String defaultErrorMessage)
+ throws IOException {
closeSocketThrowException(
socket, errorMessage != null ? errorMessage : defaultErrorMessage);
}
- private void closeSocketThrowException(SSLSocket socket, String errorMessage)
- throws SSLHandshakeException, IOException {
+ private void closeSocketThrowException(SSLSocket socket,
+ String errorMessage) throws IOException {
if (HttpLog.LOGV) {
HttpLog.v("validation error: " + errorMessage);
}
@@ -416,29 +351,4 @@ class CertificateChainValidator {
throw new SSLHandshakeException(errorMessage);
}
-
- /**
- * @param sslContext The SSL context shared accross all the SSL sessions
- * @param host The host associated with the session
- * @return A suitable SSL session from the SSL context
- */
- private SSLSession getSSLSession(SSLContext sslContext, HttpHost host) {
- if (sslContext != null && host != null) {
- Enumeration en = sslContext.getClientSessionContext().getIds();
- while (en.hasMoreElements()) {
- byte[] id = (byte[]) en.nextElement();
- if (id != null) {
- SSLSession session =
- sslContext.getClientSessionContext().getSession(id);
- if (session.isValid() &&
- host.getHostName().equals(session.getPeerHost()) &&
- host.getPort() == session.getPeerPort()) {
- return session;
- }
- }
- }
- }
-
- return null;
- }
}
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index 65e6117d2de0e638a53873e5880eb8af3d4ae9c4..c4ee5b0da0e2bfe43082616c705cff3d0dbdc072 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -55,7 +55,7 @@ public class RequestHandle {
private final static String AUTHORIZATION_HEADER = "Authorization";
private final static String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
- private final static int MAX_REDIRECT_COUNT = 16;
+ public final static int MAX_REDIRECT_COUNT = 16;
/**
* Creates a new request session.
@@ -106,6 +106,14 @@ public class RequestHandle {
return mRedirectCount >= MAX_REDIRECT_COUNT;
}
+ public int getRedirectCount() {
+ return mRedirectCount;
+ }
+
+ public void setRedirectCount(int count) {
+ mRedirectCount = count;
+ }
+
/**
* Create and queue a redirect request.
*
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index ed7c3663ce285ccbb48e9a8525d18254bb605e1c..7590bfef4535559615ba810bdd9f9e28a9f26640 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1,33 +1,44 @@
package android.os;
-import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Formatter;
import java.util.Map;
+import android.util.Log;
+import android.util.Printer;
import android.util.SparseArray;
/**
* A class providing access to battery usage statistics, including information on
* wakelocks, processes, packages, and services. All times are represented in microseconds
* except where indicated otherwise.
+ * @hide
*/
-public abstract class BatteryStats {
+public abstract class BatteryStats implements Parcelable {
+ private static final boolean LOCAL_LOGV = false;
+
/**
- * A constant indicating a partial wake lock.
+ * A constant indicating a partial wake lock timer.
*/
public static final int WAKE_TYPE_PARTIAL = 0;
/**
- * A constant indicating a full wake lock.
+ * A constant indicating a full wake lock timer.
*/
public static final int WAKE_TYPE_FULL = 1;
/**
- * A constant indicating a window wake lock.
+ * A constant indicating a window wake lock timer.
*/
public static final int WAKE_TYPE_WINDOW = 2;
+
+ /**
+ * A constant indicating a sensor timer.
+ *
+ * {@hide}
+ */
+ public static final int SENSOR = 3;
/**
* Include all of the data in the stats, including previously saved data.
@@ -48,6 +59,22 @@ public abstract class BatteryStats {
* Include only the run since the last time the device was unplugged in the stats.
*/
public static final int STATS_UNPLUGGED = 3;
+
+ /**
+ * Bump the version on this if the checkin format changes.
+ */
+ private static final int BATTERY_STATS_CHECKIN_VERSION = 1;
+
+ // TODO: Update this list if you add/change any stats above.
+ private static final String[] STAT_NAMES = { "total", "last", "current", "unplugged" };
+
+ private static final String APK_DATA = "apk";
+ private static final String PROCESS_DATA = "process";
+ private static final String SENSOR_DATA = "sensor";
+ private static final String WAKELOCK_DATA = "wakelock";
+ private static final String NETWORK_DATA = "network";
+ private static final String BATTERY_DATA = "battery";
+ private static final String MISC_DATA = "misc";
private final StringBuilder mFormatBuilder = new StringBuilder(8);
private final Formatter mFormatter = new Formatter(mFormatBuilder);
@@ -69,11 +96,16 @@ public abstract class BatteryStats {
* Returns the total time in microseconds associated with this Timer for the
* selected type of statistics.
*
- * @param now system uptime time in microseconds
+ * @param batteryRealtime system realtime on battery in microseconds
* @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
* @return a time in microseconds
*/
- public abstract long getTotalTime(long now, int which);
+ public abstract long getTotalTime(long batteryRealtime, int which);
+
+ /**
+ * Temporary for debugging.
+ */
+ public abstract void logState();
}
/**
@@ -115,8 +147,28 @@ public abstract class BatteryStats {
* @return a Map from Strings to Uid.Pkg objects.
*/
public abstract Map