summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Felipe Leme <felipeal@google.com> 2019-08-13 09:28:33 -0700
committer Felipe Leme <felipeal@google.com> 2019-08-13 10:10:53 -0700
commite5434c301ba960ba11afa6552ca65799051705a5 (patch)
tree2c36aeed6b84423285a53e6ac1cb5cfd7d37fb58
parent1a919322f14cada8cb7f6da7d1b72daebc16c03e (diff)
Initial implementation of system service optimizations for different type of users.
On R, we want to optimize boot time by not starting system services for some types of users, like a headless system user (which is the case for Automotive) As a "guinea pig", it optimizes VoiceInteractionService for headless system user, so the 3rd-party app service is not bound for user 0 (and hence its process is not launched). This change improves boot time on Automotive in about 100ms. Test: atest CtsVoiceInteractionTestCases CtsAssistTestCases # on walleye and Automotive Test: manual verification on logcat Bug: 133242016 Fixes: 137878080 Change-Id: Ib0a902855e32691a1d00bfa77ee82c8e2430977c
-rw-r--r--core/java/android/content/pm/UserInfo.java45
-rw-r--r--core/java/android/os/UserManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/SystemService.java79
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java39
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java1
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java12
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java63
7 files changed, 220 insertions, 26 deletions
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 1c37c64bb622..fcdc81cf7533 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -16,11 +16,17 @@
package android.content.pm;
+import android.annotation.IntDef;
import android.annotation.UnsupportedAppUsage;
+import android.annotation.UserIdInt;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DebugUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Per-user information.
@@ -119,10 +125,31 @@ public class UserInfo implements Parcelable {
*/
public static final int FLAG_SYSTEM = 0x00000800;
+ /**
+ * @hide
+ */
+ @IntDef(flag = true, prefix = "FLAG_", value = {
+ FLAG_PRIMARY,
+ FLAG_ADMIN,
+ FLAG_GUEST,
+ FLAG_RESTRICTED,
+ FLAG_INITIALIZED,
+ FLAG_MANAGED_PROFILE,
+ FLAG_DISABLED,
+ FLAG_QUIET_MODE,
+ FLAG_EPHEMERAL,
+ FLAG_DEMO,
+ FLAG_FULL,
+ FLAG_SYSTEM
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserInfoFlag {
+ }
+
public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
@UnsupportedAppUsage
- public int id;
+ public @UserIdInt int id;
@UnsupportedAppUsage
public int serialNumber;
@UnsupportedAppUsage
@@ -130,7 +157,7 @@ public class UserInfo implements Parcelable {
@UnsupportedAppUsage
public String iconPath;
@UnsupportedAppUsage
- public int flags;
+ public @UserInfoFlag int flags;
@UnsupportedAppUsage
public long creationTime;
@UnsupportedAppUsage
@@ -214,6 +241,10 @@ public class UserInfo implements Parcelable {
return (flags & FLAG_DEMO) == FLAG_DEMO;
}
+ public boolean isFull() {
+ return (flags & FLAG_FULL) == FLAG_FULL;
+ }
+
/**
* Returns true if the user is a split system user.
* <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
@@ -290,13 +321,23 @@ public class UserInfo implements Parcelable {
@Override
public String toString() {
+ // NOTE: do not change this string, it's used by 'pm list users', which in turn is
+ // used and parsed by TestDevice. In other words, if you change it, you'd have to change
+ // TestDevice, TestDeviceTest, and possibly others....
return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
}
+ /** @hide */
+ public static String flagsToString(int flags) {
+ return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
+ }
+
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeInt(id);
dest.writeString(name);
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 2e3b000f2b39..f302263f23ce 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -16,6 +16,7 @@
package android.os;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
@@ -224,4 +225,10 @@ public abstract class UserManagerInternal {
/** @return a specific user restriction that's in effect currently. */
public abstract boolean hasUserRestriction(String restriction, int userId);
+
+ /**
+ * Gets an {@link UserInfo} for the given {@code userId}, or {@code null} if not
+ * found.
+ */
+ public abstract @Nullable UserInfo getUserInfo(@UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 8e9e74ecedbf..4facf4ea62a2 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -18,12 +18,17 @@ package com.android.server;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.ServiceManager;
import android.os.UserManager;
+import com.android.server.pm.UserManagerService;
+
/**
* The base class for services running in the system process. Override and implement
* the lifecycle event callback methods as needed.
@@ -145,11 +150,29 @@ public abstract class SystemService {
public void onBootPhase(int phase) {}
/**
+ * @deprecated subclasses should extend {@link #onStartUser(int, int)} instead (which by default
+ * calls this method).
+ */
+ @Deprecated
+ public void onStartUser(@UserIdInt int userHandle) {}
+
+ /**
* Called when a new user is starting, for system services to initialize any per-user
* state they maintain for running users.
- * @param userHandle The identifier of the user.
+ *
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
+ */
+ public void onStartUser(@NonNull UserInfo userInfo) {
+ onStartUser(userInfo.id);
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onUnlockUser(int, int)} instead (which by
+ * default calls this method).
*/
- public void onStartUser(int userHandle) {}
+ @Deprecated
+ public void onUnlockUser(@UserIdInt int userHandle) {}
/**
* Called when an existing user is in the process of being unlocked. This
@@ -163,17 +186,38 @@ public abstract class SystemService {
* {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
* these states.
*
- * @param userHandle The identifier of the user.
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
*/
- public void onUnlockUser(int userHandle) {}
+ public void onUnlockUser(@NonNull UserInfo userInfo) {
+ onUnlockUser(userInfo.id);
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onSwitchUser(int, int)} instead (which by
+ * default calls this method).
+ */
+ @Deprecated
+ public void onSwitchUser(@UserIdInt int userHandle) {}
/**
* Called when switching to a different foreground user, for system services that have
* special behavior for whichever user is currently in the foreground. This is called
* before any application processes are aware of the new user.
- * @param userHandle The identifier of the user.
+ *
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
+ */
+ public void onSwitchUser(@NonNull UserInfo userInfo) {
+ onSwitchUser(userInfo.id);
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onStopUser(int, int)} instead (which by default
+ * calls this method).
*/
- public void onSwitchUser(int userHandle) {}
+ @Deprecated
+ public void onStopUser(@UserIdInt int userHandle) {}
/**
* Called when an existing user is stopping, for system services to finalize any per-user
@@ -183,9 +227,19 @@ public abstract class SystemService {
*
* <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
*
- * @param userHandle The identifier of the user.
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
*/
- public void onStopUser(int userHandle) {}
+ public void onStopUser(@NonNull UserInfo userInfo) {
+ onStopUser(userInfo.id);
+ }
+
+ /**
+ * @deprecated subclasses should extend {@link #onCleanupUser(int, int)} instead (which by
+ * default calls this method).
+ */
+ @Deprecated
+ public void onCleanupUser(@UserIdInt int userHandle) {}
/**
* Called when an existing user is stopping, for system services to finalize any per-user
@@ -193,11 +247,14 @@ public abstract class SystemService {
* teardown of the user is complete.
*
* <p>NOTE: When this callback is called, the CE storage for the target user may not be
- * accessible already. Use {@link #onStopUser} instead if you need to access the CE storage.
+ * accessible already. Use {@link #onCleanupUser} instead if you need to access the CE storage.
*
- * @param userHandle The identifier of the user.
+ * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+ * referenced by {@link UserManagerService} and hence should not be modified.
*/
- public void onCleanupUser(int userHandle) {}
+ public void onCleanupUser(@NonNull UserInfo userInfo) {
+ onCleanupUser(userInfo.id);
+ }
/**
* Publish the service so it is accessible to other services and apps.
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 9711152ec5b1..ebe23f61cb2a 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -19,9 +19,11 @@ package com.android.server;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.UserManagerInternal;
import android.util.Slog;
import com.android.server.utils.TimingsTraceAndSlog;
@@ -53,6 +55,8 @@ public class SystemServiceManager {
private int mCurrentPhase = -1;
+ private UserManagerInternal mUserManagerInternal;
+
SystemServiceManager(Context context) {
mContext = context;
}
@@ -187,11 +191,30 @@ public class SystemServiceManager {
}
/**
+ * Called at the beginning of {@code ActivityManagerService.systemReady()}.
+ */
+ public void preSystemReady() {
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ }
+
+ private @NonNull UserInfo getUserInfo(@UserIdInt int userHandle) {
+ if (mUserManagerInternal == null) {
+ throw new IllegalStateException("mUserManagerInternal not set yet");
+ }
+ final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle);
+ if (userInfo == null) {
+ throw new IllegalStateException("No UserInfo for " + userHandle);
+ }
+ return userInfo;
+ }
+
+ /**
* Starts the given user.
*/
- public void startUser(@NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
+ public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
t.traceBegin("ssm.startUser-" + userHandle);
Slog.i(TAG, "Calling onStartUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -199,7 +222,7 @@ public class SystemServiceManager {
t.traceBegin("onStartUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onStartUser(userHandle);
+ service.onStartUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting start of user " + userHandle
+ " to service " + service.getClass().getName(), ex);
@@ -217,6 +240,7 @@ public class SystemServiceManager {
final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
t.traceBegin("ssm.unlockUser-" + userHandle);
Slog.i(TAG, "Calling onUnlockUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -224,7 +248,7 @@ public class SystemServiceManager {
t.traceBegin("onUnlockUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onUnlockUser(userHandle);
+ service.onUnlockUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting unlock of user " + userHandle
+ " to service " + serviceName, ex);
@@ -242,6 +266,7 @@ public class SystemServiceManager {
final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
t.traceBegin("ssm.switchUser-" + userHandle);
Slog.i(TAG, "Calling switchUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -249,7 +274,7 @@ public class SystemServiceManager {
t.traceBegin("onSwitchUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onSwitchUser(userHandle);
+ service.onSwitchUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
+ " to service " + serviceName, ex);
@@ -267,6 +292,7 @@ public class SystemServiceManager {
final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
t.traceBegin("ssm.stopUser-" + userHandle);
Slog.i(TAG, "Calling onStopUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -274,7 +300,7 @@ public class SystemServiceManager {
t.traceBegin("onStopUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onStopUser(userHandle);
+ service.onStopUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
+ " to service " + serviceName, ex);
@@ -292,6 +318,7 @@ public class SystemServiceManager {
final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
t.traceBegin("ssm.cleanupUser-" + userHandle);
Slog.i(TAG, "Calling onCleanupUser u" + userHandle);
+ final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -299,7 +326,7 @@ public class SystemServiceManager {
t.traceBegin("onCleanupUser-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onCleanupUser(userHandle);
+ service.onCleanupUser(userInfo);
} catch (Exception ex) {
Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+ " to service " + serviceName, ex);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ede573a64c65..4f2cddd4ebfd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8990,6 +8990,7 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
t.traceBegin("PhaseActivityManagerReady");
+ mSystemServiceManager.preSystemReady();
synchronized(this) {
if (mSystemReady) {
// If we're done calling all the receivers, run the next "boot phase" passed in
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2ccb6c172a28..ead9d7ac2e56 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3762,6 +3762,8 @@ public class UserManagerService extends IUserManager.Stub {
pw.print(" <partial>");
}
pw.println();
+ pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" (");
+ pw.print(UserInfo.flagsToString(userInfo.flags)); pw.println(")");
pw.print(" State: ");
final int state;
synchronized (mUserStates) {
@@ -3846,6 +3848,8 @@ public class UserManagerService extends IUserManager.Stub {
pw.println(" All guests ephemeral: " + Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_guestUserEphemeral));
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
+ pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
+ pw.println(" User version: " + mUserVersion);
}
private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
@@ -4172,6 +4176,14 @@ public class UserManagerService extends IUserManager.Stub {
Bundle restrictions = getEffectiveUserRestrictions(userId);
return restrictions != null && restrictions.getBoolean(restrictionKey);
}
+
+ public @Nullable UserInfo getUserInfo(@UserIdInt int userId) {
+ UserData userData;
+ synchronized (mUsersLock) {
+ userData = mUsers.get(userId);
+ }
+ return userData == null ? null : userData.info;
+ }
}
/* Remove all the users except of the system one. */
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index b2fde548e506..b2ac5490440d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
@@ -36,6 +37,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
@@ -80,6 +82,7 @@ import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.soundtrigger.SoundTriggerInternal;
+import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
@@ -92,7 +95,7 @@ import java.util.concurrent.Executor;
*/
public class VoiceInteractionManagerService extends SystemService {
static final String TAG = "VoiceInteractionManagerService";
- static final boolean DEBUG = false;
+ static final boolean DEBUG = true; // TODO(b/133242016) STOPSHIP: change to false before R ships
final Context mContext;
final ContentResolver mResolver;
@@ -154,19 +157,37 @@ public class VoiceInteractionManagerService extends SystemService {
}
@Override
- public void onStartUser(int userHandle) {
- mServiceStub.initForUser(userHandle);
+ public void onStartUser(@NonNull UserInfo userInfo) {
+ if (DEBUG) Slog.d(TAG, "onStartUser(" + userInfo + ")");
+
+ if (!userInfo.isFull()) {
+ if (DEBUG) Slog.d(TAG, "***** skipping on non-full user " + userInfo);
+ return;
+ }
+ mServiceStub.initForUser(userInfo.id);
}
@Override
- public void onUnlockUser(int userHandle) {
- mServiceStub.initForUser(userHandle);
+ public void onUnlockUser(@NonNull UserInfo userInfo) {
+ if (DEBUG) Slog.d(TAG, "onUnlockUser(" + userInfo + ")");
+
+ if (!userInfo.isFull()) {
+ if (DEBUG) Slog.d(TAG, "***** skipping on non-full user " + userInfo);
+ return;
+ }
+ mServiceStub.initForUser(userInfo.id);
mServiceStub.switchImplementationIfNeeded(false);
}
@Override
- public void onSwitchUser(int userHandle) {
- mServiceStub.switchUser(userHandle);
+ public void onSwitchUser(@NonNull UserInfo userInfo) {
+ if (DEBUG) Slog.d(TAG, "onSwitchUser(" + userInfo + ")");
+
+ if (!userInfo.isFull()) {
+ if (DEBUG) Slog.d(TAG, "***** skipping on non-full user " + userInfo);
+ return;
+ }
+ mServiceStub.switchUser(userInfo.id);
}
class LocalService extends VoiceInteractionManagerInternal {
@@ -270,6 +291,20 @@ public class VoiceInteractionManagerService extends SystemService {
}
public void initForUser(int userHandle) {
+ final TimingsTraceAndSlog t;
+ if (DEBUG) {
+ t = TimingsTraceAndSlog.newAsyncLog();
+ t.traceBegin("VoiceInteractionSvc.initForUser(" + userHandle + ")");
+ } else {
+ t = null;
+ }
+ initForUserNoTracing(userHandle);
+ if (t != null) {
+ t.traceEnd();
+ }
+ }
+
+ private void initForUserNoTracing(@UserIdInt int userHandle) {
if (DEBUG) Slog.d(TAG, "**************** initForUser user=" + userHandle);
String curInteractorStr = Settings.Secure.getStringForUser(
mContext.getContentResolver(),
@@ -426,6 +461,20 @@ public class VoiceInteractionManagerService extends SystemService {
}
void switchImplementationIfNeededLocked(boolean force) {
+ final TimingsTraceAndSlog t;
+ if (DEBUG) {
+ t = TimingsTraceAndSlog.newAsyncLog();
+ t.traceBegin("VoiceInteractionSvc.switchImplementation(" + mCurUser + ")");
+ } else {
+ t = null;
+ }
+ switchImplementationIfNeededNoTracingLocked(force);
+ if (t != null) {
+ t.traceEnd();
+ }
+ }
+
+ void switchImplementationIfNeededNoTracingLocked(boolean force) {
if (!mSafeMode) {
String curService = Settings.Secure.getStringForUser(
mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);