diff --git a/Android.mk b/Android.mk index e10ec8007cf27..58b149ea5ac1e 100644 --- a/Android.mk +++ b/Android.mk @@ -73,10 +73,8 @@ LOCAL_SRC_FILES += \ core/java/android/app/IActivityPendingResult.aidl \ core/java/android/app/IActivityWatcher.aidl \ core/java/android/app/IAlarmManager.aidl \ - core/java/android/app/IBackupAgent.aidl \ + core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ - core/java/android/app/IIntentReceiver.aidl \ - core/java/android/app/IIntentSender.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/ISearchManager.aidl \ core/java/android/app/ISearchManagerCallback.aidl \ @@ -92,6 +90,8 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothDevice.aidl \ core/java/android/bluetooth/IBluetoothHeadset.aidl \ core/java/android/content/IContentService.aidl \ + core/java/android/content/IIntentReceiver.aidl \ + core/java/android/content/IIntentSender.aidl \ core/java/android/content/ISyncAdapter.aidl \ core/java/android/content/ISyncContext.aidl \ core/java/android/content/ISyncStatusObserver.aidl \ @@ -203,6 +203,7 @@ aidl_files := \ frameworks/base/core/java/android/app/PendingIntent.aidl \ frameworks/base/core/java/android/content/ComponentName.aidl \ frameworks/base/core/java/android/content/Intent.aidl \ + frameworks/base/core/java/android/content/IntentSender.aidl \ frameworks/base/core/java/android/content/SyncStats.aidl \ frameworks/base/core/java/android/content/res/Configuration.aidl \ frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \ diff --git a/api/current.xml b/api/current.xml index c436b2d5e8e66..479db244284b6 100644 --- a/api/current.xml +++ b/api/current.xml @@ -25161,6 +25161,17 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + By giving a IntentSender to another application, + * you are granting it the right to perform the operation you have specified + * as if the other application was yourself (with the same permissions and + * identity). As such, you should be careful about how you build the IntentSender: + * often, for example, the base Intent you supply will have the component + * name explicitly set to one of your own components, to ensure it is ultimately + * sent there and nowhere else. + * + *

