package com.android.commands.pm;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import android.accounts.IAccountManager;
import android.app.ActivityManager;
flags |= UserInfo.FLAG_MANAGED_PROFILE;
} else if ("--restricted".equals(opt)) {
flags |= UserInfo.FLAG_RESTRICTED;
+ } else if ("--ephemeral".equals(opt)) {
+ flags |= UserInfo.FLAG_EPHEMERAL;
+ } else if ("--guest".equals(opt)) {
+ flags |= UserInfo.FLAG_GUEST;
} else {
System.err.println("Error: unknown option " + opt);
return showUsage();
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
System.err.println(" pm trim-caches DESIRED_FREE_SPACE [internal|UUID]");
- System.err.println(" pm create-user [--profileOf USER_ID] [--managed] [--restricted] USER_NAME");
+ System.err.println(" pm create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral] [--guest] USER_NAME");
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
public static final int FLAG_QUIET_MODE = 0x00000080;
+ /**
+ * Indicates that this user is ephemeral. I.e. the user will be removed after leaving
+ * the foreground.
+ */
+ public static final int FLAG_EPHEMERAL = 0x00000100;
+
public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
public int id;
public boolean isQuietModeEnabled() {
return (flags & FLAG_QUIET_MODE) == FLAG_QUIET_MODE;
}
+
+ public boolean isEphemeral() {
+ return (flags & FLAG_EPHEMERAL) == FLAG_EPHEMERAL;
+ }
+
/**
* Returns true if the user is a split system user.
* <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
}
/**
+ * Checks if the calling app is running as an ephemeral user.
+ *
+ * @return whether the caller is an ephemeral user.
+ * @hide
+ */
+ public boolean isEphemeralUser() {
+ return isUserEphemeral(UserHandle.myUserId());
+ }
+
+ /**
+ * Returns whether the specified user is ephemeral.
+ * @hide
+ */
+ public boolean isUserEphemeral(int userId) {
+ final UserInfo user = getUserInfo(userId);
+ return user != null && user.isEphemeral();
+ }
+
+ /**
* Return whether the given user is actively running. This means that
* the user is in the "started" state, not "stopped" -- it is currently
* allowed to run code through scheduled alarms, receiving broadcasts,
import android.os.Parcelable;
import android.os.RemoteException;
-import java.io.FileDescriptor;
-
/**
* WARNING! Update IMountService.h and IMountService.cpp if you change this
* file. In particular, the ordering of the methods below must match the
}
@Override
- public void createUserKey(int userId, int serialNumber) throws RemoteException {
+ public void createUserKey(int userId, int serialNumber, boolean ephemeral)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(userId);
_data.writeInt(serialNumber);
+ _data.writeInt(ephemeral ? 1 : 0);
mRemote.transact(Stub.TRANSACTION_createUserKey, _data, _reply, 0);
_reply.readException();
} finally {
}
@Override
- public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+ public void prepareUserStorage(
+ String volumeUuid, int userId, int serialNumber, boolean ephemeral)
throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
_data.writeString(volumeUuid);
_data.writeInt(userId);
_data.writeInt(serialNumber);
+ _data.writeInt(ephemeral ? 1 : 0);
mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0);
_reply.readException();
} finally {
data.enforceInterface(DESCRIPTOR);
int userId = data.readInt();
int serialNumber = data.readInt();
- createUserKey(userId, serialNumber);
+ boolean ephemeral = data.readInt() != 0;
+ createUserKey(userId, serialNumber, ephemeral);
reply.writeNoException();
return true;
}
String volumeUuid = data.readString();
int userId = data.readInt();
int serialNumber = data.readInt();
- prepareUserStorage(volumeUuid, userId, serialNumber);
+ boolean ephemeral = data.readInt() != 0;
+ prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
reply.writeNoException();
return true;
}
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
throws RemoteException;
- public void createUserKey(int userId, int serialNumber) throws RemoteException;
+ public void createUserKey(int userId, int serialNumber, boolean ephemeral)
+ throws RemoteException;
public void destroyUserKey(int userId) throws RemoteException;
public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
public void lockUserKey(int userId) throws RemoteException;
public boolean isUserKeyUnlocked(int userId) throws RemoteException;
- public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
- throws RemoteException;
+ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber,
+ boolean ephemeral) throws RemoteException;
public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException;
}
}
/** {@hide} */
- public void createUserKey(int userId, int serialNumber) {
+ public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
try {
- mMountService.createUserKey(userId, serialNumber);
+ mMountService.createUserKey(userId, serialNumber, ephemeral);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/** {@hide} */
- public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+ public void prepareUserStorage(
+ String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
try {
- mMountService.prepareUserStorage(volumeUuid, userId, serialNumber);
+ mMountService.prepareUserStorage(volumeUuid, userId, serialNumber, ephemeral);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public void createUserKey(int userId, int serialNumber) {
+ public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
try {
- mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber);
+ mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber,
+ ephemeral ? 1 : 0);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
}
@Override
- public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+ public void prepareUserStorage(
+ String volumeUuid, int userId, int serialNumber, boolean ephemeral) {
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
waitForReady();
try {
mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid),
- userId, serialNumber);
+ userId, serialNumber, ephemeral ? 1 : 0);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
if (userDir.exists()) continue;
try {
- sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber);
+ sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, user.isEphemeral());
UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
} catch (IOException e) {
Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
}
void systemReady() {
- // Prune out any partially created/partially removed users.
+ // Prune out any partially created, partially removed and ephemeral users.
ArrayList<UserInfo> partials = new ArrayList<>();
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i);
- if ((ui.partial || ui.guestToRemove) && i != 0) {
+ if ((ui.partial || ui.guestToRemove || ui.isEphemeral()) && i != 0) {
partials.add(ui);
}
}
}
}
}
+
+ if (parent != null && parent.isEphemeral()) {
+ flags |= UserInfo.FLAG_EPHEMERAL;
+ }
userId = getNextAvailableId();
userInfo = new UserInfo(userId, name, null, flags);
userInfo.serialNumber = mNextSerialNumber++;
}
}
final StorageManager storage = mContext.getSystemService(StorageManager.class);
- storage.createUserKey(userId, userInfo.serialNumber);
+ storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
try {
final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
- storage.prepareUserStorage(volumeUuid, userId, userInfo.serialNumber);
+ storage.prepareUserStorage(
+ volumeUuid, userId, userInfo.serialNumber, userInfo.isEphemeral());
enforceSerialNumber(userDir, userInfo.serialNumber);
} catch (IOException e) {
Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);