Skip to content

Commit 80a4af2

Browse files
author
Dianne Hackborn
committed
Start implementing concept of "running" users.
The activity manager now keeps track of which users are running. Initially, only user 0 is running. When you switch to another user, that user is started so it is running. It is only at this point that BOOT_COMPLETED is sent for that user and it is allowed to execute anything. You can stop any user except user 0, which brings it back to the same state as when you first boot the device. This is also used to be able to more cleaning delete a user, by first stopping it before removing its data. There is a new broadcast ACTION_USER_STOPPED sent when a user is stopped; system services need to handle this like they currently handle ACTION_PACKAGE_RESTARTED when individual packages are restarted. Change-Id: I89adbd7cbaf4a0bb72ea201385f93477f40a4119
1 parent def8b0f commit 80a4af2

File tree

16 files changed

+536
-104
lines changed

16 files changed

+536
-104
lines changed

Android.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ LOCAL_SRC_FILES += \
7474
core/java/android/app/ISearchManager.aidl \
7575
core/java/android/app/ISearchManagerCallback.aidl \
7676
core/java/android/app/IServiceConnection.aidl \
77+
core/java/android/app/IStopUserCallback.aidl \
7778
core/java/android/app/IThumbnailReceiver.aidl \
7879
core/java/android/app/IThumbnailRetriever.aidl \
7980
core/java/android/app/ITransientNotification.aidl \

cmds/am/src/com/android/commands/am/Am.java

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import android.content.pm.IPackageManager;
3232
import android.content.pm.ResolveInfo;
3333
import android.net.Uri;
34-
import android.os.Binder;
3534
import android.os.Bundle;
3635
import android.os.ParcelFileDescriptor;
3736
import android.os.RemoteException;
@@ -141,6 +140,8 @@ private void run(String[] args) throws Exception {
141140
runToUri(true);
142141
} else if (op.equals("switch-user")) {
143142
runSwitchUser();
143+
} else if (op.equals("stop-user")) {
144+
runStopUser();
144145
} else {
145146
throw new IllegalArgumentException("Unknown command: " + op);
146147
}
@@ -323,7 +324,6 @@ private Intent makeIntent() throws URISyntaxException {
323324
mUserId = Integer.parseInt(nextArgRequired());
324325
} else {
325326
System.err.println("Error: Unknown option: " + opt);
326-
showUsage();
327327
return null;
328328
}
329329
}
@@ -594,7 +594,6 @@ private void runInstrument() throws Exception {
594594
no_window_animation = true;
595595
} else {
596596
System.err.println("Error: Unknown option: " + opt);
597-
showUsage();
598597
return;
599598
}
600599
}
@@ -738,7 +737,6 @@ private void runSetDebugApp() throws Exception {
738737
persistent = true;
739738
} else {
740739
System.err.println("Error: Unknown option: " + opt);
741-
showUsage();
742740
return;
743741
}
744742
}
@@ -752,13 +750,27 @@ private void runClearDebugApp() throws Exception {
752750
}
753751

754752
private void runSwitchUser() throws Exception {
755-
if (android.os.Process.myUid() != 0) {
756-
throw new RuntimeException("switchuser can only be run as root");
757-
}
758753
String user = nextArgRequired();
759754
mAm.switchUser(Integer.parseInt(user));
760755
}
761756

757+
private void runStopUser() throws Exception {
758+
String user = nextArgRequired();
759+
int res = mAm.stopUser(Integer.parseInt(user), null);
760+
if (res != ActivityManager.USER_OP_SUCCESS) {
761+
String txt = "";
762+
switch (res) {
763+
case ActivityManager.USER_OP_IS_CURRENT:
764+
txt = " (Can't stop current user)";
765+
break;
766+
case ActivityManager.USER_OP_UNKNOWN_USER:
767+
txt = " (Unknown user " + user + ")";
768+
break;
769+
}
770+
System.err.println("Switch failed: " + res + txt);
771+
}
772+
}
773+
762774
class MyActivityController extends IActivityController.Stub {
763775
final String mGdbPort;
764776

@@ -1047,7 +1059,6 @@ private void runMonitor() throws Exception {
10471059
gdbPort = nextArgRequired();
10481060
} else {
10491061
System.err.println("Error: Unknown option: " + opt);
1050-
showUsage();
10511062
return;
10521063
}
10531064
}
@@ -1065,7 +1076,6 @@ private void runScreenCompat() throws Exception {
10651076
enabled = false;
10661077
} else {
10671078
System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode);
1068-
showUsage();
10691079
return;
10701080
}
10711081