A IntentSender itself is simply a reference to a token maintained by + * the system describing the original data used to retrieve it. This means + * that, even if its owning application's process is killed, the + * IntentSender itself will remain usable from other processes that + * have been given it. If the creating application later re-retrieves the + * same kind of IntentSender (same operation, same Intent action, data, + * categories, and components, and same flags), it will receive a IntentSender + * representing the same token if that is still valid. + * + */ +public class IntentSender implements Parcelable { + private final IIntentSender mTarget; + + /** + * Exception thrown when trying to send through a PendingIntent that + * has been canceled or is otherwise no longer able to execute the request. + */ + public static class SendIntentException extends AndroidException { + public SendIntentException() { + } + + public SendIntentException(String name) { + super(name); + } + + public SendIntentException(Exception cause) { + super(cause); + } + } + + /** + * Callback interface for discovering when a send operation has + * completed. Primarily for use with a IntentSender that is + * performing a broadcast, this provides the same information as + * calling {@link Context#sendOrderedBroadcast(Intent, String, + * android.content.BroadcastReceiver, Handler, int, String, Bundle) + * Context.sendBroadcast()} with a final BroadcastReceiver. + */ + public interface OnFinished { + /** + * Called when a send operation as completed. + * + * @param IntentSender The IntentSender this operation was sent through. + * @param intent The original Intent that was sent. + * @param resultCode The final result code determined by the send. + * @param resultData The final data collected by a broadcast. + * @param resultExtras The final extras collected by a broadcast. + */ + void onSendFinished(IntentSender IntentSender, Intent intent, + int resultCode, String resultData, Bundle resultExtras); + } + + private static class FinishedDispatcher extends IIntentReceiver.Stub + implements Runnable { + private final IntentSender mIntentSender; + private final OnFinished mWho; + private final Handler mHandler; + private Intent mIntent; + private int mResultCode; + private String mResultData; + private Bundle mResultExtras; + FinishedDispatcher(IntentSender pi, OnFinished who, Handler handler) { + mIntentSender = pi; + mWho = who; + mHandler = handler; + } + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean serialized) { + mIntent = intent; + mResultCode = resultCode; + mResultData = data; + mResultExtras = extras; + if (mHandler == null) { + run(); + } else { + mHandler.post(this); + } + } + public void run() { + mWho.onSendFinished(mIntentSender, mIntent, mResultCode, + mResultData, mResultExtras); + } + } + + /** + * Perform the operation associated with this IntentSender, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * @param context The Context of the caller. This may be null if + * intent is also null. + * @param code Result code to supply back to the IntentSender's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * + * + * @throws SendIntentException Throws CanceledIntentException if the IntentSender + * is no longer allowing more intents to be sent through it. + */ + public void sendIntent(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler) throws SendIntentException { + try { + String resolvedType = intent != null ? + intent.resolveTypeIfNeeded(context.getContentResolver()) + : null; + int res = mTarget.send(code, intent, resolvedType, + onFinished != null + ? new FinishedDispatcher(this, onFinished, handler) + : null); + if (res < 0) { + throw new SendIntentException(); + } + } catch (RemoteException e) { + throw new SendIntentException(); + } + } + + /** + * Comparison operator on two IntentSender objects, such that true + * is returned then they both represent the same operation from the + * same package. + */ + @Override + public boolean equals(Object otherObj) { + if (otherObj instanceof IntentSender) { + return mTarget.asBinder().equals(((IntentSender)otherObj) + .mTarget.asBinder()); + } + return false; + } + + @Override + public int hashCode() { + return mTarget.asBinder().hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("IntentSender{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(": "); + sb.append(mTarget != null ? mTarget.asBinder() : null); + sb.append('}'); + return sb.toString(); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeStrongBinder(mTarget.asBinder()); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public IntentSender createFromParcel(Parcel in) { + IBinder target = in.readStrongBinder(); + return target != null ? new IntentSender(target) : null; + } + + public IntentSender[] newArray(int size) { + return new IntentSender[size]; + } + }; + + /** + * Convenience function for writing either a IntentSender or null pointer to + * a Parcel. You must use this with {@link #readIntentSenderOrNullFromParcel} + * for later reading it. + * + * @param sender The IntentSender to write, or null. + * @param out Where to write the IntentSender. + */ + public static void writeIntentSenderOrNullToParcel(IntentSender sender, + Parcel out) { + out.writeStrongBinder(sender != null ? sender.mTarget.asBinder() + : null); + } + + /** + * Convenience function for reading either a Messenger or null pointer from + * a Parcel. You must have previously written the Messenger with + * {@link #writeIntentSenderOrNullToParcel}. + * + * @param in The Parcel containing the written Messenger. + * + * @return Returns the Messenger read from the Parcel, or null if null had + * been written. + */ + public static IntentSender readIntentSenderOrNullFromParcel(Parcel in) { + IBinder b = in.readStrongBinder(); + return b != null ? new IntentSender(b) : null; + } + + protected IntentSender(IIntentSender target) { + mTarget = target; + } + + protected IntentSender(IBinder target) { + mTarget = IIntentSender.Stub.asInterface(target); + } +} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 9eca4a56a3aa7..607a1f57ef550 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -35,6 +35,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.net.Uri; import android.app.PendingIntent; +import android.content.IntentSender; /** * See {@link PackageManager} for documentation on most of the APIs @@ -244,6 +245,30 @@ interface IPackageManager { */ void freeStorage(in long freeStorageSize, in PendingIntent opFinishedIntent); + + /** + * Free storage by deleting LRU sorted list of cache files across + * all applications. If the currently available free storage + * on the device is greater than or equal to the requested + * free storage, no cache files are cleared. If the currently + * available storage on the device is less than the requested + * free storage, some or all of the cache files across + * all applications are deleted (based on last accessed time) + * to increase the free storage space on the device to + * the requested value. There is no guarantee that clearing all + * the cache files from all applications will clear up + * enough storage to achieve the desired value. + * @param freeStorageSize The number of bytes of storage to be + * freed by the system. Say if freeStorageSize is XX, + * and the current free storage is YY, + * if XX is less than YY, just return. if not free XX-YY number + * of bytes if possible. + * @param pi IntentSender call back used to + * notify when the operation is completed.May be null + * to indicate that no call back is desired. + */ + void freeStorageWithIntent(in long freeStorageSize, + in IntentSender pi); /** * Delete all the cache files in an applications cache directory diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 88eccf7884913..5f8dc53401417 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -22,6 +22,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; @@ -1520,7 +1521,7 @@ public abstract class PackageManager { * @hide */ public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer); - + /** * Free storage by deleting LRU sorted list of cache files across * all applications. If the currently available free storage @@ -1541,10 +1542,37 @@ public abstract class PackageManager { * @param opFinishedIntent PendingIntent call back used to * notify when the operation is completed.May be null * to indicate that no call back is desired. + * + * @deprecated + * @hide + */ + @Deprecated + public abstract void freeStorage(long freeStorageSize, PendingIntent opFinishedIntent); + + /** + * Free storage by deleting LRU sorted list of cache files across + * all applications. If the currently available free storage + * on the device is greater than or equal to the requested + * free storage, no cache files are cleared. If the currently + * available storage on the device is less than the requested + * free storage, some or all of the cache files across + * all applications are deleted (based on last accessed time) + * to increase the free storage space on the device to + * the requested value. There is no guarantee that clearing all + * the cache files from all applications will clear up + * enough storage to achieve the desired value. + * @param freeStorageSize The number of bytes of storage to be + * freed by the system. Say if freeStorageSize is XX, + * and the current free storage is YY, + * if XX is less than YY, just return. if not free XX-YY number + * of bytes if possible. + * @param pi IntentSender call back used to + * notify when the operation is completed.May be null + * to indicate that no call back is desired. * * @hide */ - public abstract void freeStorage(long freeStorageSize, PendingIntent opFinishedIntent); + public abstract void freeStorageWithIntent(long freeStorageSize, IntentSender pi); /** * Retrieve the size information for a package. diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index d98738af8dc93..0630b884b8efe 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -34,6 +34,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.IntentSender.SendIntentException; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; @@ -955,6 +957,34 @@ class PackageManagerService extends IPackageManager.Stub { } }); } + + public void freeStorageWithIntent(final long freeStorageSize, final IntentSender pi) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CLEAR_APP_CACHE, null); + // Queue up an async operation since clearing cache may take a little while. + mHandler.post(new Runnable() { + public void run() { + mHandler.removeCallbacks(this); + int retCode = -1; + if (mInstaller != null) { + retCode = mInstaller.freeCache(freeStorageSize); + if (retCode < 0) { + Log.w(TAG, "Couldn't clear application caches"); + } + } + if(pi != null) { + try { + // Callback via pending intent + int code = (retCode >= 0) ? 1 : 0; + pi.sendIntent(null, code, null, + null, null); + } catch (SendIntentException e1) { + Log.i(TAG, "Failed to send pending intent"); + } + } + } + }); + } public ActivityInfo getActivityInfo(ComponentName component, int flags) { synchronized (mPackages) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c0d4496e00c03..ae867fb755376 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -34,8 +34,6 @@ import android.app.Dialog; import android.app.IActivityWatcher; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; -import android.app.IIntentReceiver; -import android.app.IIntentSender; import android.app.IServiceConnection; import android.app.IThumbnailReceiver; import android.app.Instrumentation; @@ -48,6 +46,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index 4057ae82d3749..da55049de7ada 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.app.IIntentReceiver; +import android.content.IIntentReceiver; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index 4381392822554..fa2a1004cda71 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -17,8 +17,8 @@ package com.android.server.am; import android.app.IActivityManager; -import android.app.IIntentSender; -import android.app.IIntentReceiver; +import android.content.IIntentSender; +import android.content.IIntentReceiver; import android.app.PendingIntent; import android.content.Intent; import android.os.Binder; diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java index 0facefc52d3a3..32c24c6cbf6fa 100644 --- a/services/java/com/android/server/am/ReceiverList.java +++ b/services/java/com/android/server/am/ReceiverList.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.app.IIntentReceiver; +import android.content.IIntentReceiver; import android.content.Intent; import android.os.Binder; import android.os.Bundle; diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java index f98a2519f7516..4ffc0ffeb430c 100644 --- a/test-runner/android/test/mock/MockPackageManager.java +++ b/test-runner/android/test/mock/MockPackageManager.java @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDeleteObserver; @@ -338,6 +339,15 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** + * @hide - to match hiding in superclass + */ + @Override + public void freeStorageWithIntent( + long idealStorageSize, IntentSender pi) { + throw new UnsupportedOperationException(); + } + /** * @hide - to match hiding in superclass */