Merge "Fix issue #37360626: Apps can schedule alarms (and other things) with temp whitelist" into oc-dev
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 588a1bf..b60aed6 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -51,6 +51,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -347,7 +348,7 @@
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
try {
mResult.offer(intent, 5, TimeUnit.SECONDS);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0dfaf6a..e9ee1386 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -169,7 +169,8 @@
* 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);
+ public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
+ IBinder whitelistToken, long duration);
/**
* Allow DeviceIdleController to tell us about what apps are whitelisted.
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 68fce75..9552d17 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -562,8 +562,8 @@
void notifyLockedProfile(int userId);
void startConfirmDeviceCredentialIntent(in Intent intent, in Bundle options);
void sendIdleJobTrigger();
- int sendIntentSender(in IIntentSender target, int code, in Intent intent,
- in String resolvedType, in IIntentReceiver finishedReceiver,
+ int sendIntentSender(in IIntentSender target, in IBinder whitelistToken, int code,
+ in Intent intent, in String resolvedType, in IIntentReceiver finishedReceiver,
in String requiredPermission, in Bundle options);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 06509ae..4294eab 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -42,8 +42,10 @@
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.BadParcelableException;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -835,6 +837,22 @@
public ArraySet<PendingIntent> allPendingIntents;
/**
+ * Token identifying the notification that is applying doze/bgcheck whitelisting to the
+ * pending intents inside of it, so only those will get the behavior.
+ *
+ * @hide
+ */
+ static public IBinder whitelistToken;
+
+ /**
+ * Must be set by a process to start associating tokens with Notification objects
+ * coming in to it. This is set by NotificationManagerService.
+ *
+ * @hide
+ */
+ static public IBinder processWhitelistToken;
+
+ /**
* {@link #extras} key: this is the title of the notification,
* as supplied to {@link Builder#setContentTitle(CharSequence)}.
*/
@@ -1823,6 +1841,13 @@
{
int version = parcel.readInt();
+ whitelistToken = parcel.readStrongBinder();
+ if (whitelistToken == null) {
+ whitelistToken = processWhitelistToken;
+ }
+ // Propagate this token to all pending intents that are unmarshalled from the parcel.
+ parcel.setClassCookie(PendingIntent.class, whitelistToken);
+
when = parcel.readLong();
creationTime = parcel.readLong();
if (parcel.readInt() != 0) {
@@ -1929,6 +1954,7 @@
* @hide
*/
public void cloneInto(Notification that, boolean heavy) {
+ that.whitelistToken = this.whitelistToken;
that.when = this.when;
that.creationTime = this.creationTime;
that.mSmallIcon = this.mSmallIcon;
@@ -2158,6 +2184,7 @@
private void writeToParcelImpl(Parcel parcel, int flags) {
parcel.writeInt(1);
+ parcel.writeStrongBinder(whitelistToken);
parcel.writeLong(when);
parcel.writeLong(creationTime);
if (mSmallIcon == null && icon != 0) {
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index dc432af..11d9b5e 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -93,6 +93,7 @@
*/
public final class PendingIntent implements Parcelable {
private final IIntentSender mTarget;
+ private IBinder mWhitelistToken;
/** @hide */
@IntDef(flag = true,
@@ -656,7 +657,7 @@
*
*/
public IntentSender getIntentSender() {
- return new IntentSender(mTarget);
+ return new IntentSender(mTarget, mWhitelistToken);
}
/**
@@ -870,7 +871,7 @@
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
int res = ActivityManager.getService().sendIntentSender(
- mTarget, code, intent, resolvedType,
+ mTarget, mWhitelistToken, code, intent, resolvedType,
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
: null,
@@ -1085,7 +1086,9 @@
= new Parcelable.Creator<PendingIntent>() {
public PendingIntent createFromParcel(Parcel in) {
IBinder target = in.readStrongBinder();
- return target != null ? new PendingIntent(target) : null;
+ return target != null
+ ? new PendingIntent(target, in.getClassCookie(PendingIntent.class))
+ : null;
}
public PendingIntent[] newArray(int size) {
@@ -1108,31 +1111,39 @@
}
/**
- * Convenience function for reading either a Messenger or null pointer from
- * a Parcel. You must have previously written the Messenger with
+ * Convenience function for reading either a PendingIntent or null pointer from
+ * a Parcel. You must have previously written the PendingIntent with
* {@link #writePendingIntentOrNullToParcel}.
*
- * @param in The Parcel containing the written Messenger.
+ * @param in The Parcel containing the written PendingIntent.
*
- * @return Returns the Messenger read from the Parcel, or null if null had
+ * @return Returns the PendingIntent read from the Parcel, or null if null had
* been written.
*/
@Nullable
public static PendingIntent readPendingIntentOrNullFromParcel(@NonNull Parcel in) {
IBinder b = in.readStrongBinder();
- return b != null ? new PendingIntent(b) : null;
+ return b != null ? new PendingIntent(b, in.getClassCookie(PendingIntent.class)) : null;
}
/*package*/ PendingIntent(IIntentSender target) {
mTarget = target;
}
- /*package*/ PendingIntent(IBinder target) {
+ /*package*/ PendingIntent(IBinder target, Object cookie) {
mTarget = IIntentSender.Stub.asInterface(target);
+ if (cookie != null) {
+ mWhitelistToken = (IBinder)cookie;
+ }
}
/** @hide */
public IIntentSender getTarget() {
return mTarget;
}
+
+ /** @hide */
+ public IBinder getWhitelistToken() {
+ return mWhitelistToken;
+ }
}
diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl
index 45c62d4..21ea2fe 100644
--- a/core/java/android/content/IIntentSender.aidl
+++ b/core/java/android/content/IIntentSender.aidl
@@ -22,6 +22,6 @@
/** @hide */
oneway interface IIntentSender {
- void send(int code, in Intent intent, String resolvedType,
+ void send(int code, in Intent intent, String resolvedType, in IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, in Bundle options);
}
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 4adb5b7..0a456b5 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -56,6 +56,7 @@
*/
public class IntentSender implements Parcelable {
private final IIntentSender mTarget;
+ IBinder mWhitelistToken;
/**
* Exception thrown when trying to send through a PendingIntent that
@@ -187,7 +188,7 @@
String resolvedType = intent != null ?
intent.resolveTypeIfNeeded(context.getContentResolver())
: null;
- int res = ActivityManager.getService().sendIntentSender(mTarget,
+ int res = ActivityManager.getService().sendIntentSender(mTarget, mWhitelistToken,
code, intent, resolvedType,
onFinished != null
? new FinishedDispatcher(this, onFinished, handler)
@@ -365,6 +366,12 @@
}
/** @hide */
+ public IntentSender(IIntentSender target, IBinder whitelistToken) {
+ mTarget = target;
+ mWhitelistToken = whitelistToken;
+ }
+
+ /** @hide */
public IntentSender(IBinder target) {
mTarget = IIntentSender.Stub.asInterface(target);
}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 65025fb..8d85880 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -1548,6 +1548,7 @@
Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
+ p.adoptClassCookies(parcel);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index c5c743b..38a5395 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -204,6 +204,8 @@
private boolean mOwnsNativeParcelObject;
private long mNativeSize;
+ private ArrayMap<Class, Object> mClassCookies;
+
private RuntimeException mStack;
private static final int POOL_SIZE = 6;
@@ -497,6 +499,24 @@
return nativeCompareData(mNativePtr, other.mNativePtr);
}
+ /** @hide */
+ public final void setClassCookie(Class clz, Object cookie) {
+ if (mClassCookies == null) {
+ mClassCookies = new ArrayMap<>();
+ }
+ mClassCookies.put(clz, cookie);
+ }
+
+ /** @hide */
+ public final Object getClassCookie(Class clz) {
+ return mClassCookies != null ? mClassCookies.get(clz) : null;
+ }
+
+ /** @hide */
+ public final void adoptClassCookies(Parcel from) {
+ mClassCookies = from.mClassCookies;
+ }
+
/**
* Report whether the parcel contains any marshalled file descriptors.
*/
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7304c22..ee57952 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4598,7 +4598,7 @@
mAppSwitchesAllowedTime = 0;
}
}
- int ret = pir.sendInner(0, fillInIntent, resolvedType, null, null,
+ int ret = pir.sendInner(0, fillInIntent, resolvedType, null, null, null,
resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions, null);
return ret;
}
@@ -7504,11 +7504,12 @@
}
@Override
- public int sendIntentSender(IIntentSender target, int code, Intent intent, String resolvedType,
+ public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code,
+ Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
if (target instanceof PendingIntentRecord) {
return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,
- finishedReceiver, requiredPermission, options);
+ whitelistToken, finishedReceiver, requiredPermission, options);
} else {
if (intent == null) {
// Weird case: someone has given us their own custom IIntentSender, and now
@@ -7520,7 +7521,8 @@
intent = new Intent(Intent.ACTION_MAIN);
}
try {
- target.send(code, intent, resolvedType, null, requiredPermission, options);
+ target.send(code, intent, resolvedType, whitelistToken, null,
+ requiredPermission, options);
} catch (RemoteException e) {
}
// Platform code can rely on getting a result back when the send is done, but if
@@ -7817,11 +7819,15 @@
// be guarded by permission checking.
int getUidState(int uid) {
synchronized (this) {
- UidRecord uidRec = mActiveUids.get(uid);
- return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
+ return getUidStateLocked(uid);
}
}
+ int getUidStateLocked(int uid) {
+ UidRecord uidRec = mActiveUids.get(uid);
+ return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
+ }
+
@Override
public boolean isInMultiWindowMode(IBinder token) {
final long origId = Binder.clearCallingIdentity();
@@ -23642,12 +23648,13 @@
}
@Override
- public void setPendingIntentWhitelistDuration(IIntentSender target, long duration) {
+ public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken,
+ long duration) {
if (!(target instanceof PendingIntentRecord)) {
Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
return;
}
- ((PendingIntentRecord) target).setWhitelistDurationLocked(duration);
+ ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index dab122f..6eae9e6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -22,11 +22,8 @@
import android.app.IActivityContainer;
import android.app.IActivityController;
import android.app.IActivityManager;
-import android.app.IInstrumentationWatcher;
import android.app.IStopUserCallback;
-import android.app.Instrumentation;
import android.app.ProfilerInfo;
-import android.app.UiAutomationConnection;
import android.app.WaitResult;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
@@ -37,7 +34,6 @@
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.IPackageManager;
-import android.content.pm.InstrumentationInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8b7efff..4049500 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4731,7 +4731,7 @@
checkEmbeddedAllowedInner(userId, pendingIntent.key.requestIntent,
pendingIntent.key.requestResolvedType);
- return pendingIntent.sendInner(0, null, null, null, null, null, null, 0,
+ return pendingIntent.sendInner(0, null, null, null, null, null, null, null, 0,
FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this);
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 7ba67c5..6eca3fa 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -32,6 +32,7 @@
import android.os.RemoteException;
import android.os.TransactionTooLargeException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.TimeUtils;
@@ -51,7 +52,7 @@
final WeakReference<PendingIntentRecord> ref;
boolean sent = false;
boolean canceled = false;
- private long whitelistDuration = 0;
+ private ArrayMap<IBinder, Long> whitelistDuration;
private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
String stringName;
@@ -194,8 +195,19 @@
ref = new WeakReference<PendingIntentRecord>(this);
}
- void setWhitelistDurationLocked(long duration) {
- this.whitelistDuration = duration;
+ void setWhitelistDurationLocked(IBinder whitelistToken, long duration) {
+ if (duration > 0) {
+ if (whitelistDuration == null) {
+ whitelistDuration = new ArrayMap<>();
+ }
+ whitelistDuration.put(whitelistToken, duration);
+ } else if (whitelistDuration != null) {
+ whitelistDuration.remove(whitelistToken);
+ if (whitelistDuration.size() <= 0) {
+ whitelistDuration = null;
+ }
+
+ }
this.stringName = null;
}
@@ -219,19 +231,20 @@
return listeners;
}
- public void send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
- String requiredPermission, Bundle options) {
- sendInner(code, intent, resolvedType, finishedReceiver,
- requiredPermission, null, null, 0, 0, 0, options, null);
- }
-
- public int sendWithResult(int code, Intent intent, String resolvedType,
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- return sendInner(code, intent, resolvedType, finishedReceiver,
+ sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,
requiredPermission, null, null, 0, 0, 0, options, null);
}
- int sendInner(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
+ public int sendWithResult(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ return sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,
+ requiredPermission, null, null, 0, 0, 0, options, null);
+ }
+
+ int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+ IIntentReceiver finishedReceiver,
String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues, Bundle options, IActivityContainer container) {
if (intent != null) intent.setDefusable(true);
@@ -276,20 +289,29 @@
final long origId = Binder.clearCallingIdentity();
- if (whitelistDuration > 0) {
- StringBuilder tag = new StringBuilder(64);
- tag.append("pendingintent:");
- UserHandle.formatUid(tag, callingUid);
- tag.append(":");
- if (finalIntent.getAction() != null) {
- tag.append(finalIntent.getAction());
- } else if (finalIntent.getComponent() != null) {
- finalIntent.getComponent().appendShortString(tag);
- } else if (finalIntent.getData() != null) {
- tag.append(finalIntent.getData());
+ if (whitelistDuration != null) {
+ Long duration = whitelistDuration.get(whitelistToken);
+ if (duration != null) {
+ int procState = owner.getUidState(callingUid);
+ if (!ActivityManager.isProcStateBackground(procState)) {
+ StringBuilder tag = new StringBuilder(64);
+ tag.append("pendingintent:");
+ UserHandle.formatUid(tag, callingUid);
+ tag.append(":");
+ if (finalIntent.getAction() != null) {
+ tag.append(finalIntent.getAction());
+ } else if (finalIntent.getComponent() != null) {
+ finalIntent.getComponent().appendShortString(tag);
+ } else if (finalIntent.getData() != null) {
+ tag.append(finalIntent.getData());
+ }
+ owner.tempWhitelistForPendingIntentLocked(callingPid,
+ callingUid, uid, duration, tag.toString());
+ } else {
+ Slog.w(TAG, "Not doing whitelist " + this + ": caller state="
+ + procState);
+ }
}
- owner.tempWhitelistForPendingIntentLocked(callingPid,
- callingUid, uid, whitelistDuration, tag.toString());
}
boolean sendFinish = finishedReceiver != null;
@@ -425,10 +447,17 @@
pw.print(prefix); pw.print("sent="); pw.print(sent);
pw.print(" canceled="); pw.println(canceled);
}
- if (whitelistDuration != 0) {
+ if (whitelistDuration != null) {
pw.print(prefix);
pw.print("whitelistDuration=");
- TimeUtils.formatDuration(whitelistDuration, pw);
+ for (int i = 0; i < whitelistDuration.size(); i++) {
+ if (i != 0) {
+ pw.print(", ");
+ }
+ pw.print(Integer.toHexString(System.identityHashCode(whitelistDuration.keyAt(i))));
+ pw.print(":");
+ TimeUtils.formatDuration(whitelistDuration.valueAt(i), pw);
+ }
pw.println();
}
if (mCancelCallbacks != null) {
@@ -451,9 +480,16 @@
sb.append(key.packageName);
sb.append(' ');
sb.append(key.typeName());
- if (whitelistDuration > 0) {
+ if (whitelistDuration != null) {
sb.append( " (whitelist: ");
- TimeUtils.formatDuration(whitelistDuration, sb);
+ for (int i = 0; i < whitelistDuration.size(); i++) {
+ if (i != 0) {
+ sb.append(",");
+ }
+ sb.append(Integer.toHexString(System.identityHashCode(whitelistDuration.keyAt(i))));
+ sb.append(":");
+ TimeUtils.formatDuration(whitelistDuration.valueAt(i), sb);
+ }
sb.append(")");
}
sb.append('}');
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 598597b..da919ec 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -344,6 +344,7 @@
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
+ private static final IBinder WHITELIST_TOKEN = new Binder();
private RankingHandler mRankingHandler;
private long mLastOverRateLogTime;
private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -983,6 +984,7 @@
public NotificationManagerService(Context context) {
super(context);
+ Notification.processWhitelistToken = WHITELIST_TOKEN;
}
// TODO - replace these methods with a single VisibleForTesting constructor
@@ -3256,7 +3258,8 @@
for (int i = 0; i < intentCount; i++) {
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
- am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
+ am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
+ WHITELIST_TOKEN, duration);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c1d68b8..3e920d4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -48,6 +48,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.SystemProperties;
@@ -1686,7 +1687,7 @@
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
try {
mResult.offer(intent, 5, TimeUnit.SECONDS);