@@ -1090,7 +1100,6 @@ private void runDisplaySize() throws Exception {
10901100
int div = size.indexOf('x');
10911101
if (div <= 0 || div >= (size.length()-1)) {
10921102
System.err.println("Error: bad size " + size);
1093-
showUsage();
10941103
return;
10951104
}
10961105
String mstr = size.substring(0, div);
@@ -1100,7 +1109,6 @@ private void runDisplaySize() throws Exception {
11001109
n = Integer.parseInt(nstr);
11011110
} catch (NumberFormatException e) {
11021111
System.err.println("Error: bad number " + e);
1103-
showUsage();
11041112
return;
11051113
}
11061114
}
@@ -1139,12 +1147,10 @@ private void runDisplayDensity() throws Exception {
11391147
density = Integer.parseInt(densityStr);
11401148
} catch (NumberFormatException e) {
11411149
System.err.println("Error: bad number " + e);
1142-
showUsage();
11431150
return;
11441151
}
11451152
if (density < 72) {
11461153
System.err.println("Error: density must be >= 72");
1147-
showUsage();
11481154
return;
11491155
}
11501156
}
@@ -1345,6 +1351,7 @@ private static void showUsage() {
13451351
" am to-uri [INTENT]\n" +
13461352
" am to-intent-uri [INTENT]\n" +
13471353
" am switch-user <USER_ID>\n" +
1354+
" am stop-user <USER_ID>\n" +
13481355
"\n" +
13491356
"am start: start an Activity. Options are:\n" +
13501357
" -D: enable debugging\n" +
@@ -1403,6 +1410,12 @@ private static void showUsage() {
14031410
"\n" +
14041411
"am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
14051412
"\n" +
1413+
"am switch-user: switch to put USER_ID in the foreground, starting" +
1414+
" execution of that user if it is currently stopped.\n" +
1415+
"\n" +
1416+
"am stop-user: stop execution of USER_ID, not allowing it to run any" +
1417+
" code until a later explicit switch to it.\n" +
1418+
"\n" +
14061419
"<INTENT> specifications include these flags and arguments:\n" +
14071420
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
14081421
" [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +

core/java/android/app/ActivityManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,15 @@ public class ActivityManager {
210210
*/
211211
public static final int INTENT_SENDER_SERVICE = 4;
212212

213+
/** @hide User operation call: success! */
214+
public static final int USER_OP_SUCCESS = 0;
215+
216+
/** @hide User operation call: given user id is not known. */
217+
public static final int USER_OP_UNKNOWN_USER = -1;
218+
219+
/** @hide User operation call: given user id is the current user, can't be stopped. */
220+
public static final int USER_OP_IS_CURRENT = -2;
221+
213222
/*package*/ ActivityManager(Context context, Handler handler) {
214223
mContext = context;
215224
mHandler = handler;

core/java/android/app/ActivityManagerNative.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,17 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
15701570
return true;
15711571
}
15721572

1573+
case STOP_USER_TRANSACTION: {
1574+
data.enforceInterface(IActivityManager.descriptor);
1575+
int userid = data.readInt();
1576+
IStopUserCallback callback = IStopUserCallback.Stub.asInterface(
1577+
data.readStrongBinder());
1578+
int result = stopUser(userid, callback);
1579+
reply.writeNoException();
1580+
reply.writeInt(result);
1581+
return true;
1582+
}
1583+
15731584
case GET_CURRENT_USER_TRANSACTION: {
15741585
data.enforceInterface(IActivityManager.descriptor);
15751586
UserInfo userInfo = getCurrentUser();
@@ -3756,11 +3767,25 @@ public boolean switchUser(int userid) throws RemoteException {
37563767
return result;
37573768
}
37583769

3770+
public int stopUser(int userid, IStopUserCallback callback) throws RemoteException {
3771+
Parcel data = Parcel.obtain();
3772+
Parcel reply = Parcel.obtain();
3773+
data.writeInterfaceToken(IActivityManager.descriptor);
3774+
data.writeInt(userid);
3775+
data.writeStrongInterface(callback);
3776+
mRemote.transact(STOP_USER_TRANSACTION, data, reply, 0);
3777+
reply.readException();
3778+
int result = reply.readInt();
3779+
reply.recycle();
3780+
data.recycle();
3781+
return result;
3782+
}
3783+
37593784
public UserInfo getCurrentUser() throws RemoteException {
37603785
Parcel data = Parcel.obtain();
37613786
Parcel reply = Parcel.obtain();
37623787
data.writeInterfaceToken(IActivityManager.descriptor);
3763-
mRemote.transact(SWITCH_USER_TRANSACTION, data, reply, 0);
3788+
mRemote.transact(GET_CURRENT_USER_TRANSACTION, data, reply, 0);
37643789
reply.readException();
37653790
UserInfo userInfo = UserInfo.CREATOR.createFromParcel(reply);
37663791
reply.recycle();

core/java/android/app/IActivityManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ public void setPackageAskScreenCompat(String packageName, boolean ask)
331331

332332
// Multi-user APIs
333333
public boolean switchUser(int userid) throws RemoteException;
334+
public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
334335
public UserInfo getCurrentUser() throws RemoteException;
335336

336337
public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
@@ -611,4 +612,5 @@ private WaitResult(Parcel source) {
611612
int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+150;
612613
int IS_INTENT_SENDER_AN_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+151;
613614
int START_ACTIVITY_AS_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+152;
615+
int STOP_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+153;
614616
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
** Copyright 2012, The Android Open Source Project
3+
**
4+
** Licensed under the Apache License, Version 2.0 (the "License");
5+
** you may not use this file except in compliance with the License.
6+
** You may obtain a copy of the License at
7+
**
8+
** http://www.apache.org/licenses/LICENSE-2.0
9+
**
10+
** Unless required by applicable law or agreed to in writing, software
11+
** distributed under the License is distributed on an "AS IS" BASIS,
12+
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
** See the License for the specific language governing permissions and
14+
** limitations under the License.
15+
*/
16+
17+
package android.app;
18+
19+
/**
20+
* Callback to find out when we have finished stopping a user.
21+
* {@hide}
22+
*/
23+
interface IStopUserCallback
24+
{
25+
void userStopped(int userId);
26+
void userStopAborted(int userId);
27+
}

core/java/android/content/Intent.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import android.content.res.Resources;
2828
import android.content.res.TypedArray;
2929
import android.graphics.Rect;
30-
import android.media.RemoteControlClient;
3130
import android.net.Uri;
3231
import android.os.Bundle;
3332
import android.os.IBinder;
@@ -2286,6 +2285,15 @@ public static Intent createChooser(Intent target, CharSequence title) {
22862285
public static final String ACTION_USER_ADDED =
22872286
"android.intent.action.USER_ADDED";
22882287

2288+
/**
2289+
* Broadcast sent to the system when a user is stopped. Carries an extra EXTRA_USER_HANDLE that has
2290+
* the userHandle of the user. This is similar to {@link #ACTION_PACKAGE_RESTARTED},
2291+
* but for an entire user instead of a specific package.
2292+
* @hide
2293+
*/
2294+
public static final String ACTION_USER_STOPPED =
2295+
"android.intent.action.USER_STOPPED";
2296+
22892297
/**
22902298
* Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
22912299
* the userHandle of the user.

core/java/com/android/internal/content/PackageMonitor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
4949
sPackageFilt.addAction(Intent.ACTION_UID_REMOVED);
5050
sPackageFilt.addDataScheme("package");
5151
sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
52+
sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
5253
sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
5354
sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
5455
}
@@ -136,6 +137,9 @@ public void onPackageChanged(String packageName, int uid, String[] components) {
136137
public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
137138
return false;
138139
}
140+
141+
public void onHandleUserStop(Intent intent, int userHandle) {
142+
}
139143

140144
public void onUidRemoved(int uid) {
141145
}
@@ -307,6 +311,10 @@ public void onReceive(Context context, Intent intent) {
307311
intent.getIntExtra(Intent.EXTRA_UID, 0), true);
308312
} else if (Intent.ACTION_UID_REMOVED.equals(action)) {
309313
onUidRemoved(intent.getIntExtra(Intent.EXTRA_UID, 0));
314+
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
315+
if (intent.hasExtra(Intent.EXTRA_USER_HANDLE)) {
316+
onHandleUserStop(intent, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
317+
}
310318
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
311319
String[] pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
312320
mAppearingPackages = pkgList;

core/res/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
6363
<protected-broadcast android:name="android.intent.action.USER_ADDED" />
6464
<protected-broadcast android:name="android.intent.action.USER_REMOVED" />
65+
<protected-broadcast android:name="android.intent.action.USER_STOPPED" />
6566
<protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
6667

6768
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />

services/java/com/android/server/AlarmManagerService.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import android.os.PowerManager;
3535
import android.os.SystemClock;
3636
import android.os.SystemProperties;
37+
import android.os.UserHandle;
3738
import android.os.WorkSource;
3839
import android.text.TextUtils;
3940
import android.text.format.Time;
@@ -303,7 +304,7 @@ private void removeLocked(ArrayList<Alarm> alarmList,
303304
}
304305
}
305306
}
306-
307+
307308
public void removeLocked(String packageName) {
308309
removeLocked(mRtcWakeupAlarms, packageName);
309310
removeLocked(mRtcAlarms, packageName);
@@ -327,6 +328,29 @@ private void removeLocked(ArrayList<Alarm> alarmList,
327328
}
328329
}
329330
}
331+
332+
public void removeUserLocked(int userHandle) {
333+
removeUserLocked(mRtcWakeupAlarms, userHandle);
334+
removeUserLocked(mRtcAlarms, userHandle);
335+
removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle);
336+
removeUserLocked(mElapsedRealtimeAlarms, userHandle);
337+
}
338+
339+
private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) {
340+
if (alarmList.size() <= 0) {
341+
return;
342+
}
343+
344+
// iterator over the list removing any it where the intent match
345+
Iterator<Alarm> it = alarmList.iterator();
346+
347+
while (it.hasNext()) {
348+
Alarm alarm = it.next();
349+
if (UserHandle.getUserId(alarm.operation.getTargetUid()) == userHandle) {
350+
it.remove();
351+
}
352+
}
353+
}
330354

331355
public boolean lookForPackageLocked(String packageName) {
332356
return lookForPackageLocked(mRtcWakeupAlarms, packageName)
@@ -822,6 +846,7 @@ public UninstallReceiver() {
822846
// Register for events related to sdcard installation.
823847
IntentFilter sdFilter = new IntentFilter();
824848
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
849+
sdFilter.addAction(Intent.ACTION_USER_STOPPED);
825850
mContext.registerReceiver(this, sdFilter);
826851
}
827852

@@ -841,6 +866,11 @@ public void onReceive(Context context, Intent intent) {
841866
return;
842867
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
843868
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
869+
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
870+
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
871+
if (userHandle >= 0) {
872+
removeUserLocked(userHandle);
873+
}
844874
} else {
845875
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
846876
&& intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {

0 commit comments

Comments
 (0)