diff options
author | Dianne Hackborn <hackbod@google.com> | 2015-06-16 17:18:23 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2015-06-17 11:41:45 -0700 |
commit | a750a63d639f6936af456df904fa6b9ba941885e (patch) | |
tree | 41cd79aec0dedbf162eab2749bd6dec45bb7509d | |
parent | f5820276635a2d3d2e3c694eddba7e6a02093a20 (diff) | |
download | frameworks_base-a750a63d639f6936af456df904fa6b9ba941885e.zip frameworks_base-a750a63d639f6936af456df904fa6b9ba941885e.tar.gz frameworks_base-a750a63d639f6936af456df904fa6b9ba941885e.tar.bz2 |
Fix issue #21814207 and issue #21814212 (alarm manager)
Issue #21814207: AlarmManager.setAndAllowWhileIdle should also allow wake locks.
Introduce a whole new infrastructure for providing options when
sending broadcasts, much like ActivityOptions. There is a single
option right now, asking the activity manager to apply a tempory
whitelist to each receiver of the broadcast.
Issue #21814212: Need to allow configuration of alarm manager parameters
The various alarm manager timing configurations are not modifiable
through settings, much like DeviceIdleController. Also did a few
tweaks in the existing DeviceIdleController impl.
Change-Id: Ifd01013185acc4de668617b1e46e78e30ebed041
26 files changed, 804 insertions, 241 deletions
diff --git a/api/current.txt b/api/current.txt index 3c1612d..ca8cb8c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3758,7 +3758,7 @@ package android.app { method public void requestUsageTimeReport(android.app.PendingIntent); method public android.os.Bundle toBundle(); method public void update(android.app.ActivityOptions); - field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.usage_time"; + field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time"; field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages"; } @@ -5194,6 +5194,7 @@ package android.app { method public void send(int, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException; method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException; method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String) throws android.app.PendingIntent.CanceledException; + method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String, android.os.Bundle) throws android.app.PendingIntent.CanceledException; method public static void writePendingIntentOrNullToParcel(android.app.PendingIntent, android.os.Parcel); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR; diff --git a/api/system-current.txt b/api/system-current.txt index 90d5ad3..fe8a21e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3851,7 +3851,7 @@ package android.app { method public void requestUsageTimeReport(android.app.PendingIntent); method public android.os.Bundle toBundle(); method public void update(android.app.ActivityOptions); - field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.usage_time"; + field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time"; field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages"; } @@ -4183,6 +4183,12 @@ package android.app { method public int getWidth(); } + public class BroadcastOptions { + method public static android.app.BroadcastOptions makeBasic(); + method public void setTemporaryAppWhitelistDuration(long); + method public android.os.Bundle toBundle(); + } + public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int); ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int); @@ -5289,6 +5295,7 @@ package android.app { method public void send(int, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException; method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException; method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String) throws android.app.PendingIntent.CanceledException; + method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String, android.os.Bundle) throws android.app.PendingIntent.CanceledException; method public static void writePendingIntentOrNullToParcel(android.app.PendingIntent, android.os.Parcel); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR; @@ -7963,10 +7970,12 @@ package android.content { method public abstract void revokeUriPermission(android.net.Uri, int); method public abstract void sendBroadcast(android.content.Intent); method public abstract void sendBroadcast(android.content.Intent, java.lang.String); + method public abstract void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle); method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String); method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String); method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); + method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public abstract deprecated void sendStickyBroadcast(android.content.Intent); method public abstract deprecated void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); @@ -8140,10 +8149,12 @@ package android.content { method public void revokeUriPermission(android.net.Uri, int); method public void sendBroadcast(android.content.Intent); method public void sendBroadcast(android.content.Intent, java.lang.String); + method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String); method public void sendOrderedBroadcast(android.content.Intent, java.lang.String); method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); + method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public deprecated void sendStickyBroadcast(android.content.Intent); method public deprecated void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); @@ -34017,10 +34028,12 @@ package android.test.mock { method public void revokeUriPermission(android.net.Uri, int); method public void sendBroadcast(android.content.Intent); method public void sendBroadcast(android.content.Intent, java.lang.String); + method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle); method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String); method public void sendOrderedBroadcast(android.content.Intent, java.lang.String); method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); + method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public void sendStickyBroadcast(android.content.Intent); method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle); diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index bf3b455..69ba27c 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -1002,7 +1002,7 @@ public class Am extends BaseCommand { IntentReceiver receiver = new IntentReceiver(); System.out.println("Broadcasting: " + intent); mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, mReceiverPermission, - android.app.AppOpsManager.OP_NONE, true, false, mUserId); + android.app.AppOpsManager.OP_NONE, null, true, false, mUserId); receiver.waitForFinish(); } @@ -1658,7 +1658,7 @@ public class Am extends BaseCommand { Intent intent = new Intent( "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE"); mAm.broadcastIntent(null, intent, null, null, 0, null, null, null, - android.app.AppOpsManager.OP_NONE, true, false, UserHandle.USER_ALL); + android.app.AppOpsManager.OP_NONE, null, true, false, UserHandle.USER_ALL); } private void runScreenCompat() throws Exception { diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 1faf41b..2be44bc 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1812,7 +1812,7 @@ public final class Pm { private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { @Override public int send(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver, String requiredPermission) { + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { try { mResult.offer(intent, 5, TimeUnit.SECONDS); } catch (InterruptedException e) { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 6ae21eb..680feae 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -108,7 +108,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM try { getDefault().broadcastIntent( null, intent, null, null, Activity.RESULT_OK, null, null, - null /*permission*/, appOp, false, true, userId); + null /*permission*/, appOp, null, false, true, userId); } catch (RemoteException ex) { } } @@ -458,12 +458,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM Bundle resultExtras = data.readBundle(); String perm = data.readString(); int appOp = data.readInt(); + Bundle options = data.readBundle(); boolean serialized = data.readInt() != 0; boolean sticky = data.readInt() != 0; int userId = data.readInt(); int res = broadcastIntent(app, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, perm, appOp, - serialized, sticky, userId); + options, serialized, sticky, userId); reply.writeNoException(); reply.writeInt(res); return true; @@ -2991,9 +2992,9 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } public int broadcastIntent(IApplicationThread caller, - Intent intent, String resolvedType, IIntentReceiver resultTo, + Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, - String requiredPermission, int appOp, boolean serialized, + String requiredPermission, int appOp, Bundle options, boolean serialized, boolean sticky, int userId) throws RemoteException { Parcel data = Parcel.obtain(); @@ -3008,6 +3009,7 @@ class ActivityManagerProxy implements IActivityManager data.writeBundle(map); data.writeString(requiredPermission); data.writeInt(appOp); + data.writeBundle(options); data.writeInt(serialized ? 1 : 0); data.writeInt(sticky ? 1 : 0); data.writeInt(userId); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 6fb997e..2406985 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -43,7 +43,7 @@ public class ActivityOptions { * A long in the extras delivered by {@link #requestUsageTimeReport} that contains * the total time (in ms) the user spent in the app flow. */ - public static final String EXTRA_USAGE_TIME_REPORT = "android.usage_time"; + public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time"; /** * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains @@ -56,67 +56,67 @@ public class ActivityOptions { * The package name that created the options. * @hide */ - public static final String KEY_PACKAGE_NAME = "android:packageName"; + public static final String KEY_PACKAGE_NAME = "android:activity.packageName"; /** * Type of animation that arguments specify. * @hide */ - public static final String KEY_ANIM_TYPE = "android:animType"; + public static final String KEY_ANIM_TYPE = "android:activity.animType"; /** * Custom enter animation resource ID. * @hide */ - public static final String KEY_ANIM_ENTER_RES_ID = "android:animEnterRes"; + public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes"; /** * Custom exit animation resource ID. * @hide */ - public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes"; + public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes"; /** * Custom in-place animation resource ID. * @hide */ - public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:animInPlaceRes"; + public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes"; /** * Bitmap for thumbnail animation. * @hide */ - public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail"; + public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail"; /** * Start X position of thumbnail animation. * @hide */ - public static final String KEY_ANIM_START_X = "android:animStartX"; + public static final String KEY_ANIM_START_X = "android:activity.animStartX"; /** * Start Y position of thumbnail animation. * @hide */ - public static final String KEY_ANIM_START_Y = "android:animStartY"; + public static final String KEY_ANIM_START_Y = "android:activity.animStartY"; /** * Initial width of the animation. * @hide */ - public static final String KEY_ANIM_WIDTH = "android:animWidth"; + public static final String KEY_ANIM_WIDTH = "android:activity.animWidth"; /** * Initial height of the animation. * @hide */ - public static final String KEY_ANIM_HEIGHT = "android:animHeight"; + public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight"; /** * Callback for when animation is started. * @hide */ - public static final String KEY_ANIM_START_LISTENER = "android:animStartListener"; + public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener"; /** * For Activity transitions, the calling Activity's TransitionListener used to @@ -124,15 +124,18 @@ public class ActivityOptions { * complete. */ private static final String KEY_TRANSITION_COMPLETE_LISTENER - = "android:transitionCompleteListener"; - - private static final String KEY_TRANSITION_IS_RETURNING = "android:transitionIsReturning"; - private static final String KEY_TRANSITION_SHARED_ELEMENTS = "android:sharedElementNames"; - private static final String KEY_RESULT_DATA = "android:resultData"; - private static final String KEY_RESULT_CODE = "android:resultCode"; - private static final String KEY_EXIT_COORDINATOR_INDEX = "android:exitCoordinatorIndex"; - - private static final String KEY_USAGE_TIME_REPORT = "android:usageTimeReport"; + = "android:activity.transitionCompleteListener"; + + private static final String KEY_TRANSITION_IS_RETURNING + = "android:activity.transitionIsReturning"; + private static final String KEY_TRANSITION_SHARED_ELEMENTS + = "android:activity.sharedElementNames"; + private static final String KEY_RESULT_DATA = "android:activity.resultData"; + private static final String KEY_RESULT_CODE = "android:activity.resultCode"; + private static final String KEY_EXIT_COORDINATOR_INDEX + = "android:activity.exitCoordinatorIndex"; + + private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport"; /** @hide */ public static final int ANIM_NONE = 0; diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java new file mode 100644 index 0000000..1f378da --- /dev/null +++ b/core/java/android/app/BroadcastOptions.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.annotation.SystemApi; +import android.os.Bundle; + +/** + * Helper class for building an options Bundle that can be used with + * {@link android.content.Context#sendBroadcast(android.content.Intent) + * Context.sendBroadcast(Intent)} and related methods. + * {@hide} + */ +@SystemApi +public class BroadcastOptions { + private long mTemporaryAppWhitelistDuration; + + /** + * How long to temporarily put an app on the power whitelist when executing this broadcast + * to it. + * @hide + */ + public static final String KEY_TEMPORARY_APP_WHITELIST_DURATION + = "android:broadcast.temporaryAppWhitelistDuration"; + + public static BroadcastOptions makeBasic() { + BroadcastOptions opts = new BroadcastOptions(); + return opts; + } + + private BroadcastOptions() { + } + + /** @hide */ + public BroadcastOptions(Bundle opts) { + mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION); + } + + /** + * Set a duration for which the system should temporary place an application on the + * power whitelist when this broadcast is being delivered to it. + * @param duration The duration in milliseconds; 0 means to not place on whitelist. + */ + public void setTemporaryAppWhitelistDuration(long duration) { + mTemporaryAppWhitelistDuration = duration; + } + + /** + * Return {@link #setTemporaryAppWhitelistDuration}. + * @hide + */ + public long getTemporaryAppWhitelistDuration() { + return mTemporaryAppWhitelistDuration; + } + + /** + * Returns the created options as a Bundle, which can be passed to + * {@link android.content.Context#sendBroadcast(android.content.Intent) + * Context.sendBroadcast(Intent)} and related methods. + * Note that the returned Bundle is still owned by the ActivityOptions + * object; you must not modify it, but can supply it to the sendBroadcast + * methods that take an options Bundle. + */ + public Bundle toBundle() { + Bundle b = new Bundle(); + if (mTemporaryAppWhitelistDuration > 0) { + b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration); + } + return b.isEmpty() ? null : b; + } +} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index be36af7..0420fb6 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -46,7 +46,6 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -676,8 +675,8 @@ class ContextImpl extends Context { + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( - getOuterContext(), mMainThread.getApplicationThread(), null, - (Activity)null, intent, -1, options); + getOuterContext(), mMainThread.getApplicationThread(), null, + (Activity) null, intent, -1, options); } /** @hide */ @@ -710,8 +709,8 @@ class ContextImpl extends Context { + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivitiesAsUser( - getOuterContext(), mMainThread.getApplicationThread(), null, - (Activity)null, intents, options, userHandle.getIdentifier()); + getOuterContext(), mMainThread.getApplicationThread(), null, + (Activity) null, intents, options, userHandle.getIdentifier()); } @Override @@ -724,8 +723,8 @@ class ContextImpl extends Context { + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivities( - getOuterContext(), mMainThread.getApplicationThread(), null, - (Activity)null, intents, options); + getOuterContext(), mMainThread.getApplicationThread(), null, + (Activity) null, intents, options); } @Override @@ -766,9 +765,9 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( - mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false, - getUserId()); + mMainThread.getApplicationThread(), intent, resolvedType, null, + Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, + getUserId()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } @@ -781,9 +780,24 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( - mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, - false, false, getUserId()); + mMainThread.getApplicationThread(), intent, resolvedType, null, + Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, + null, false, false, getUserId()); + } catch (RemoteException e) { + throw new RuntimeException("Failure from system", e); + } + } + + @Override + public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) { + warnIfCallingFromSystemProcess(); + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + try { + intent.prepareToLeaveProcess(); + ActivityManagerNative.getDefault().broadcastIntent( + mMainThread.getApplicationThread(), intent, resolvedType, null, + Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, + options, false, false, getUserId()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } @@ -796,25 +810,24 @@ class ContextImpl extends Context { try { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( - mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false, - getUserId()); + mMainThread.getApplicationThread(), intent, resolvedType, null, + Activity.RESULT_OK, null, null, receiverPermission, appOp, null, false, false, + getUserId()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } } @Override - public void sendOrderedBroadcast(Intent intent, - String receiverPermission) { + public void sendOrderedBroadcast(Intent intent, String receiverPermission) { warnIfCallingFromSystemProcess(); String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( - mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false, - getUserId()); + mMainThread.getApplicationThread(), intent, resolvedType, null, + Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, + null, true, false, getUserId()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } @@ -826,7 +839,16 @@ class ContextImpl extends Context { Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE, - resultReceiver, scheduler, initialCode, initialData, initialExtras); + resultReceiver, scheduler, initialCode, initialData, initialExtras, null); + } + + @Override + public void sendOrderedBroadcast(Intent intent, + String receiverPermission, Bundle options, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE, + resultReceiver, scheduler, initialCode, initialData, initialExtras, options); } @Override @@ -834,6 +856,14 @@ class ContextImpl extends Context { String receiverPermission, int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { + sendOrderedBroadcast(intent, receiverPermission, appOp, + resultReceiver, scheduler, initialCode, initialData, initialExtras, null); + } + + void sendOrderedBroadcast(Intent intent, + String receiverPermission, int appOp, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras, Bundle options) { warnIfCallingFromSystemProcess(); IIntentReceiver rd = null; if (resultReceiver != null) { @@ -858,7 +888,7 @@ class ContextImpl extends Context { ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermission, appOp, - true, false, getUserId()); + options, true, false, getUserId()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } @@ -871,7 +901,7 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, - AppOpsManager.OP_NONE, false, false, user.getIdentifier()); + AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } @@ -891,7 +921,7 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false, + Activity.RESULT_OK, null, null, receiverPermission, appOp, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -934,7 +964,7 @@ class ContextImpl extends Context { ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, receiverPermission, - appOp, true, false, user.getIdentifier()); + appOp, null, true, false, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } @@ -949,7 +979,7 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, + Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, getUserId()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -986,7 +1016,7 @@ class ContextImpl extends Context { ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, - AppOpsManager.OP_NONE, true, true, getUserId()); + AppOpsManager.OP_NONE, null, true, true, getUserId()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } @@ -1017,7 +1047,7 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, user.getIdentifier()); + Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } @@ -1052,7 +1082,7 @@ class ContextImpl extends Context { ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, rd, initialCode, initialData, initialExtras, null, - AppOpsManager.OP_NONE, true, true, user.getIdentifier()); + AppOpsManager.OP_NONE, null, true, true, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 9311e5e..e7f7e13 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -107,7 +107,7 @@ public interface IActivityManager extends IInterface { public int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, String requiredPermission, - int appOp, boolean serialized, boolean sticky, int userId) throws RemoteException; + int appOp, Bundle options, boolean serialized, boolean sticky, int userId) throws RemoteException; public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException; public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast, int flags) throws RemoteException; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 031854a..c42ba65 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -613,7 +613,7 @@ public final class PendingIntent implements Parcelable { * is no longer allowing more intents to be sent through it. */ public void send() throws CanceledException { - send(null, 0, null, null, null, null); + send(null, 0, null, null, null, null, null); } /** @@ -627,7 +627,7 @@ public final class PendingIntent implements Parcelable { * is no longer allowing more intents to be sent through it. */ public void send(int code) throws CanceledException { - send(null, code, null, null, null, null); + send(null, code, null, null, null, null, null); } /** @@ -646,9 +646,9 @@ public final class PendingIntent implements Parcelable { * @throws CanceledException Throws CanceledException if the PendingIntent * is no longer allowing more intents to be sent through it. */ - public void send(Context context, int code, Intent intent) + public void send(Context context, int code, @Nullable Intent intent) throws CanceledException { - send(context, code, intent, null, null, null); + send(context, code, intent, null, null, null, null); } /** @@ -667,9 +667,9 @@ public final class PendingIntent implements Parcelable { * @throws CanceledException Throws CanceledException if the PendingIntent * is no longer allowing more intents to be sent through it. */ - public void send(int code, OnFinished onFinished, Handler handler) + public void send(int code, @Nullable OnFinished onFinished, @Nullable Handler handler) throws CanceledException { - send(null, code, null, onFinished, handler, null); + send(null, code, null, onFinished, handler, null, null); } /** @@ -705,9 +705,9 @@ public final class PendingIntent implements Parcelable { * @throws CanceledException Throws CanceledException if the PendingIntent * is no longer allowing more intents to be sent through it. */ - public void send(Context context, int code, Intent intent, - OnFinished onFinished, Handler handler) throws CanceledException { - send(context, code, intent, onFinished, handler, null); + public void send(Context context, int code, @Nullable Intent intent, + @Nullable OnFinished onFinished, @Nullable Handler handler) throws CanceledException { + send(context, code, intent, onFinished, handler, null, null); } /** @@ -748,8 +748,56 @@ public final class PendingIntent implements Parcelable { * @throws CanceledException Throws CanceledException if the PendingIntent * is no longer allowing more intents to be sent through it. */ - public void send(Context context, int code, Intent intent, - OnFinished onFinished, Handler handler, String requiredPermission) + public void send(Context context, int code, @Nullable Intent intent, + @Nullable OnFinished onFinished, @Nullable Handler handler, + @Nullable String requiredPermission) + throws CanceledException { + send(context, code, intent, onFinished, handler, requiredPermission, null); + } + + /** + * Perform the operation associated with this PendingIntent, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * <p>For the intent parameter, a PendingIntent + * often has restrictions on which fields can be supplied here, based on + * how the PendingIntent was retrieved in {@link #getActivity}, + * {@link #getBroadcast}, or {@link #getService}. + * + * @param context The Context of the caller. This may be null if + * <var>intent</var> is also null. + * @param code Result code to supply back to the PendingIntent'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. + * If flag {@link #FLAG_IMMUTABLE} was set when this pending intent was + * created, this argument will be ignored. + * @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. + * @param requiredPermission Name of permission that a recipient of the PendingIntent + * is required to hold. This is only valid for broadcast intents, and + * corresponds to the permission argument in + * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}. + * If null, no permission is required. + * @param options Additional options the caller would like to provide to modify the sending + * behavior. May be built from an {@link ActivityOptions} to apply to an activity start. + * + * @see #send() + * @see #send(int) + * @see #send(Context, int, Intent) + * @see #send(int, android.app.PendingIntent.OnFinished, Handler) + * @see #send(Context, int, Intent, OnFinished, Handler) + * + * @throws CanceledException Throws CanceledException if the PendingIntent + * is no longer allowing more intents to be sent through it. + */ + public void send(Context context, int code, @Nullable Intent intent, + @Nullable OnFinished onFinished, @Nullable Handler handler, + @Nullable String requiredPermission, @Nullable Bundle options) throws CanceledException { try { String resolvedType = intent != null ? @@ -759,7 +807,7 @@ public final class PendingIntent implements Parcelable { onFinished != null ? new FinishedDispatcher(this, onFinished, handler) : null, - requiredPermission); + requiredPermission, options); if (res < 0) { throw new CanceledException(); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 83ce087..675515b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1517,6 +1517,38 @@ public abstract class Context { @Nullable String receiverPermission); /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing + * an optional required permission to be enforced. This + * call is asynchronous; it returns immediately, and you will continue + * executing while the receivers are run. No results are propagated from + * receivers and receivers can not abort the broadcast. If you want + * to allow receivers to propagate results or abort the broadcast, you must + * send an ordered broadcast using + * {@link #sendOrderedBroadcast(Intent, String)}. + * + * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission (optional) String naming a permission that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) + * @hide + */ + @SystemApi + public abstract void sendBroadcast(Intent intent, + @Nullable String receiverPermission, + @Nullable Bundle options); + + /** * Like {@link #sendBroadcast(Intent, String)}, but also allows specification * of an associated app op as per {@link android.app.AppOpsManager}. * @hide @@ -1588,11 +1620,60 @@ public abstract class Context { * @see android.app.Activity#RESULT_OK */ public abstract void sendOrderedBroadcast(@NonNull Intent intent, - @Nullable String receiverPermission, BroadcastReceiver resultReceiver, + @Nullable String receiverPermission, @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras); /** + * Version of {@link #sendBroadcast(Intent)} that allows you to + * receive data back from the broadcast. This is accomplished by + * supplying your own BroadcastReceiver when calling, which will be + * treated as a final receiver at the end of the broadcast -- its + * {@link BroadcastReceiver#onReceive} method will be called with + * the result values collected from the other receivers. The broadcast will + * be serialized in the same way as calling + * {@link #sendOrderedBroadcast(Intent, String)}. + * + * <p>Like {@link #sendBroadcast(Intent)}, this method is + * asynchronous; it will return before + * resultReceiver.onReceive() is called. + * + * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * + * @param intent The Intent to broadcast; all receivers matching this + * Intent will receive the broadcast. + * @param receiverPermission String naming a permissions that + * a receiver must hold in order to receive your broadcast. + * If null, no permission is required. + * @param options (optional) Additional sending options, generated from a + * {@link android.app.BroadcastOptions}. + * @param resultReceiver Your own BroadcastReceiver to treat as the final + * receiver of the broadcast. + * @param scheduler A custom Handler with which to schedule the + * resultReceiver callback; if null it will be + * scheduled in the Context's main thread. + * @param initialCode An initial value for the result code. Often + * Activity.RESULT_OK. + * @param initialData An initial value for the result data. Often + * null. + * @param initialExtras An initial value for the result extras. Often + * null. + * @see #sendBroadcast(Intent) + * @see #sendBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String) + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see android.app.Activity#RESULT_OK + * @hide + */ + @SystemApi + public abstract void sendOrderedBroadcast(@NonNull Intent intent, + @Nullable String receiverPermission, @Nullable Bundle options, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras); + + /** * Like {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, * int, String, android.os.Bundle)}, but also allows specification * of an associated app op as per {@link android.app.AppOpsManager}. diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index fb9e194..4e7c832 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.SystemApi; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; @@ -401,6 +402,13 @@ public class ContextWrapper extends Context { } /** @hide */ + @SystemApi + @Override + public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) { + mBase.sendBroadcast(intent, receiverPermission, options); + } + + /** @hide */ @Override public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { mBase.sendBroadcast(intent, receiverPermission, appOp); @@ -423,6 +431,18 @@ public class ContextWrapper extends Context { } /** @hide */ + @SystemApi + @Override + public void sendOrderedBroadcast( + Intent intent, String receiverPermission, Bundle options, BroadcastReceiver resultReceiver, + Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + mBase.sendOrderedBroadcast(intent, receiverPermission, + options, resultReceiver, scheduler, initialCode, + initialData, initialExtras); + } + + /** @hide */ @Override public void sendOrderedBroadcast( Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl index 7dbd6f2..f3affa7 100644 --- a/core/java/android/content/IIntentSender.aidl +++ b/core/java/android/content/IIntentSender.aidl @@ -18,9 +18,10 @@ package android.content; import android.content.IIntentReceiver; import android.content.Intent; +import android.os.Bundle; /** @hide */ interface IIntentSender { int send(int code, in Intent intent, String resolvedType, - IIntentReceiver finishedReceiver, String requiredPermission); + IIntentReceiver finishedReceiver, String requiredPermission, in Bundle options); } diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index 166495b..13a767e 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -195,7 +195,7 @@ public class IntentSender implements Parcelable { onFinished != null ? new FinishedDispatcher(this, onFinished, handler) : null, - requiredPermission); + requiredPermission, null); if (res < 0) { throw new SendIntentException(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3e9c9de..56cd1a7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7133,6 +7133,29 @@ public final class Settings { public static final String APP_IDLE_CONSTANTS = "app_idle_constants"; /** + * Alarm manager specific settings. + * This is encoded as a key=value list, separated by commas. Ex: + * + * "min_futurity=5000,allow_while_idle_short_time=4500" + * + * The following keys are supported: + * + * <pre> + * min_futurity (long) + * min_interval (long) + * allow_while_idle_short_time (long) + * allow_while_idle_long_time (long) + * allow_while_idle_whitelist_duration (long) + * </pre> + * + * <p> + * Type: string + * @hide + * @see com.android.server.AlarmManagerService.Constants + */ + public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants"; + + /** * Get the key that retrieves a bluetooth headset's priority. * @hide */ diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 26ece72..839b87a 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -20,13 +20,16 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AlarmManager; +import android.app.BroadcastOptions; import android.app.IAlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -43,6 +46,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.ArrayMap; +import android.util.KeyValueListParser; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -75,22 +79,6 @@ import static android.app.AlarmManager.ELAPSED_REALTIME; import com.android.internal.util.LocalLog; class AlarmManagerService extends SystemService { - // The threshold for how long an alarm can be late before we print a - // warning message. The time duration is in milliseconds. - private static final long LATE_ALARM_THRESHOLD = 10 * 1000; - - // Minimum futurity of a new alarm - private static final long MIN_FUTURITY = 5 * 1000; // 5 seconds, in millis - - // Minimum alarm recurrence interval - private static final long MIN_INTERVAL = 60 * 1000; // one minute, in millis - - // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle. - private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 60*1000; - - // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. - private static final long ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000; - private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP; private static final int RTC_MASK = 1 << RTC; private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP; @@ -102,7 +90,6 @@ class AlarmManagerService extends SystemService { static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup static final String TAG = "AlarmManager"; - static final String ClockReceiver_TAG = "ClockReceiver"; static final boolean localLOGV = false; static final boolean DEBUG_BATCH = localLOGV || false; static final boolean DEBUG_VALIDATE = localLOGV || false; @@ -148,7 +135,7 @@ class AlarmManagerService extends SystemService { long mNextNonWakeupDeliveryTime; long mLastTimeChangeClockTime; long mLastTimeChangeRealtime; - long mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME; + long mAllowWhileIdleMinTime; int mNumTimeChanged; /** @@ -157,6 +144,11 @@ class AlarmManagerService extends SystemService { */ final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray(); + /** + * Broadcast options to use for FLAG_ALLOW_WHILE_IDLE. + */ + Bundle mIdleOptions; + private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser = new SparseArray<>(); private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray = @@ -169,12 +161,137 @@ class AlarmManagerService extends SystemService { private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray = new SparseArray<>(); + /** + * All times are in milliseconds. These constants are kept synchronized with the system + * global Settings. Any access to this class or its fields should be done while + * holding the AlarmManagerService.mLock lock. + */ + private final class Constants extends ContentObserver { + // Key names stored in the settings value. + private static final String KEY_MIN_FUTURITY = "min_futurity"; + private static final String KEY_MIN_INTERVAL = "min_interval"; + private static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time"; + private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time"; + private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION + = "allow_while_idle_whitelist_duration"; + + private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; + private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; + private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = 60*1000; + private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000; + private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000; + + // Minimum futurity of a new alarm + public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; + + // Minimum alarm recurrence interval + public long MIN_INTERVAL = DEFAULT_MIN_INTERVAL; + + // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle. + public long ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME; + + // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. + public long ALLOW_WHILE_IDLE_LONG_TIME = DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME; + + // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE. + public long ALLOW_WHILE_IDLE_WHITELIST_DURATION + = DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION; + + private ContentResolver mResolver; + private final KeyValueListParser mParser = new KeyValueListParser(','); + private long mLastAllowWhileIdleWhitelistDuration = -1; + + public Constants(Handler handler) { + super(handler); + updateAllowWhileIdleMinTimeLocked(); + updateAllowWhileIdleWhitelistDurationLocked(); + } + + public void start(ContentResolver resolver) { + mResolver = resolver; + mResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.ALARM_MANAGER_CONSTANTS), false, this); + updateConstants(); + } + + public void updateAllowWhileIdleMinTimeLocked() { + mAllowWhileIdleMinTime = mPendingIdleUntil != null + ? ALLOW_WHILE_IDLE_LONG_TIME : ALLOW_WHILE_IDLE_SHORT_TIME; + } + + public void updateAllowWhileIdleWhitelistDurationLocked() { + if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) { + mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION; + BroadcastOptions opts = BroadcastOptions.makeBasic(); + opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION); + mIdleOptions = opts.toBundle(); + } + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + updateConstants(); + } + + private void updateConstants() { + synchronized (mLock) { + try { + mParser.setString(Settings.Global.getString(mResolver, + Settings.Global.ALARM_MANAGER_CONSTANTS)); + } catch (IllegalArgumentException e) { + // Failed to parse the settings string, log this and move on + // with defaults. + Slog.e(TAG, "Bad device idle settings", e); + } + + MIN_FUTURITY = mParser.getLong(KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY); + MIN_INTERVAL = mParser.getLong(KEY_MIN_INTERVAL, DEFAULT_MIN_INTERVAL); + ALLOW_WHILE_IDLE_SHORT_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME, + DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME); + ALLOW_WHILE_IDLE_LONG_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME, + DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME); + ALLOW_WHILE_IDLE_WHITELIST_DURATION = mParser.getLong( + KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION, + DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION); + + updateAllowWhileIdleMinTimeLocked(); + updateAllowWhileIdleWhitelistDurationLocked(); + } + } + + void dump(PrintWriter pw) { + pw.println(" Settings:"); + + pw.print(" "); pw.print(KEY_MIN_FUTURITY); pw.print("="); + TimeUtils.formatDuration(MIN_FUTURITY, pw); + pw.println(); + + pw.print(" "); pw.print(KEY_MIN_INTERVAL); pw.print("="); + TimeUtils.formatDuration(MIN_INTERVAL, pw); + pw.println(); + + pw.print(" "); pw.print(KEY_ALLOW_WHILE_IDLE_SHORT_TIME); pw.print("="); + TimeUtils.formatDuration(ALLOW_WHILE_IDLE_SHORT_TIME, pw); + pw.println(); + + pw.print(" "); pw.print(KEY_ALLOW_WHILE_IDLE_LONG_TIME); pw.print("="); + TimeUtils.formatDuration(ALLOW_WHILE_IDLE_LONG_TIME, pw); + pw.println(); + + pw.print(" "); pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION); pw.print("="); + TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw); + pw.println(); + } + } + + final Constants mConstants; + // Alarm delivery ordering bookkeeping static final int PRIO_TICK = 0; static final int PRIO_WAKEUP = 1; static final int PRIO_NORMAL = 2; - class PriorityClass { + final class PriorityClass { int seq; int priority; @@ -184,11 +301,10 @@ class AlarmManagerService extends SystemService { } } - final HashMap<String, PriorityClass> mPriorities = - new HashMap<String, PriorityClass>(); + final HashMap<String, PriorityClass> mPriorities = new HashMap<>(); int mCurrentSeq = 0; - class WakeupEvent { + static final class WakeupEvent { public long when; public int uid; public String action; @@ -482,6 +598,7 @@ class AlarmManagerService extends SystemService { public AlarmManagerService(Context context) { super(context); + mConstants = new Constants(mHandler); } static long convertToElapsed(long when, int type) { @@ -595,7 +712,7 @@ class AlarmManagerService extends SystemService { } // Make sure we are using the correct ALLOW_WHILE_IDLE min time. - mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME; + mConstants.updateAllowWhileIdleMinTimeLocked(); // Reschedule everything. rescheduleKernelAlarmsLocked(); @@ -714,6 +831,13 @@ class AlarmManagerService extends SystemService { } @Override + public void onBootPhase(int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + mConstants.start(getContext().getContentResolver()); + } + } + + @Override protected void finalize() throws Throwable { try { close(mNativeData); @@ -784,11 +908,12 @@ class AlarmManagerService extends SystemService { // Sanity check the recurrence interval. This will catch people who supply // seconds when the API expects milliseconds. - if (interval > 0 && interval < MIN_INTERVAL) { + final long minInterval = mConstants.MIN_INTERVAL; + if (interval > 0 && interval < minInterval) { Slog.w(TAG, "Suspiciously short interval " + interval - + " millis; expanding to " + (int)(MIN_INTERVAL/1000) + + " millis; expanding to " + (minInterval/1000) + " seconds"); - interval = MIN_INTERVAL; + interval = minInterval; } if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) { @@ -805,7 +930,7 @@ class AlarmManagerService extends SystemService { final long nowElapsed = SystemClock.elapsedRealtime(); final long nominalTrigger = convertToElapsed(triggerAtTime, type); // Try to prevent spamming by making sure we aren't firing alarms in the immediate future - final long minTrigger = nowElapsed + MIN_FUTURITY; + final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY; final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger; final long maxElapsed; @@ -904,7 +1029,7 @@ class AlarmManagerService extends SystemService { if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) { mPendingIdleUntil = a; - mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_LONG_TIME; + mConstants.updateAllowWhileIdleMinTimeLocked(); needRebatch = true; } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { @@ -1054,45 +1179,48 @@ class AlarmManagerService extends SystemService { void dumpImpl(PrintWriter pw) { synchronized (mLock) { pw.println("Current Alarm Manager state:"); + mConstants.dump(pw); + pw.println(); + final long nowRTC = System.currentTimeMillis(); final long nowELAPSED = SystemClock.elapsedRealtime(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - pw.print("nowRTC="); pw.print(nowRTC); + pw.print(" nowRTC="); pw.print(nowRTC); pw.print("="); pw.print(sdf.format(new Date(nowRTC))); pw.print(" nowELAPSED="); TimeUtils.formatDuration(nowELAPSED, pw); pw.println(); - pw.print("mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime); + pw.print(" mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime); pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime))); - pw.print("mLastTimeChangeRealtime="); + pw.print(" mLastTimeChangeRealtime="); TimeUtils.formatDuration(mLastTimeChangeRealtime, pw); pw.println(); if (!mInteractive) { - pw.print("Time since non-interactive: "); + pw.print(" Time since non-interactive: "); TimeUtils.formatDuration(nowELAPSED - mNonInteractiveStartTime, pw); pw.println(); - pw.print("Max wakeup delay: "); + pw.print(" Max wakeup delay: "); TimeUtils.formatDuration(currentNonWakeupFuzzLocked(nowELAPSED), pw); pw.println(); - pw.print("Time since last dispatch: "); + pw.print(" Time since last dispatch: "); TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw); pw.println(); - pw.print("Next non-wakeup delivery time: "); + pw.print(" Next non-wakeup delivery time: "); TimeUtils.formatDuration(nowELAPSED - mNextNonWakeupDeliveryTime, pw); pw.println(); } long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED); long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED); - pw.print("Next non-wakeup alarm: "); + pw.print(" Next non-wakeup alarm: "); TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw); pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC))); - pw.print("Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw); + pw.print(" Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw); pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC))); - pw.print("Num time change events: "); pw.println(mNumTimeChanged); + pw.print(" Num time change events: "); pw.println(mNumTimeChanged); pw.println(); - pw.println("Next alarm clock information: "); + pw.println(" Next alarm clock information: "); final TreeSet<Integer> users = new TreeSet<>(); for (int i = 0; i < mNextAlarmClockForUser.size(); i++) { users.add(mNextAlarmClockForUser.keyAt(i)); @@ -1104,7 +1232,7 @@ class AlarmManagerService extends SystemService { final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user); final long time = next != null ? next.getTriggerTime() : 0; final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user); - pw.print(" user:"); pw.print(user); + pw.print(" user:"); pw.print(user); pw.print(" pendingSend:"); pw.print(pendingSend); pw.print(" time:"); pw.print(time); if (time > 0) { @@ -1115,25 +1243,25 @@ class AlarmManagerService extends SystemService { } if (mAlarmBatches.size() > 0) { pw.println(); - pw.print("Pending alarm batches: "); + pw.print(" Pending alarm batches: "); pw.println(mAlarmBatches.size()); for (Batch b : mAlarmBatches) { pw.print(b); pw.println(':'); - dumpAlarmList(pw, b.alarms, " ", nowELAPSED, nowRTC, sdf); + dumpAlarmList(pw, b.alarms, " ", nowELAPSED, nowRTC, sdf); } } if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) { pw.println(); - pw.println("Idle mode state:"); - pw.print(" Idling until: "); + pw.println(" Idle mode state:"); + pw.print(" Idling until: "); if (mPendingIdleUntil != null) { pw.println(mPendingIdleUntil); mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf); } else { pw.println("null"); } - pw.println(" Pending alarms:"); - dumpAlarmList(pw, mPendingWhileIdleAlarms, " ", nowELAPSED, nowRTC, sdf); + pw.println(" Pending alarms:"); + dumpAlarmList(pw, mPendingWhileIdleAlarms, " ", nowELAPSED, nowRTC, sdf); } if (mNextWakeFromIdle != null) { pw.println(); @@ -1142,17 +1270,17 @@ class AlarmManagerService extends SystemService { } pw.println(); - pw.print("Past-due non-wakeup alarms: "); + pw.print(" Past-due non-wakeup alarms: "); if (mPendingNonWakeupAlarms.size() > 0) { pw.println(mPendingNonWakeupAlarms.size()); - dumpAlarmList(pw, mPendingNonWakeupAlarms, " ", nowELAPSED, nowRTC, sdf); + dumpAlarmList(pw, mPendingNonWakeupAlarms, " ", nowELAPSED, nowRTC, sdf); } else { pw.println("(none)"); } - pw.print(" Number of delayed alarms: "); pw.print(mNumDelayedAlarms); + pw.print(" Number of delayed alarms: "); pw.print(mNumDelayedAlarms); pw.print(", total delay time: "); TimeUtils.formatDuration(mTotalDelayTime, pw); pw.println(); - pw.print(" Max delay time: "); TimeUtils.formatDuration(mMaxDelayTime, pw); + pw.print(" Max delay time: "); TimeUtils.formatDuration(mMaxDelayTime, pw); pw.print(", max non-interactive time: "); TimeUtils.formatDuration(mNonInteractiveTime, pw); pw.println(); @@ -1161,11 +1289,11 @@ class AlarmManagerService extends SystemService { pw.print(" Broadcast ref count: "); pw.println(mBroadcastRefCount); pw.println(); - pw.print("mAllowWhileIdleMinTime="); + pw.print(" mAllowWhileIdleMinTime="); TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw); pw.println(); if (mLastAllowWhileIdleDispatch.size() > 0) { - pw.println("Last allow while idle dispatch times:"); + pw.println(" Last allow while idle dispatch times:"); for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) { pw.print(" UID "); UserHandle.formatUid(pw, mLastAllowWhileIdleDispatch.keyAt(i)); @@ -1969,6 +2097,7 @@ class AlarmManagerService extends SystemService { mLastAlarmDeliveryTime = nowELAPSED; for (int i=0; i<triggerList.size(); i++) { Alarm alarm = triggerList.get(i); + final boolean allowWhileIdle = (alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0; try { if (localLOGV) { Slog.v(TAG, "sending alarm " + alarm); @@ -1987,7 +2116,7 @@ class AlarmManagerService extends SystemService { alarm.operation.send(getContext(), 0, mBackgroundIntent.putExtra( Intent.EXTRA_ALARM_COUNT, alarm.count), - mResultReceiver, mHandler); + mResultReceiver, mHandler, null, allowWhileIdle ? mIdleOptions : null); // we have an active broadcast so stay awake. if (mBroadcastRefCount == 0) { @@ -2000,7 +2129,7 @@ class AlarmManagerService extends SystemService { mInFlight.add(inflight); mBroadcastRefCount++; - if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) { + if (allowWhileIdle) { // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm. mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED); } diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 6eba3f6..8dd087a 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -19,7 +19,6 @@ package com.android.server; import android.Manifest; import android.app.ActivityManagerNative; import android.app.AlarmManager; -import android.app.AppGlobals; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -27,7 +26,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; @@ -101,10 +99,6 @@ public class DeviceIdleController extends SystemService private static final String ACTION_ENTER_INACTIVE_STATE = "com.android.server.device_idle.ENTER_INACTIVE_STATE"; - // TODO: These need to be moved to system settings. - - - private AlarmManager mAlarmManager; private IBatteryStats mBatteryStats; private PowerManagerInternal mLocalPowerManager; @@ -180,7 +174,7 @@ public class DeviceIdleController extends SystemService * List of end times for UIDs that are temporarily marked as being allowed to access * the network and acquire wakelocks. Times are in milliseconds. */ - private SparseLongArray mTempWhitelistAppIdEndTimes = new SparseLongArray(); + private final SparseLongArray mTempWhitelistAppIdEndTimes = new SparseLongArray(); /** * Current app IDs of temporarily whitelist apps for high-priority messages. @@ -234,7 +228,7 @@ public class DeviceIdleController extends SystemService * global Settings. Any access to this class or its fields should be done while * holding the DeviceIdleController lock. */ - private class Constants extends ContentObserver { + private final class Constants extends ContentObserver { // Key names stored in the settings value. private static final String KEY_INACTIVE_TIMEOUT = "inactive_to"; private static final String KEY_SENSING_TIMEOUT = "sensing_to"; @@ -403,49 +397,49 @@ public class DeviceIdleController extends SystemService void dump(PrintWriter pw) { pw.println(" Settings:"); - pw.print(" DOZE_INACTIVE_TIMEOUT="); + pw.print(" "); pw.print(KEY_INACTIVE_TIMEOUT); pw.print("="); TimeUtils.formatDuration(INACTIVE_TIMEOUT, pw); pw.println(); - pw.print(" DOZE_SENSING_TIMEOUT="); + pw.print(" "); pw.print(KEY_SENSING_TIMEOUT); pw.print("="); TimeUtils.formatDuration(SENSING_TIMEOUT, pw); pw.println(); - pw.print(" DOZE_MOTION_INACTIVE_TIMEOUT="); + pw.print(" "); pw.print(KEY_MOTION_INACTIVE_TIMEOUT); pw.print("="); TimeUtils.formatDuration(MOTION_INACTIVE_TIMEOUT, pw); pw.println(); - pw.print(" DOZE_IDLE_AFTER_INACTIVE_TIMEOUT="); + pw.print(" "); pw.print(KEY_IDLE_AFTER_INACTIVE_TIMEOUT); pw.print("="); TimeUtils.formatDuration(IDLE_AFTER_INACTIVE_TIMEOUT, pw); pw.println(); - pw.print(" DOZE_IDLE_PENDING_TIMEOUT="); + pw.print(" "); pw.print(KEY_IDLE_PENDING_TIMEOUT); pw.print("="); TimeUtils.formatDuration(IDLE_PENDING_TIMEOUT, pw); pw.println(); - pw.print(" DOZE_MAX_IDLE_PENDING_TIMEOUT="); + pw.print(" "); pw.print(KEY_MAX_IDLE_PENDING_TIMEOUT); pw.print("="); TimeUtils.formatDuration(MAX_IDLE_PENDING_TIMEOUT, pw); pw.println(); - pw.print(" DOZE_IDLE_PENDING_FACTOR="); + pw.print(" "); pw.print(KEY_IDLE_PENDING_FACTOR); pw.print("="); pw.println(IDLE_PENDING_FACTOR); - pw.print(" DOZE_IDLE_TIMEOUT="); + pw.print(" "); pw.print(KEY_IDLE_TIMEOUT); pw.print("="); TimeUtils.formatDuration(IDLE_TIMEOUT, pw); pw.println(); - pw.print(" DOZE_MAX_IDLE_TIMEOUT="); + pw.print(" "); pw.print(KEY_MAX_IDLE_TIMEOUT); pw.print("="); TimeUtils.formatDuration(MAX_IDLE_TIMEOUT, pw); pw.println(); - pw.print(" DOZE_IDLE_FACTOR="); + pw.print(" "); pw.print(KEY_IDLE_FACTOR); pw.print("="); pw.println(IDLE_FACTOR); - pw.print(" DOZE_MIN_TIME_TO_ALARM="); + pw.print(" "); pw.print(KEY_MIN_TIME_TO_ALARM); pw.print("="); TimeUtils.formatDuration(MIN_TIME_TO_ALARM, pw); pw.println(); - pw.print(" DOZE_MAX_TEMP_APP_WHITELIST_DURATION="); + pw.print(" "); pw.print(KEY_MAX_TEMP_APP_WHITELIST_DURATION); pw.print("="); TimeUtils.formatDuration(MAX_TEMP_APP_WHITELIST_DURATION, pw); pw.println(); } @@ -465,6 +459,7 @@ public class DeviceIdleController extends SystemService } else if (result == AnyMotionDetector.RESULT_MOVED) { if (DEBUG) Slog.d(TAG, "RESULT_MOVED received."); synchronized (this) { + EventLogTags.writeDeviceIdle(mState, "sense_moved"); enterInactiveStateLocked(); } } @@ -573,15 +568,11 @@ public class DeviceIdleController extends SystemService userId, /*allowAll=*/ false, /*requireFull=*/ false, - "addAppBrieflyToWhitelist", null); + "addPowerSaveTempWhitelistApp", null); final long token = Binder.clearCallingIdentity(); try { - PackageInfo pi = AppGlobals.getPackageManager() - .getPackageInfo(packageName, 0, userId); - if (pi == null) return; DeviceIdleController.this.addPowerSaveTempWhitelistAppInternal(packageName, duration, userId); - } catch (RemoteException re) { } finally { Binder.restoreCallingIdentity(token); } @@ -592,6 +583,12 @@ public class DeviceIdleController extends SystemService } } + public final class LocalService { + public void addPowerSaveTempWhitelistAppDirect(int appId, long duration) { + DeviceIdleController.this.addPowerSaveTempWhitelistAppDirectInternal(appId, duration); + } + } + public DeviceIdleController(Context context) { super(context); mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml")); @@ -635,6 +632,7 @@ public class DeviceIdleController extends SystemService } publishBinderService(Context.DEVICE_IDLE_CONTROLLER, new BinderService()); + publishLocalService(LocalService.class, new LocalService()); } @Override @@ -765,33 +763,41 @@ public class DeviceIdleController extends SystemService try { int uid = getContext().getPackageManager().getPackageUid(packageName, userId); int appId = UserHandle.getAppId(uid); - final long timeNow = System.currentTimeMillis(); - synchronized (this) { - duration = Math.min(duration, mConstants.MAX_TEMP_APP_WHITELIST_DURATION); - long currentEndTime = mTempWhitelistAppIdEndTimes.get(appId); - // Set the new end time - mTempWhitelistAppIdEndTimes.put(appId, timeNow + duration); - if (DEBUG) { - Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist"); - } - if (currentEndTime == 0) { - // No pending timeout for the app id, post a delayed message - postTempActiveTimeoutMessage(appId, duration); - updateTempWhitelistAppIdsLocked(); - reportTempWhitelistChangedLocked(); - } - } + addPowerSaveTempWhitelistAppDirectInternal(appId, duration); } catch (NameNotFoundException e) { } } + /** + * Adds an app to the temporary whitelist and resets the endTime for granting the + * app an exemption to access network and acquire wakelocks. + */ + public void addPowerSaveTempWhitelistAppDirectInternal(int appId, long duration) { + final long timeNow = SystemClock.elapsedRealtime(); + synchronized (this) { + duration = Math.min(duration, mConstants.MAX_TEMP_APP_WHITELIST_DURATION); + long currentEndTime = mTempWhitelistAppIdEndTimes.get(appId); + // Set the new end time + mTempWhitelistAppIdEndTimes.put(appId, timeNow + duration); + if (DEBUG) { + Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist"); + } + if (currentEndTime == 0) { + // No pending timeout for the app id, post a delayed message + postTempActiveTimeoutMessage(appId, duration); + updateTempWhitelistAppIdsLocked(); + reportTempWhitelistChangedLocked(); + } + } + } + private void postTempActiveTimeoutMessage(int uid, long delay) { mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TEMP_APP_WHITELIST_TIMEOUT, uid, 0), delay); } void checkTempAppWhitelistTimeout(int uid) { - final long timeNow = System.currentTimeMillis(); + final long timeNow = SystemClock.elapsedRealtime(); synchronized (this) { long endTime = mTempWhitelistAppIdEndTimes.get(uid); if (endTime == 0) { @@ -1295,6 +1301,8 @@ public class DeviceIdleController extends SystemService } synchronized (this) { + mConstants.dump(pw); + int size = mPowerSaveWhitelistApps.size(); if (size > 0) { pw.println(" Whitelist system apps:"); @@ -1313,17 +1321,34 @@ public class DeviceIdleController extends SystemService } size = mPowerSaveWhitelistAppIds.size(); if (size > 0) { - pw.println(" Whitelist app uids:"); + pw.println(" Whitelist app ids:"); for (int i = 0; i < size; i++) { - pw.print(" UID="); + pw.print(" "); pw.print(mPowerSaveWhitelistAppIds.keyAt(i)); + pw.println(); + } + } + size = mTempWhitelistAppIdEndTimes.size(); + if (size > 0) { + pw.println(" Temp whitelist schedule:"); + final long timeNow = SystemClock.elapsedRealtime(); + for (int i = 0; i < size; i++) { + pw.print(" UID="); + pw.print(mTempWhitelistAppIdEndTimes.keyAt(i)); pw.print(": "); - pw.print(mPowerSaveWhitelistAppIds.valueAt(i)); + TimeUtils.formatDuration(mTempWhitelistAppIdEndTimes.valueAt(i), timeNow, pw); + pw.println(); + } + } + size = mTempWhitelistAppIdArray != null ? mTempWhitelistAppIdArray.length : 0; + if (size > 0) { + pw.println(" Temp whitelist app ids:"); + for (int i = 0; i < size; i++) { + pw.print(" "); + pw.print(mTempWhitelistAppIdArray[i]); pw.println(); } } - - mConstants.dump(pw); pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor); pw.print(" mCurDisplay="); pw.println(mCurDisplay); diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 6e67970..e0a9ab4 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -34,7 +34,7 @@ import android.os.ServiceManager; * local interfaces that other services within the system server may use to access * privileged internal functions. * <li>Then {@link #onBootPhase(int)} is called as many times as there are boot phases - * until {@link #PHASE_BOOT_COMPLETE} is sent, which is the last boot phase. Each phase + * until {@link #PHASE_BOOT_COMPLETED} is sent, which is the last boot phase. Each phase * is an opportunity to do special work, like acquiring optional service dependencies, * waiting to see if SafeMode is enabled, or registering with a service that gets * started after this one. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 667abb6..c4f460e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -39,6 +39,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.Manifest; import android.app.AppOpsManager; import android.app.ApplicationThreadNative; +import android.app.BroadcastOptions; import android.app.IActivityContainer; import android.app.IActivityContainerCallback; import android.app.IAppTask; @@ -89,6 +90,7 @@ import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import com.android.server.AppOpsService; import com.android.server.AttributeCache; +import com.android.server.DeviceIdleController; import com.android.server.IntentResolver; import com.android.server.LocalServices; import com.android.server.ServiceThread; @@ -941,6 +943,11 @@ public final class ActivityManagerService extends ActivityManagerNative UsageStatsManagerInternal mUsageStatsService; /** + * Access to DeviceIdleController service. + */ + DeviceIdleController.LocalService mLocalDeviceIdleController; + + /** * Information about and control over application operations */ final AppOpsService mAppOpsService; @@ -1438,7 +1445,7 @@ public final class ActivityManagerService extends ActivityManagerNative } broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */); + null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */); if (mShowDialogs) { Dialog d = new AppNotRespondingDialog(ActivityManagerService.this, @@ -2559,9 +2566,9 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void batterySendBroadcast(Intent intent) { - broadcastIntentLocked(null, null, intent, null, - null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, -1, - Process.SYSTEM_UID, UserHandle.USER_ALL); + broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, + AppOpsManager.OP_NONE, null, false, false, + -1, Process.SYSTEM_UID, UserHandle.USER_ALL); } /** @@ -5089,7 +5096,7 @@ public final class ActivityManagerService extends ActivityManagerNative Uri.fromParts("package", packageName, null)); intent.putExtra(Intent.EXTRA_UID, pkgUid); broadcastIntentInPackage("android", Process.SYSTEM_UID, intent, - null, null, 0, null, null, null, false, false, userId); + null, null, 0, null, null, null, null, false, false, userId); } catch (RemoteException e) { } } finally { @@ -5322,9 +5329,9 @@ public final class ActivityManagerService extends ActivityManagerNative mStackSupervisor.closeSystemDialogsLocked(); - broadcastIntentLocked(null, null, intent, null, - null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, -1, - Process.SYSTEM_UID, UserHandle.USER_ALL); + broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, + AppOpsManager.OP_NONE, null, false, false, + -1, Process.SYSTEM_UID, UserHandle.USER_ALL); } @Override @@ -5423,8 +5430,7 @@ public final class ActivityManagerService extends ActivityManagerNative intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid)); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - false, false, - MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid)); + null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid)); } private void forceStopUserLocked(int userId, String reason) { @@ -5435,8 +5441,7 @@ public final class ActivityManagerService extends ActivityManagerNative intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - false, false, - MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } private final boolean killPackageProcessesLocked(String packageName, int appId, @@ -6329,8 +6334,8 @@ public final class ActivityManagerService extends ActivityManagerNative }, 0, null, null, android.Manifest.permission.RECEIVE_BOOT_COMPLETED, - AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID, - userId); + AppOpsManager.OP_NONE, null, true, false, + MY_PID, Process.SYSTEM_UID, userId); } } scheduleStartProfilesLocked(); @@ -11442,8 +11447,7 @@ public final class ActivityManagerService extends ActivityManagerNative EventLogTags.writeAmPreBoot(users[curUser], intent.getComponent().getPackageName()); broadcastIntentLocked(null, null, intent, null, this, 0, null, null, null, AppOpsManager.OP_NONE, - true, false, MY_PID, Process.SYSTEM_UID, - users[curUser]); + null, true, false, MY_PID, Process.SYSTEM_UID, users[curUser]); } public void performReceive(Intent intent, int resultCode, @@ -11535,6 +11539,9 @@ public final class ActivityManagerService extends ActivityManagerNative return; } + mLocalDeviceIdleController + = LocalServices.getService(DeviceIdleController.LocalService.class); + // Make sure we have the current profile info, since it is needed for // security checks. updateCurrentProfileIdsLocked(); @@ -11704,7 +11711,7 @@ public final class ActivityManagerService extends ActivityManagerNative intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId); + null, false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId); intent = new Intent(Intent.ACTION_USER_STARTING); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId); @@ -11717,7 +11724,7 @@ public final class ActivityManagerService extends ActivityManagerNative } }, 0, null, null, INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE, - true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } catch (Throwable t) { Slog.wtf(TAG, "Failed sending first user broadcasts", t); } finally { @@ -16101,8 +16108,8 @@ public final class ActivityManagerService extends ActivityManagerNative Intent intent = allSticky.get(i); BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, - null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0, - null, null, false, true, true, -1); + null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers, + null, 0, null, null, false, true, true, -1); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } @@ -16255,9 +16262,8 @@ public final class ActivityManagerService extends ActivityManagerNative private final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, - Bundle map, String requiredPermission, int appOp, - boolean ordered, boolean sticky, int callingPid, int callingUid, - int userId) { + Bundle resultExtras, String requiredPermission, int appOp, Bundle options, + boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { intent = new Intent(intent); // By default broadcasts do not go to stopped apps. @@ -16292,6 +16298,28 @@ public final class ActivityManagerService extends ActivityManagerNative } } + BroadcastOptions brOptions = null; + if (options != null) { + brOptions = new BroadcastOptions(options); + if (brOptions.getTemporaryAppWhitelistDuration() > 0) { + // See if the caller is allowed to do this. Note we are checking against + // the actual real caller (not whoever provided the operation as say a + // PendingIntent), because that who is actually supplied the arguments. + if (checkComponentPermission( + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + Binder.getCallingPid(), Binder.getCallingUid(), -1, true) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: " + intent.getAction() + + " broadcast from " + callerPackage + " (pid=" + callingPid + + ", uid=" + callingUid + ")" + + " requires " + + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } + } + /* * Prevent non-system code (defined here to be non-persistent * processes) from sending protected broadcasts. @@ -16598,8 +16626,8 @@ public final class ActivityManagerService extends ActivityManagerNative final BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermission, - appOp, registeredReceivers, resultTo, resultCode, resultData, map, - ordered, sticky, false, userId); + appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData, + resultExtras, ordered, sticky, false, userId); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r); if (!replaced) { @@ -16687,8 +16715,8 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, - requiredPermission, appOp, receivers, resultTo, resultCode, - resultData, map, ordered, sticky, false, userId); + requiredPermission, appOp, brOptions, receivers, resultTo, resultCode, + resultData, resultExtras, ordered, sticky, false, userId); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r + ": prev had " + queue.mOrderedBroadcasts.size()); @@ -16735,8 +16763,9 @@ public final class ActivityManagerService extends ActivityManagerNative public final int broadcastIntent(IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, - int resultCode, String resultData, Bundle map, - String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) { + int resultCode, String resultData, Bundle resultExtras, + String requiredPermission, int appOp, Bundle options, + boolean serialized, boolean sticky, int userId) { enforceNotIsolatedCaller("broadcastIntent"); synchronized(this) { intent = verifyBroadcastLocked(intent); @@ -16747,8 +16776,8 @@ public final class ActivityManagerService extends ActivityManagerNative final long origId = Binder.clearCallingIdentity(); int res = broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, - intent, resolvedType, resultTo, - resultCode, resultData, map, requiredPermission, appOp, serialized, sticky, + intent, resolvedType, resultTo, resultCode, resultData, resultExtras, + requiredPermission, appOp, null, serialized, sticky, callingPid, callingUid, userId); Binder.restoreCallingIdentity(origId); return res; @@ -16757,15 +16786,16 @@ public final class ActivityManagerService extends ActivityManagerNative int broadcastIntentInPackage(String packageName, int uid, Intent intent, String resolvedType, IIntentReceiver resultTo, - int resultCode, String resultData, Bundle map, - String requiredPermission, boolean serialized, boolean sticky, int userId) { + int resultCode, String resultData, Bundle resultExtras, + String requiredPermission, Bundle options, boolean serialized, boolean sticky, + int userId) { synchronized(this) { intent = verifyBroadcastLocked(intent); final long origId = Binder.clearCallingIdentity(); int res = broadcastIntentLocked(null, packageName, intent, resolvedType, - resultTo, resultCode, resultData, map, requiredPermission, - AppOpsManager.OP_NONE, serialized, sticky, -1, uid, userId); + resultTo, resultCode, resultData, resultExtras, requiredPermission, + AppOpsManager.OP_NONE, options, serialized, sticky, -1, uid, userId); Binder.restoreCallingIdentity(origId); return res; } @@ -17162,8 +17192,8 @@ public final class ActivityManagerService extends ActivityManagerNative | Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, - null, AppOpsManager.OP_NONE, false, false, MY_PID, - Process.SYSTEM_UID, UserHandle.USER_ALL); + null, AppOpsManager.OP_NONE, null, false, false, + MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); @@ -17172,7 +17202,7 @@ public final class ActivityManagerService extends ActivityManagerNative } broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } } } @@ -19642,7 +19672,7 @@ public final class ActivityManagerService extends ActivityManagerNative intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - false, false, MY_PID, Process.SYSTEM_UID, userId); + null, false, false, MY_PID, Process.SYSTEM_UID, userId); } if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) { @@ -19657,8 +19687,7 @@ public final class ActivityManagerService extends ActivityManagerNative onUserInitialized(uss, foreground, oldUserId, userId); } }, 0, null, null, null, AppOpsManager.OP_NONE, - true, false, MY_PID, Process.SYSTEM_UID, - userId); + null, true, false, MY_PID, Process.SYSTEM_UID, userId); uss.initializing = true; } else { getUserManagerLocked().makeInitialized(userInfo.id); @@ -19680,13 +19709,13 @@ public final class ActivityManagerService extends ActivityManagerNative broadcastIntentLocked(null, null, intent, null, new IIntentReceiver.Stub() { @Override - public void performReceive(Intent intent, int resultCode, String data, - Bundle extras, boolean ordered, boolean sticky, int sendingUser) - throws RemoteException { + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, boolean sticky, + int sendingUser) throws RemoteException { } }, 0, null, null, INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE, - true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } } } finally { @@ -19724,7 +19753,7 @@ public final class ActivityManagerService extends ActivityManagerNative intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - false, false, MY_PID, Process.SYSTEM_UID, profileUserId); + null, false, false, MY_PID, Process.SYSTEM_UID, profileUserId); } } if (newUserId >= 0) { @@ -19739,7 +19768,7 @@ public final class ActivityManagerService extends ActivityManagerNative intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - false, false, MY_PID, Process.SYSTEM_UID, profileUserId); + null, false, false, MY_PID, Process.SYSTEM_UID, profileUserId); } intent = new Intent(Intent.ACTION_USER_SWITCHED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY @@ -19748,7 +19777,7 @@ public final class ActivityManagerService extends ActivityManagerNative broadcastIntentLocked(null, null, intent, null, null, 0, null, null, android.Manifest.permission.MANAGE_USERS, AppOpsManager.OP_NONE, - false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } } finally { Binder.restoreCallingIdentity(ident); @@ -19925,7 +19954,7 @@ public final class ActivityManagerService extends ActivityManagerNative broadcastIntentLocked(null, null, intent, null, null, 0, null, null, android.Manifest.permission.RECEIVE_BOOT_COMPLETED, AppOpsManager.OP_NONE, - true, false, MY_PID, Process.SYSTEM_UID, userId); + null, true, false, MY_PID, Process.SYSTEM_UID, userId); } } } @@ -20057,14 +20086,14 @@ public final class ActivityManagerService extends ActivityManagerNative mSystemServiceManager.stopUser(userId); broadcastIntentLocked(null, null, shutdownIntent, null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE, - true, false, MY_PID, Process.SYSTEM_UID, userId); + null, true, false, MY_PID, Process.SYSTEM_UID, userId); } }; // Kick things off. broadcastIntentLocked(null, null, stoppingIntent, null, stoppingReceiver, 0, null, null, INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE, - true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 80b8a93..2335071 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -26,6 +26,7 @@ import java.util.Set; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.content.ComponentName; import android.content.IIntentReceiver; import android.content.Intent; @@ -43,6 +44,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.util.EventLog; import android.util.Slog; +import com.android.server.DeviceIdleController; import static com.android.server.am.ActivityManagerDebugConfig.*; @@ -146,6 +148,8 @@ public final class BroadcastQueue { static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG; static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1; + static final int SCHEDULE_TEMP_WHITELIST_MSG + = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 2; final BroadcastHandler mHandler; @@ -167,6 +171,13 @@ public final class BroadcastQueue { broadcastTimeoutLocked(true); } } break; + case SCHEDULE_TEMP_WHITELIST_MSG: { + DeviceIdleController.LocalService dic = mService.mLocalDeviceIdleController; + if (dic != null) { + dic.addPowerSaveTempWhitelistAppDirect(UserHandle.getAppId(msg.arg1), + msg.arg2); + } + } break; } } }; @@ -547,6 +558,19 @@ public final class BroadcastQueue { } } + final void scheduleTempWhitelistLocked(int uid, long duration) { + if (duration > Integer.MAX_VALUE) { + duration = Integer.MAX_VALUE; + } + // XXX ideally we should pause the broadcast until everything behind this is done, + // or else we will likely start dispatching the broadcast before we have opened + // access to the app (there is a lot of asynchronicity behind this). It is probably + // not that big a deal, however, because the main purpose here is to allow apps + // to hold wake locks, and they will be able to acquire their wake lock immediately + // it just won't be enabled until we get through this work. + mHandler.obtainMessage(SCHEDULE_TEMP_WHITELIST_MSG, uid, (int)duration).sendToTarget(); + } + final void processNextBroadcast(boolean fromMsg) { synchronized(mService) { BroadcastRecord r; @@ -721,7 +745,9 @@ public final class BroadcastQueue { setBroadcastTimeoutLocked(timeoutTime); } - Object nextReceiver = r.receivers.get(recIdx); + final BroadcastOptions brOptions = r.options; + final Object nextReceiver = r.receivers.get(recIdx); + if (nextReceiver instanceof BroadcastFilter) { // Simple case: this is a registered receiver who gets // a direct call. @@ -739,6 +765,11 @@ public final class BroadcastQueue { + r.ordered + " receiver=" + r.receiver); r.state = BroadcastRecord.IDLE; scheduleBroadcastsLocked(); + } else { + if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) { + scheduleTempWhitelistLocked(filter.owningUid, + brOptions.getTemporaryAppWhitelistDuration()); + } } return; } @@ -882,6 +913,11 @@ public final class BroadcastQueue { + info.activityInfo.applicationInfo.uid); } + if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) { + scheduleTempWhitelistLocked(receiverUid, + brOptions.getTemporaryAppWhitelistDuration()); + } + // Broadcast is being executed, its package can't be stopped. try { AppGlobals.getPackageManager().setPackageStoppedState( diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index c050d03..b943222 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -17,6 +17,7 @@ package com.android.server.am; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.content.IIntentReceiver; import android.content.ComponentName; import android.content.Intent; @@ -52,6 +53,7 @@ final class BroadcastRecord extends Binder { final String resolvedType; // the resolved data type final String requiredPermission; // a permission the caller has required final int appOp; // an app op that is associated with this broadcast + final BroadcastOptions options; // BroadcastOptions supplied by caller final List receivers; // contains BroadcastFilter and ResolveInfo IIntentReceiver resultTo; // who receives final result if non-null long enqueueClockTime; // the clock time the broadcast was enqueued @@ -105,6 +107,9 @@ final class BroadcastRecord extends Binder { pw.print(prefix); pw.print("requiredPermission="); pw.print(requiredPermission); pw.print(" appOp="); pw.println(appOp); } + if (options != null) { + pw.print(prefix); pw.print("options="); pw.println(options.toBundle()); + } pw.print(prefix); pw.print("enqueueClockTime="); pw.print(new Date(enqueueClockTime)); pw.print(" dispatchClockTime="); @@ -180,8 +185,8 @@ final class BroadcastRecord extends Binder { BroadcastRecord(BroadcastQueue _queue, Intent _intent, ProcessRecord _callerApp, String _callerPackage, int _callingPid, int _callingUid, String _resolvedType, String _requiredPermission, - int _appOp, List _receivers, IIntentReceiver _resultTo, int _resultCode, - String _resultData, Bundle _resultExtras, boolean _serialized, + int _appOp, BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo, + int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId) { queue = _queue; @@ -194,6 +199,7 @@ final class BroadcastRecord extends Binder { resolvedType = _resolvedType; requiredPermission = _requiredPermission; appOp = _appOp; + options = _options; receivers = _receivers; resultTo = _resultTo; resultCode = _resultCode; diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 531de46..ece3ffb 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -199,9 +199,9 @@ final class PendingIntentRecord extends IIntentSender.Stub { } public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, - String requiredPermission) throws TransactionTooLargeException { + String requiredPermission, Bundle options) throws TransactionTooLargeException { return sendInner(code, intent, resolvedType, finishedReceiver, - requiredPermission, null, null, 0, 0, 0, null, null); + requiredPermission, null, null, 0, 0, 0, options, null); } int sendInner(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, @@ -293,9 +293,9 @@ final class PendingIntentRecord extends IIntentSender.Stub { // If a completion callback has been requested, require // that the broadcast be delivered synchronously int sent = owner.broadcastIntentInPackage(key.packageName, uid, - finalIntent, resolvedType, - finishedReceiver, code, null, null, - requiredPermission, (finishedReceiver != null), false, userId); + finalIntent, resolvedType, finishedReceiver, code, null, null, + requiredPermission, options, (finishedReceiver != null), + false, userId); if (sent == ActivityManager.BROADCAST_SUCCESS) { sendFinish = false; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 5b7dd70..06e27fc 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8808,7 +8808,7 @@ public class PackageManagerService extends IPackageManager.Stub { } am.broadcastIntent(null, intent, null, finishedReceiver, 0, null, null, null, android.app.AppOpsManager.OP_NONE, - finishedReceiver != null, false, id); + null, finishedReceiver != null, false, id); } } catch (RemoteException ex) { } @@ -8982,7 +8982,7 @@ public class PackageManagerService extends IPackageManager.Stub { .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES) .setPackage(packageName); am.broadcastIntent(null, bcIntent, null, null, 0, null, null, null, - android.app.AppOpsManager.OP_NONE, false, false, userId); + android.app.AppOpsManager.OP_NONE, null, false, false, userId); } } catch (RemoteException e) { // shouldn't happen diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 75b8278..3767fce 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -981,7 +981,7 @@ public class UsageStatsService extends SystemService implements } } - private class BinderService extends IUsageStatsManager.Stub { + private final class BinderService extends IUsageStatsManager.Stub { private boolean hasPermission(String callingPackage) { final int callingUid = Binder.getCallingUid(); @@ -1121,7 +1121,7 @@ public class UsageStatsService extends SystemService implements * ActivityManagerService will call these methods holding the 'am' lock, which means we * shouldn't be doing any IO work or other long running tasks in these methods. */ - private class LocalService extends UsageStatsManagerInternal { + private final class LocalService extends UsageStatsManagerInternal { @Override public void reportEvent(ComponentName component, int userId, int eventType) { diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index 04ded9d..e5e3e44 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -16,6 +16,7 @@ package android.test.mock; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -317,6 +318,13 @@ public class MockContext extends Context { } /** @hide */ + @SystemApi + @Override + public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) { + throw new UnsupportedOperationException(); + } + + /** @hide */ @Override public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { throw new UnsupportedOperationException(); @@ -336,6 +344,15 @@ public class MockContext extends Context { } /** @hide */ + @SystemApi + @Override + public void sendOrderedBroadcast(Intent intent, String receiverPermission, + Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, + Bundle initialExtras) { + throw new UnsupportedOperationException(); + } + + /** @hide */ @Override public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 6be5a95..422b2aa 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -1506,6 +1506,12 @@ public final class BridgeContext extends Context { } @Override + public void sendBroadcast(Intent arg0, String arg1, Bundle arg2) { + // pass + + } + + @Override public void sendBroadcast(Intent intent, String receiverPermission, int appOp) { // pass } @@ -1525,6 +1531,14 @@ public final class BridgeContext extends Context { } @Override + public void sendOrderedBroadcast(Intent arg0, String arg1, + Bundle arg7, BroadcastReceiver arg2, Handler arg3, int arg4, String arg5, + Bundle arg6) { + // pass + + } + + @Override public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras) { |