diff options
12 files changed, 241 insertions, 30 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 4b8d9ee37cc6..3a70a4cd4b99 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -18,6 +18,7 @@ package android.app; import android.annotation.NonNull; import android.content.ComponentName; +import android.content.IIntentSender; import android.os.IBinder; import android.service.voice.IVoiceInteractionSession; @@ -144,4 +145,10 @@ public abstract class ActivityManagerInternal { * Kill foreground apps from the specified user. */ public abstract void killForegroundAppsForUser(int userHandle); + + /** + * Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions + * such as Power Save mode. + */ + public abstract void setPendingIntentWhitelistDuration(IIntentSender target, long duration); } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index cd9a05b7bf0f..f12c284d68ff 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -6966,8 +6966,8 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } - public void notifyLockedProfile(@UserIdInt int userId) throws RemoteException - { + @Override + public void notifyLockedProfile(@UserIdInt int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -6978,6 +6978,7 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + @Override public void startConfirmDeviceCredentialIntent(Intent intent) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -6989,6 +6990,7 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + @Override public int sendIntentSender(IIntentSender target, int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) throws RemoteException { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index b7588687a8e4..108350a2b9ce 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -285,6 +285,12 @@ public abstract class Context { public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080; /** + * @hide Flag for {@link #bindService}: allows application hosting service to manage whitelists + * such as temporary allowing a {@code PendingIntent} to bypass Power Save mode. + */ + public static final int BIND_ALLOW_WHITELIST_MANAGEMENT = 0x01000000; + + /** * @hide Flag for {@link #bindService}: Like {@link #BIND_FOREGROUND_SERVICE}, * but only applies while the device is awake. */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0bc514ed0545..f867fb024686 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -60,8 +60,8 @@ import android.util.AndroidException; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; - import android.util.MemoryIntArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.internal.widget.ILockSettings; @@ -7950,6 +7950,7 @@ public final class Settings { * idle_factor (float) * min_time_to_alarm (long) * max_temp_app_whitelist_duration (long) + * notification_whitelist_duration (long) * </pre> * * <p> diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 69960c7223d2..bb966f7b0eca 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -542,6 +542,8 @@ public class DeviceIdleController extends SystemService "mms_temp_app_whitelist_duration"; private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION = "sms_temp_app_whitelist_duration"; + private static final String KEY_NOTIFICATION_WHITELIST_DURATION = + "notification_whitelist_duration"; /** * This is the time, after becoming inactive, that we go in to the first @@ -752,6 +754,14 @@ public class DeviceIdleController extends SystemService */ public long SMS_TEMP_APP_WHITELIST_DURATION; + /** + * Amount of time we would like to whitelist an app that is handling a + * {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}. + * @see Settings.Global#DEVICE_IDLE_CONSTANTS + * @see #KEY_NOTIFICATION_WHITELIST_DURATION + */ + public long NOTIFICATION_WHITELIST_DURATION; + private final ContentResolver mResolver; private final boolean mHasWatch; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -842,6 +852,8 @@ public class DeviceIdleController extends SystemService KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L); SMS_TEMP_APP_WHITELIST_DURATION = mParser.getLong( KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L); + NOTIFICATION_WHITELIST_DURATION = mParser.getLong( + KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L); } } @@ -945,6 +957,10 @@ public class DeviceIdleController extends SystemService pw.print(" "); pw.print(KEY_SMS_TEMP_APP_WHITELIST_DURATION); pw.print("="); TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw); pw.println(); + + pw.print(" "); pw.print(KEY_NOTIFICATION_WHITELIST_DURATION); pw.print("="); + TimeUtils.formatDuration(NOTIFICATION_WHITELIST_DURATION, pw); + pw.println(); } } @@ -1252,6 +1268,10 @@ public class DeviceIdleController extends SystemService addPowerSaveTempWhitelistAppDirectInternal(0, appId, duration, sync, reason); } + public long getNotificationWhitelistDuration() { + return mConstants.NOTIFICATION_WHITELIST_DURATION; + } + public void setNetworkPolicyTempWhitelistCallback(Runnable callback) { setNetworkPolicyTempWhitelistCallbackInternal(callback); } @@ -1632,7 +1652,7 @@ public class DeviceIdleController extends SystemService } entry.first.value = timeNow + duration; if (DEBUG) { - Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist"); + Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist. New entry: " + newEntry); } if (newEntry) { // No pending timeout for the app id, post a delayed message @@ -1665,12 +1685,18 @@ public class DeviceIdleController extends SystemService } private void postTempActiveTimeoutMessage(int uid, long delay) { + if (DEBUG) { + Slog.d(TAG, "postTempActiveTimeoutMessage: uid=" + uid + ", delay=" + delay); + } mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TEMP_APP_WHITELIST_TIMEOUT, uid, 0), delay); } void checkTempAppWhitelistTimeout(int uid) { final long timeNow = SystemClock.elapsedRealtime(); + if (DEBUG) { + Slog.d(TAG, "checkTempAppWhitelistTimeout: uid=" + uid + ", timeNow=" + timeNow); + } synchronized (this) { Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(uid); if (entry == null) { @@ -1694,6 +1720,9 @@ public class DeviceIdleController extends SystemService } } else { // Need more time + if (DEBUG) { + Slog.d(TAG, "Time to remove UID " + uid + ": " + entry.first.value); + } postTempActiveTimeoutMessage(uid, entry.first.value - timeNow); } } @@ -2514,6 +2543,8 @@ public class DeviceIdleController extends SystemService pw.println(" Print currently whitelisted apps."); pw.println(" whitelist [package ...]"); pw.println(" Add (prefix with +) or remove (prefix with -) packages."); + pw.println(" tempwhitelist"); + pw.println(" Print packages that are temporarily whitelisted."); pw.println(" tempwhitelist [-u] [package ..]"); pw.println(" Temporarily place packages in whitelist for 10 seconds."); } @@ -2817,8 +2848,7 @@ public class DeviceIdleController extends SystemService pw.println("Failed: " + re); } } else { - pw.println("At least one package name must be specified"); - return -1; + dumpTempWhitelistSchedule(pw, false); } } else { return shell.handleDefaultCommands(cmd); @@ -2943,20 +2973,8 @@ public class DeviceIdleController extends SystemService 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(": "); - Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.valueAt(i); - TimeUtils.formatDuration(entry.first.value, timeNow, pw); - pw.print(" - "); - pw.println(entry.second); - } - } + dumpTempWhitelistSchedule(pw, true); + size = mTempWhitelistAppIdArray != null ? mTempWhitelistAppIdArray.length : 0; if (size > 0) { pw.println(" Temp whitelist app ids:"); @@ -2968,7 +2986,7 @@ public class DeviceIdleController extends SystemService } pw.print(" mLightEnabled="); pw.print(mLightEnabled); - pw.print(" mDeepEnabled="); pw.println(mDeepEnabled); + pw.print(" mDeepEnabled="); pw.println(mDeepEnabled); pw.print(" mForceIdle="); pw.println(mForceIdle); pw.print(" mMotionSensor="); pw.println(mMotionSensor); pw.print(" mCurDisplay="); pw.println(mCurDisplay); @@ -3040,4 +3058,26 @@ public class DeviceIdleController extends SystemService } } } -} + + void dumpTempWhitelistSchedule(PrintWriter pw, boolean printTitle) { + final int size = mTempWhitelistAppIdEndTimes.size(); + if (size > 0) { + String prefix = ""; + if (printTitle) { + pw.println(" Temp whitelist schedule:"); + prefix = " "; + } + final long timeNow = SystemClock.elapsedRealtime(); + for (int i = 0; i < size; i++) { + pw.print(prefix); + pw.print("UID="); + pw.print(mTempWhitelistAppIdEndTimes.keyAt(i)); + pw.print(": "); + Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.valueAt(i); + TimeUtils.formatDuration(entry.first.value, timeNow, pw); + pw.print(" - "); + pw.println(entry.second); + } + } + } + } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c24123a1be5c..6a8c8b053ea7 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -831,8 +831,9 @@ public final class ActiveServices { int clientLabel = 0; PendingIntent clientIntent = null; + final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID; - if (callerApp.info.uid == Process.SYSTEM_UID) { + if (isCallerSystem) { // Hacky kind of thing -- allow system stuff to tell us // what they are, so we can report this elsewhere for // others to know why certain services are running. @@ -854,6 +855,12 @@ public final class ActiveServices { "BIND_TREAT_LIKE_ACTIVITY"); } + if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) { + throw new SecurityException( + "Non-system caller " + caller + " (pid=" + Binder.getCallingPid() + + ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service); + } + final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND; final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0; @@ -1124,6 +1131,11 @@ public final class ActiveServices { } if (r.binding.service.app != null) { + if (r.binding.service.app.whitelistManager) { + // Must reset flag here because on computeOomAdjLocked() the service + // connection will be gone... + r.binding.service.app.whitelistManager = false; + } // This could have made the service less important. if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { r.binding.service.app.treatLikeActivity = true; diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index f2bf4f939bad..43bb5ee8d3e4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -90,6 +90,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false; static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false; + static final boolean DEBUG_WHITELISTS = DEBUG_ALL || false; static final String POSTFIX_ADD_REMOVE = (APPEND_CATEGORY_NAME) ? "_AddRemove" : ""; static final String POSTFIX_APP = (APPEND_CATEGORY_NAME) ? "_App" : ""; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7c828293be8c..ec54116003c5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -321,6 +321,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISS import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP; @@ -7135,6 +7136,41 @@ public final class ActivityManagerService extends ActivityManagerNative } } + /** + * Whitelists {@code targetUid} to temporarily bypass Power Save mode. + * + * <p>{@code callerUid} must be allowed to request such whitelist by calling + * {@link #addTempPowerSaveWhitelistGrantorUid(int)}. + */ + void tempWhitelistAppForPowerSave(int callerPid, int callerUid, int targetUid, long duration) { + if (DEBUG_WHITELISTS) { + Slog.d(TAG, "tempWhitelistAppForPowerSave(" + callerPid + ", " + callerUid + ", " + + targetUid + ", " + duration + ")"); + } + synchronized (mPidsSelfLocked) { + final ProcessRecord pr = mPidsSelfLocked.get(callerPid); + if (pr == null) { + Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid " + callerPid); + return; + } + if (!pr.whitelistManager) { + if (DEBUG_WHITELISTS) { + Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid + ": pid " + + callerPid + " is not allowed"); + } + return; + } + } + + final long token = Binder.clearCallingIdentity(); + try { + mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(targetUid, duration, + true, "pe from uid:" + callerUid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override public void cancelIntentSender(IIntentSender sender) { if (!(sender instanceof PendingIntentRecord)) { @@ -19023,6 +19059,9 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + + app.whitelistManager = false; + for (int conni = s.connections.size()-1; conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND @@ -19041,6 +19080,10 @@ public final class ActivityManagerService extends ActivityManagerNative // Binding to ourself is not interesting. continue; } + if ((cr.flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { + app.whitelistManager = true; + } + if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) { ProcessRecord client = cr.binding.client; int clientAdj = computeOomAdjLocked(client, cachedAdj, @@ -21312,6 +21355,15 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + + @Override + public void setPendingIntentWhitelistDuration(IIntentSender target, long duration) { + if (!(target instanceof PendingIntentRecord)) { + Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target); + return; + } + ((PendingIntentRecord) target).setWhitelistDuration(duration); + } } private final class SleepTokenImpl extends SleepToken { diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 1f8d26bc55d0..c1ff4dd0e4a3 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -47,6 +47,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { final WeakReference<PendingIntentRecord> ref; boolean sent = false; boolean canceled = false; + private long whitelistDuration = 0; String stringName; String lastTagPrefix; @@ -66,9 +67,9 @@ final class PendingIntentRecord extends IIntentSender.Stub { final int flags; final int hashCode; final int userId; - + private static final int ODD_PRIME_NUMBER = 37; - + Key(int _t, String _p, ActivityRecord _a, String _w, int _r, Intent[] _i, String[] _it, int _f, Bundle _o, int _userId) { type = _t; @@ -106,7 +107,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { //Slog.i(ActivityManagerService.TAG, this + " hashCode=0x" // + Integer.toHexString(hashCode)); } - + public boolean equals(Object otherObj) { if (otherObj == null) { return false; @@ -198,6 +199,11 @@ final class PendingIntentRecord extends IIntentSender.Stub { ref = new WeakReference<PendingIntentRecord>(this); } + void setWhitelistDuration(long duration) { + this.whitelistDuration = duration; + this.stringName = null; + } + public void send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { sendInner(code, intent, resolvedType, finishedReceiver, @@ -216,6 +222,14 @@ final class PendingIntentRecord extends IIntentSender.Stub { if (intent != null) intent.setDefusable(true); if (options != null) options.setDefusable(true); + if (whitelistDuration > 0 && !canceled) { + // Must call before acquiring the lock. It's possible the method return before sending + // the intent due to some validations inside the lock, in which case the UID shouldn't + // be whitelisted, but since the whitelist is temporary, that would be ok. + owner.tempWhitelistAppForPowerSave(Binder.getCallingPid(), Binder.getCallingUid(), uid, + whitelistDuration); + } + synchronized (owner) { final ActivityContainer activityContainer = (ActivityContainer)container; if (activityContainer != null && activityContainer.mParentActivity != null && @@ -361,7 +375,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { } } } - + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("uid="); pw.print(uid); pw.print(" packageName="); pw.print(key.packageName); @@ -383,6 +397,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { pw.print(prefix); pw.print("sent="); pw.print(sent); pw.print(" canceled="); pw.println(canceled); } + pw.print(prefix); pw.println("whitelistDuration="); pw.println(whitelistDuration); } public String toString() { @@ -396,6 +411,9 @@ final class PendingIntentRecord extends IIntentSender.Stub { sb.append(key.packageName); sb.append(' '); sb.append(key.typeName()); + if (whitelistDuration > 0) { + sb.append( " (whitelistDuration: ").append(whitelistDuration).append("ms)"); + } sb.append('}'); return stringName = sb.toString(); } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index da18f32787bd..691fd2abe0b3 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -194,6 +194,8 @@ final class ProcessRecord { // Process is currently hosting a backup agent for backup or restore public boolean inFullBackup; + // App is allowed to manage whitelists such as temporary Power Save mode whitelist. + boolean whitelistManager; void dump(PrintWriter pw, String prefix) { final long now = SystemClock.uptimeMillis(); @@ -376,6 +378,9 @@ final class ProcessRecord { } pw.println(); } + if (whitelistManager) { + pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager); + } if (activities.size() > 0) { pw.print(prefix); pw.println("Activities:"); for (int i=0; i<activities.size(); i++) { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 53c5a6dc65e2..dc85dd7ef40e 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -16,6 +16,10 @@ package com.android.server.notification; +import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT; +import static android.content.Context.BIND_AUTO_CREATE; +import static android.content.Context.BIND_FOREGROUND_SERVICE; + import android.annotation.NonNull; import android.app.ActivityManager; import android.app.PendingIntent; @@ -43,7 +47,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -56,7 +59,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; -import java.util.Map.Entry; import java.util.Objects; import java.util.Set; @@ -681,7 +683,7 @@ abstract public class ManagedServices { }; if (!mContext.bindServiceAsUser(intent, serviceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT, new UserHandle(userid))) { mServicesBinding.remove(servicesBindingTag); Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index cf4669d77789..b5a8bf3f4d1a 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -40,11 +40,13 @@ import static android.service.notification.NotificationListenerService.TRIM_FULL import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT; import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE; + import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import android.Manifest; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -56,6 +58,7 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.app.PendingIntent; +import android.app.RemoteInput; import android.app.StatusBarManager; import android.app.backup.BackupManager; import android.app.usage.UsageEvents; @@ -64,6 +67,7 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; @@ -90,6 +94,7 @@ import android.os.IBinder; import android.os.IInterface; import android.os.Looper; import android.os.Message; +import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; @@ -127,6 +132,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; +import com.android.server.DeviceIdleController; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -2542,6 +2548,8 @@ public class NotificationManagerService extends SystemService { + " id=" + id + " notification=" + notification); } + markAsSentFromNotification(notification); + // Sanitize inputs notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN, Notification.PRIORITY_MAX); @@ -2556,6 +2564,63 @@ public class NotificationManagerService extends SystemService { idOut[0] = id; } + private static void markAsSentFromNotification(Notification notification) { + final ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class); + final long duration = LocalServices.getService(DeviceIdleController.LocalService.class) + .getNotificationWhitelistDuration(); + + int size = 0; + if (notification.contentIntent != null) { + am.setPendingIntentWhitelistDuration(notification.contentIntent.getTarget(), duration); + } + if (notification.deleteIntent != null) { + am.setPendingIntentWhitelistDuration(notification.deleteIntent.getTarget(), duration); + } + if (notification.fullScreenIntent != null) { + am.setPendingIntentWhitelistDuration(notification.fullScreenIntent.getTarget(), + duration); + } + if (notification.actions != null) { + for (Notification.Action action: notification.actions) { + am.setPendingIntentWhitelistDuration(action.actionIntent.getTarget(), duration); + setPendingIntentWhitelistDuration(am, duration, action.getExtras()); + final RemoteInput[] remoteInputs = action.getRemoteInputs(); + if (remoteInputs != null) { + for (RemoteInput remoteInput : remoteInputs) { + setPendingIntentWhitelistDuration(am, duration, remoteInput.getExtras()); + } + } + } + } + } + + private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration, + Bundle extras) { + for (String key : extras.keySet()) { + setPendingIntentWhitelistDuration(am, duration, extras.getParcelable(key)); + final Parcelable[] parcelableArray = extras.getParcelableArray(key); + if (parcelableArray != null) { + for (Parcelable parcelable: parcelableArray) { + setPendingIntentWhitelistDuration(am, duration, parcelable); + } + } + final ArrayList<Parcelable> parcelableList = extras.getParcelableArrayList(key); + if (parcelableList != null) { + for (Parcelable parcelable: parcelableList) { + setPendingIntentWhitelistDuration(am, duration, parcelable); + } + } + } + } + + private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration, + Parcelable parcelable) { + if (parcelable instanceof PendingIntent) { + am.setPendingIntentWhitelistDuration(((PendingIntent) parcelable).getTarget(), + duration); + } + } + private class EnqueueNotificationRunnable implements Runnable { private final NotificationRecord r; private final int userId; |