summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmith Yamasani <yamasani@google.com>2012-02-03 12:06:38 -0800
committerAndroid Git Automerger <android-git-automerger@android.com>2012-02-03 12:06:38 -0800
commitbf6644a7e6b63153da73be191798a56f460a7057 (patch)
treecfea6b67d2ff5aa4849cfdb1810d33b45cd061bf
parenta4d82be7c4ba7e81f829f718e9e1331bddeafd9e (diff)
parent1ef7d13172248848805b9ceb6161b0741d8580dd (diff)
downloadframeworks_base-bf6644a7e6b63153da73be191798a56f460a7057.zip
frameworks_base-bf6644a7e6b63153da73be191798a56f460a7057.tar.gz
frameworks_base-bf6644a7e6b63153da73be191798a56f460a7057.tar.bz2
am 1ef7d131: Merge "Multi-user - 1st major checkin"
* commit '1ef7d13172248848805b9ceb6161b0741d8580dd': Multi-user - 1st major checkin
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java14
-rw-r--r--cmds/installd/commands.c42
-rw-r--r--cmds/installd/installd.c6
-rw-r--r--cmds/installd/installd.h4
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java60
-rw-r--r--core/java/android/app/ActivityManager.java3
-rw-r--r--core/java/android/app/ActivityManagerNative.java21
-rw-r--r--core/java/android/app/ActivityThread.java11
-rw-r--r--core/java/android/app/ApplicationPackageManager.java15
-rw-r--r--core/java/android/app/ContextImpl.java25
-rw-r--r--core/java/android/app/IActivityManager.java6
-rw-r--r--core/java/android/app/LoadedApk.java10
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java54
-rw-r--r--core/java/android/content/pm/PackageParser.java52
-rw-r--r--core/java/android/os/Binder.java31
-rw-r--r--core/java/android/os/UserId.java90
-rw-r--r--core/jni/android_util_Binder.cpp6
-rw-r--r--core/tests/coretests/src/android/app/activity/BroadcastTest.java6
-rw-r--r--include/binder/IPCThreadState.h2
-rw-r--r--libs/binder/IPCThreadState.cpp8
-rw-r--r--policy/src/com/android/internal/policy/impl/GlobalActions.java58
-rw-r--r--services/java/com/android/server/AppWidgetService.java1491
-rw-r--r--services/java/com/android/server/AppWidgetServiceImpl.java1606
-rw-r--r--services/java/com/android/server/BackupManagerService.java3
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java3
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java710
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java5
-rw-r--r--services/java/com/android/server/am/ActivityStack.java70
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java4
-rw-r--r--services/java/com/android/server/am/ProviderMap.java244
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java4
-rw-r--r--services/java/com/android/server/am/TaskRecord.java11
-rw-r--r--services/java/com/android/server/pm/Installer.java21
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java77
-rw-r--r--services/java/com/android/server/pm/UserManager.java35
36 files changed, 3007 insertions, 1803 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index fddb429..3d36ebf 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -31,6 +31,7 @@ import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -135,6 +136,8 @@ public class Am {
runToUri(false);
} else if (op.equals("to-intent-uri")) {
runToUri(true);
+ } else if (op.equals("switch-profile")) {
+ runSwitchUser();
} else {
throw new IllegalArgumentException("Unknown command: " + op);
}
@@ -531,7 +534,8 @@ public class Am {
Intent intent = makeIntent();
IntentReceiver receiver = new IntentReceiver();
System.out.println("Broadcasting: " + intent);
- mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false);
+ mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false,
+ Binder.getOrigCallingUser());
receiver.waitForFinish();
}
@@ -722,6 +726,14 @@ public class Am {
mAm.setDebugApp(null, false, true);
}
+ private void runSwitchUser() throws Exception {
+ if (android.os.Process.myUid() != 0) {
+ throw new RuntimeException("switchuser can only be run as root");
+ }
+ String user = nextArgRequired();
+ mAm.switchUser(Integer.parseInt(user));
+ }
+
class MyActivityController extends IActivityController.Stub {
final String mGdbPort;
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index dd92bbe..203d180 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -148,6 +148,48 @@ int delete_persona(uid_t persona)
return delete_dir_contents(pkgdir, 1, NULL);
}
+int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy)
+{
+ char src_data_dir[PKG_PATH_MAX];
+ char pkg_path[PKG_PATH_MAX];
+ DIR *d;
+ struct dirent *de;
+ struct stat s;
+ uid_t uid;
+
+ if (create_persona_path(src_data_dir, src_persona)) {
+ return -1;
+ }
+
+ d = opendir(src_data_dir);
+ if (d != NULL) {
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+
+ if (de->d_type == DT_DIR) {
+ int subfd;
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+ /* Create the full path to the package's data dir */
+ create_pkg_path(pkg_path, name, PKG_DIR_POSTFIX, src_persona);
+ /* Get the file stat */
+ if (stat(pkg_path, &s) < 0) continue;
+ /* Get the uid of the package */
+ ALOGI("Adding datadir for uid = %d\n", s.st_uid);
+ uid = (uid_t) s.st_uid % PER_USER_RANGE;
+ /* Create the directory for the target */
+ make_user_data(name, uid + target_persona * PER_USER_RANGE,
+ target_persona);
+ }
+ }
+ closedir(d);
+ }
+ return 0;
+}
+
int delete_cache(const char *pkgname)
{
char cachedir[PKG_PATH_MAX];
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 569b491..7f94a96 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -107,6 +107,11 @@ static int do_rm_user(char **arg, char reply[REPLY_MAX])
return delete_persona(atoi(arg[0])); /* userid */
}
+static int do_clone_user_data(char **arg, char reply[REPLY_MAX])
+{
+ return clone_persona_data(atoi(arg[0]), atoi(arg[1]), atoi(arg[2]));
+}
+
static int do_movefiles(char **arg, char reply[REPLY_MAX])
{
return movefiles();
@@ -146,6 +151,7 @@ struct cmdinfo cmds[] = {
{ "unlinklib", 1, do_unlinklib },
{ "mkuserdata", 3, do_mk_user_data },
{ "rmuser", 1, do_rm_user },
+ { "cloneuserdata", 3, do_clone_user_data },
};
static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 173cabf..78342bb 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -72,6 +72,9 @@
#define PKG_NAME_MAX 128 /* largest allowed package name */
#define PKG_PATH_MAX 256 /* max size of any path we use */
+#define PER_USER_RANGE ((uid_t)100000) /* range of uids per user
+ uid = persona * PER_USER_RANGE + appid */
+
/* data structures */
typedef struct {
@@ -143,6 +146,7 @@ int renamepkg(const char *oldpkgname, const char *newpkgname);
int delete_user_data(const char *pkgname, uid_t persona);
int make_user_data(const char *pkgname, uid_t uid, uid_t persona);
int delete_persona(uid_t persona);
+int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy);
int delete_cache(const char *pkgname);
int move_dex(const char *src, const char *dst);
int rm_dex(const char *path);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c0ba543..f457842 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -16,8 +16,6 @@
package com.android.commands.pm;
-import com.android.internal.content.PackageHelper;
-
import android.app.ActivityManagerNative;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
@@ -33,14 +31,17 @@ import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.UserInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
-import android.os.Parcel;
+import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import com.android.internal.content.PackageHelper;
+
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -135,13 +136,18 @@ public final class Pm {
return;
}
- if ("createUser".equals(op)) {
- runCreateUser();
+ if ("create-profile".equals(op)) {
+ runCreateProfile();
+ return;
+ }
+
+ if ("remove-profile".equals(op)) {
+ runRemoveProfile();
return;
}
- if ("removeUser".equals(op)) {
- runRemoveUser();
+ if ("list-profiles".equals(op)) {
+ runListProfiles();
return;
}
@@ -829,10 +835,10 @@ public final class Pm {
}
}
- public void runCreateUser() {
+ public void runCreateProfile() {
// Need to be run as root
if (Process.myUid() != ROOT_UID) {
- System.err.println("Error: createUser must be run as root");
+ System.err.println("Error: create-profile must be run as root");
return;
}
String name;
@@ -845,7 +851,7 @@ public final class Pm {
name = arg;
try {
if (mPm.createUser(name, 0) == null) {
- System.err.println("Error: couldn't create user.");
+ System.err.println("Error: couldn't create profile.");
showUsage();
}
} catch (RemoteException e) {
@@ -855,10 +861,10 @@ public final class Pm {
}
- public void runRemoveUser() {
+ public void runRemoveProfile() {
// Need to be run as root
if (Process.myUid() != ROOT_UID) {
- System.err.println("Error: removeUser must be run as root");
+ System.err.println("Error: remove-profile must be run as root");
return;
}
int userId;
@@ -877,7 +883,7 @@ public final class Pm {
}
try {
if (!mPm.removeUser(userId)) {
- System.err.println("Error: couldn't remove user.");
+ System.err.println("Error: couldn't remove profile.");
showUsage();
}
} catch (RemoteException e) {
@@ -886,6 +892,27 @@ public final class Pm {
}
}
+ public void runListProfiles() {
+ // Need to be run as root
+ if (Process.myUid() != ROOT_UID) {
+ System.err.println("Error: list-profiles must be run as root");
+ return;
+ }
+ try {
+ List<UserInfo> users = mPm.getUsers();
+ if (users == null) {
+ System.err.println("Error: couldn't get users");
+ } else {
+ System.out.println("Users:");
+ for (int i = 0; i < users.size(); i++) {
+ System.out.println("\t" + users.get(i).toString());
+ }
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
boolean finished;
boolean result;
@@ -966,7 +993,8 @@ public final class Pm {
ClearDataObserver obs = new ClearDataObserver();
try {
- if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs)) {
+ if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs,
+ Binder.getOrigCallingUser())) {
System.err.println("Failed");
}
@@ -1132,8 +1160,8 @@ public final class Pm {
System.err.println(" pm disable-user PACKAGE_OR_COMPONENT");
System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]");
System.err.println(" pm get-install-location");
- System.err.println(" pm createUser USER_NAME");
- System.err.println(" pm removeUser USER_ID");
+ System.err.println(" pm create-profile USER_NAME");
+ System.err.println(" pm remove-profile USER_ID");
System.err.println("");
System.err.println("pm list packages: prints all packages, optionally only");
System.err.println(" those whose package name contains the text in FILTER. Options:");
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9661b9e..d98d87b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -30,6 +30,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.os.Binder;
import android.os.Debug;
import android.os.Handler;
import android.os.Parcel;
@@ -975,7 +976,7 @@ public class ActivityManager {
public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
try {
return ActivityManagerNative.getDefault().clearApplicationUserData(packageName,
- observer);
+ observer, Binder.getOrigCallingUser());
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7994d7c..d80902d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -91,7 +91,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
try {
getDefault().broadcastIntent(
null, intent, null, null, Activity.RESULT_OK, null, null,
- null /*permission*/, false, true);
+ null /*permission*/, false, true, Binder.getOrigCallingUser());
} catch (RemoteException ex) {
}
}
@@ -306,9 +306,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
String perm = data.readString();
boolean serialized = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
+ int userId = data.readInt();
int res = broadcastIntent(app, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, perm,
- serialized, sticky);
+ serialized, sticky, userId);
reply.writeNoException();
reply.writeInt(res);
return true;
@@ -320,7 +321,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
IBinder b = data.readStrongBinder();
IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null;
Intent intent = Intent.CREATOR.createFromParcel(data);
- unbroadcastIntent(app, intent);
+ int userId = data.readInt();
+ unbroadcastIntent(app, intent, userId);
reply.writeNoException();
return true;
}
@@ -900,7 +902,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
String packageName = data.readString();
IPackageDataObserver observer = IPackageDataObserver.Stub.asInterface(
data.readStrongBinder());
- boolean res = clearApplicationUserData(packageName, observer);
+ int userId = data.readInt();
+ boolean res = clearApplicationUserData(packageName, observer, userId);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@@ -1819,7 +1822,7 @@ class ActivityManagerProxy implements IActivityManager
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
String requiredPermission, boolean serialized,
- boolean sticky) throws RemoteException
+ boolean sticky, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -1834,6 +1837,7 @@ class ActivityManagerProxy implements IActivityManager
data.writeString(requiredPermission);
data.writeInt(serialized ? 1 : 0);
data.writeInt(sticky ? 1 : 0);
+ data.writeInt(userId);
mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
@@ -1841,13 +1845,15 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
return res;
}
- public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException
+ public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId)
+ throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
intent.writeToParcel(data, 0);
+ data.writeInt(userId);
mRemote.transact(UNBROADCAST_INTENT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -2651,12 +2657,13 @@ class ActivityManagerProxy implements IActivityManager
return res;
}
public boolean clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer) throws RemoteException {
+ final IPackageDataObserver observer, final int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeString(packageName);
data.writeStrongBinder(observer.asBinder());
+ data.writeInt(userId);
mRemote.transact(CLEAR_APP_DATA_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3c5f53a..e4cfc99 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -47,6 +47,7 @@ import android.net.Proxy;
import android.net.ProxyProperties;
import android.opengl.GLUtils;
import android.os.AsyncTask;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
@@ -60,6 +61,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -132,6 +134,7 @@ public final class ActivityThread {
private static final boolean DEBUG_RESULTS = false;
private static final boolean DEBUG_BACKUP = true;
private static final boolean DEBUG_CONFIGURATION = false;
+ private static final boolean DEBUG_SERVICE = true;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@@ -635,6 +638,9 @@ public final class ActivityThread {
s.intent = intent;
s.rebind = rebind;
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+ + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
queueOrSendMessage(H.BIND_SERVICE, s);
}
@@ -1592,7 +1598,8 @@ public final class ActivityThread {
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
boolean securityViolation = includeCode && ai.uid != 0
&& ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
- ? ai.uid != mBoundApplication.appInfo.uid : true);
+ ? !UserId.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
+ : true);
if ((flags&(Context.CONTEXT_INCLUDE_CODE
|Context.CONTEXT_IGNORE_SECURITY))
== Context.CONTEXT_INCLUDE_CODE) {
@@ -2294,6 +2301,8 @@ public final class ActivityThread {
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 180a442..fee2beb 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1177,13 +1177,14 @@ final class ApplicationPackageManager extends PackageManager {
*/
@Override
public List<UserInfo> getUsers() {
- // TODO:
- // Dummy code, always returns just the primary user
- ArrayList<UserInfo> users = new ArrayList<UserInfo>();
- UserInfo primary = new UserInfo(0, "Root!",
- UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
- users.add(primary);
- return users;
+ try {
+ return mPM.getUsers();
+ } catch (RemoteException re) {
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>();
+ UserInfo primary = new UserInfo(0, "Root!", UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+ users.add(primary);
+ return users;
+ }
}
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2bf1fb7..db5113e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -75,6 +75,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserId;
import android.os.Vibrator;
import android.os.storage.StorageManager;
import android.telephony.TelephonyManager;
@@ -896,7 +897,8 @@ class ContextImpl extends Context {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, false, false);
+ Activity.RESULT_OK, null, null, null, false, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -908,7 +910,8 @@ class ContextImpl extends Context {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermission, false, false);
+ Activity.RESULT_OK, null, null, receiverPermission, false, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -921,7 +924,8 @@ class ContextImpl extends Context {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, receiverPermission, true, false);
+ Activity.RESULT_OK, null, null, receiverPermission, true, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -954,7 +958,7 @@ class ContextImpl extends Context {
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, receiverPermission,
- true, false);
+ true, false, Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -966,7 +970,8 @@ class ContextImpl extends Context {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
- Activity.RESULT_OK, null, null, null, false, true);
+ Activity.RESULT_OK, null, null, null, false, true,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -999,7 +1004,7 @@ class ContextImpl extends Context {
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, null,
- true, true);
+ true, true, Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -1014,7 +1019,7 @@ class ContextImpl extends Context {
try {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().unbroadcastIntent(
- mMainThread.getApplicationThread(), intent);
+ mMainThread.getApplicationThread(), intent, Binder.getOrigCallingUser());
} catch (RemoteException e) {
}
}
@@ -1215,8 +1220,7 @@ class ContextImpl extends Context {
int pid = Binder.getCallingPid();
if (pid != Process.myPid()) {
- return checkPermission(permission, pid,
- Binder.getCallingUid());
+ return checkPermission(permission, pid, Binder.getCallingUid());
}
return PackageManager.PERMISSION_DENIED;
}
@@ -1384,7 +1388,8 @@ class ContextImpl extends Context {
Uri uri, int modeFlags, String message) {
enforceForUri(
modeFlags, checkCallingUriPermission(uri, modeFlags),
- false, Binder.getCallingUid(), uri, message);
+ false,
+ Binder.getCallingUid(), uri, message);
}
public void enforceCallingOrSelfUriPermission(
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 5222d37..39817ac 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -114,8 +114,8 @@ public interface IActivityManager extends IInterface {
public int broadcastIntent(IApplicationThread caller, Intent intent,
String resolvedType, IIntentReceiver resultTo, int resultCode,
String resultData, Bundle map, String requiredPermission,
- boolean serialized, boolean sticky) throws RemoteException;
- public void unbroadcastIntent(IApplicationThread caller, Intent intent) throws RemoteException;
+ boolean serialized, boolean sticky, int userId) throws RemoteException;
+ public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException;
/* oneway */
public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException;
public void attachApplication(IApplicationThread app) throws RemoteException;
@@ -209,7 +209,7 @@ public interface IActivityManager extends IInterface {
int flags) throws RemoteException;
public void cancelIntentSender(IIntentSender sender) throws RemoteException;
public boolean clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer) throws RemoteException;
+ final IPackageDataObserver observer, int userId) throws RemoteException;
public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
public void setProcessLimit(int max) throws RemoteException;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0c6baeb..fcbcd81 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -36,6 +36,7 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
+import android.os.UserId;
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.view.CompatibilityInfoHolder;
@@ -67,6 +68,8 @@ final class ServiceConnectionLeaked extends AndroidRuntimeException {
*/
public final class LoadedApk {
+ private static final String TAG = "LoadedApk";
+
private final ActivityThread mActivityThread;
private final ApplicationInfo mApplicationInfo;
final String mPackageName;
@@ -113,8 +116,13 @@ public final class LoadedApk {
mApplicationInfo = aInfo;
mPackageName = aInfo.packageName;
mAppDir = aInfo.sourceDir;
- mResDir = aInfo.uid == Process.myUid() ? aInfo.sourceDir
+ final int myUid = Process.myUid();
+ mResDir = aInfo.uid == myUid ? aInfo.sourceDir
: aInfo.publicSourceDir;
+ if (!UserId.isSameUser(aInfo.uid, myUid)) {
+ aInfo.dataDir = PackageManager.getDataDirForUser(UserId.getUserId(myUid),
+ mPackageName);
+ }
mSharedLibraries = aInfo.sharedLibraryFiles;
mDataDir = aInfo.dataDir;
mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index decb974..bb35c29 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -364,4 +364,6 @@ interface IPackageManager {
VerifierDeviceIdentity getVerifierDeviceIdentity();
boolean isFirstBoot();
+
+ List<UserInfo> getUsers();
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8541748..26a9181 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -28,6 +28,7 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Environment;
import android.util.AndroidException;
import android.util.DisplayMetrics;
@@ -753,13 +754,6 @@ public abstract class PackageManager {
public static final int VERIFICATION_REJECT = -1;
/**
- * Range of IDs allocated for a user.
- *
- * @hide
- */
- public static final int PER_USER_RANGE = 100000;
-
- /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
* audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
* lag in sound input or output.
@@ -2615,39 +2609,6 @@ public abstract class PackageManager {
public abstract void updateUserFlags(int id, int flags);
/**
- * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
- * user.
- * @hide
- */
- public static boolean isSameUser(int uid1, int uid2) {
- return getUserId(uid1) == getUserId(uid2);
- }
-
- /**
- * Returns the user id for a given uid.
- * @hide
- */
- public static int getUserId(int uid) {
- return uid / PER_USER_RANGE;
- }
-
- /**
- * Returns the uid that is composed from the userId and the appId.
- * @hide
- */
- public static int getUid(int userId, int appId) {
- return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
- }
-
- /**
- * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
- * @hide
- */
- public static int getAppId(int uid) {
- return uid % PER_USER_RANGE;
- }
-
- /**
* Returns the device identity that verifiers can use to associate their
* scheme to a particular device. This should not be used by anything other
* than a package verifier.
@@ -2656,4 +2617,17 @@ public abstract class PackageManager {
* @hide
*/
public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
+
+ /**
+ * Returns the data directory for a particular user and package, given the uid of the package.
+ * @param uid uid of the package, including the userId and appId
+ * @param packageName name of the package
+ * @return the user-specific data directory for the package
+ * @hide
+ */
+ public static String getDataDirForUser(int userId, String packageName) {
+ // TODO: This should be shared with Installer's knowledge of user directory
+ return Environment.getDataDirectory().toString() + "/user/" + userId
+ + "/" + packageName;
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e593d5b..faee873 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,18 +24,17 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
+import android.os.UserId;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
-import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedInputStream;
import java.io.File;
@@ -59,6 +58,11 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
/**
* Package archive parsing
*
@@ -209,6 +213,8 @@ public class PackageParser {
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime) {
+ final int userId = Binder.getOrigCallingUser();
+
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
pi.versionCode = p.mVersionCode;
@@ -250,7 +256,8 @@ public class PackageParser {
final Activity activity = p.activities.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags);
+ pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
+ userId);
}
}
}
@@ -271,7 +278,7 @@ public class PackageParser {
final Activity activity = p.receivers.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags);
+ pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags, userId);
}
}
}
@@ -292,7 +299,7 @@ public class PackageParser {
final Service service = p.services.get(i);
if (service.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.services[j++] = generateServiceInfo(p.services.get(i), flags);
+ pi.services[j++] = generateServiceInfo(p.services.get(i), flags, userId);
}
}
}
@@ -313,7 +320,7 @@ public class PackageParser {
final Provider provider = p.providers.get(i);
if (provider.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags);
+ pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, userId);
}
}
}
@@ -3241,8 +3248,12 @@ public class PackageParser {
}
public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
+ return generateApplicationInfo(p, flags, UserId.getUserId(Binder.getCallingUid()));
+ }
+
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p, null)) {
+ if (!copyNeeded(flags, p, null) && userId == 0) {
// CompatibilityMode is global state. It's safe to modify the instance
// of the package.
if (!sCompatibilityModeEnabled) {
@@ -3258,6 +3269,10 @@ public class PackageParser {
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
+ if (userId != 0) {
+ ai.uid = UserId.getUid(userId, ai.uid);
+ ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
+ }
if ((flags & PackageManager.GET_META_DATA) != 0) {
ai.metaData = p.mAppMetaData;
}
@@ -3325,16 +3340,15 @@ public class PackageParser {
}
}
- public static final ActivityInfo generateActivityInfo(Activity a,
- int flags) {
+ public static final ActivityInfo generateActivityInfo(Activity a, int flags, int userId) {
if (a == null) return null;
- if (!copyNeeded(flags, a.owner, a.metaData)) {
+ if (!copyNeeded(flags, a.owner, a.metaData) && userId == 0) {
return a.info;
}
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo(a.info);
ai.metaData = a.metaData;
- ai.applicationInfo = generateApplicationInfo(a.owner, flags);
+ ai.applicationInfo = generateApplicationInfo(a.owner, flags, userId);
return ai;
}
@@ -3359,15 +3373,15 @@ public class PackageParser {
}
}
- public static final ServiceInfo generateServiceInfo(Service s, int flags) {
+ public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
if (s == null) return null;
- if (!copyNeeded(flags, s.owner, s.metaData)) {
+ if (!copyNeeded(flags, s.owner, s.metaData) && userId == 0) {
return s.info;
}
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo(s.info);
si.metaData = s.metaData;
- si.applicationInfo = generateApplicationInfo(s.owner, flags);
+ si.applicationInfo = generateApplicationInfo(s.owner, flags, userId);
return si;
}
@@ -3400,12 +3414,12 @@ public class PackageParser {
}
}
- public static final ProviderInfo generateProviderInfo(Provider p,
- int flags) {
+ public static final ProviderInfo generateProviderInfo(Provider p, int flags, int userId) {
if (p == null) return null;
if (!copyNeeded(flags, p.owner, p.metaData)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
- || p.info.uriPermissionPatterns == null)) {
+ || p.info.uriPermissionPatterns == null)
+ && userId == 0) {
return p.info;
}
// Make shallow copies so we can store the metadata safely
@@ -3414,7 +3428,7 @@ public class PackageParser {
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags);
+ pi.applicationInfo = generateApplicationInfo(p.owner, flags, userId);
return pi;
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 24569fa..577fc43 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -49,6 +49,7 @@ public class Binder implements IBinder {
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Binder";
+ /* mObject is used by native code, do not remove or rename */
private int mObject;
private IInterface mOwner;
private String mDescriptor;
@@ -70,7 +71,35 @@ public class Binder implements IBinder {
* incoming transaction, then its own uid is returned.
*/
public static final native int getCallingUid();
-
+
+ /**
+ * Return the original ID of the user assigned to the process that sent you the current
+ * transaction that is being processed. This uid can be used with higher-level system services
+ * to determine its identity and check permissions. If the current thread is not currently
+ * executing an incoming transaction, then its own uid is returned.
+ * <p/>
+ * This value cannot be reset by calls to {@link #clearCallingIdentity()}.
+ * @hide
+ */
+ public static final int getOrigCallingUid() {
+ if (UserId.MU_ENABLED) {
+ return getOrigCallingUidNative();
+ } else {
+ return getCallingUid();
+ }
+ }
+
+ private static final native int getOrigCallingUidNative();
+
+ /**
+ * Utility function to return the user id of the calling process.
+ * @return userId of the calling process, extracted from the callingUid
+ * @hide
+ */
+ public static final int getOrigCallingUser() {
+ return UserId.getUserId(getOrigCallingUid());
+ }
+
/**
* Reset the identity of the incoming IPC on the current thread. This can
* be useful if, while handling an incoming call, you will be calling
diff --git a/core/java/android/os/UserId.java b/core/java/android/os/UserId.java
new file mode 100644
index 0000000..4124d51
--- /dev/null
+++ b/core/java/android/os/UserId.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+public final class UserId {
+ /**
+ * Range of IDs allocated for a user.
+ *
+ * @hide
+ */
+ public static final int PER_USER_RANGE = 100000;
+
+ public static final int USER_ALL = -1;
+
+ /**
+ * Enable multi-user related side effects. Set this to false if there are problems with single
+ * user usecases.
+ * */
+ public static final boolean MU_ENABLED = true;
+
+ /**
+ * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
+ * user.
+ * @hide
+ */
+ public static final boolean isSameUser(int uid1, int uid2) {
+ return getUserId(uid1) == getUserId(uid2);
+ }
+
+ /**
+ * Checks to see if both uids are referring to the same app id, ignoring the user id part of the
+ * uids.
+ * @param uid1 uid to compare
+ * @param uid2 other uid to compare
+ * @return whether the appId is the same for both uids
+ * @hide
+ */
+ public static final boolean isSameApp(int uid1, int uid2) {
+ return getAppId(uid1) == getAppId(uid2);
+ }
+
+ /**
+ * Returns the user id for a given uid.
+ * @hide
+ */
+ public static final int getUserId(int uid) {
+ if (MU_ENABLED) {
+ return uid / PER_USER_RANGE;
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the uid that is composed from the userId and the appId.
+ * @hide
+ */
+ public static final int getUid(int userId, int appId) {
+ if (MU_ENABLED) {
+ return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
+ } else {
+ return appId;
+ }
+ }
+
+ /**
+ * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
+ * @hide
+ */
+ public static final int getAppId(int uid) {
+ return uid % PER_USER_RANGE;
+ }
+}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 990a617..e00970a 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -739,6 +739,11 @@ static jint android_os_Binder_getCallingUid(JNIEnv* env, jobject clazz)
return IPCThreadState::self()->getCallingUid();
}
+static jint android_os_Binder_getOrigCallingUid(JNIEnv* env, jobject clazz)
+{
+ return IPCThreadState::self()->getOrigCallingUid();
+}
+
static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
{
return IPCThreadState::self()->clearCallingIdentity();
@@ -810,6 +815,7 @@ static const JNINativeMethod gBinderMethods[] = {
/* name, signature, funcPtr */
{ "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
+ { "getOrigCallingUidNative", "()I", (void*)android_os_Binder_getOrigCallingUid },
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
{ "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 4b1f9fd..d527c0d 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -303,7 +303,8 @@ public class BroadcastTest extends ActivityTestsBase {
public void testSetSticky() throws Exception {
Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
intent.putExtra("test", LaunchpadActivity.DATA_1);
- ActivityManagerNative.getDefault().unbroadcastIntent(null, intent);
+ ActivityManagerNative.getDefault().unbroadcastIntent(null, intent,
+ Binder.getOrigCallingUser());
ActivityManagerNative.broadcastStickyIntent(intent, null);
addIntermediate("finished-broadcast");
@@ -320,7 +321,8 @@ public class BroadcastTest extends ActivityTestsBase {
ActivityManagerNative.broadcastStickyIntent(intent, null);
ActivityManagerNative.getDefault().unbroadcastIntent(
- null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null));
+ null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null),
+ Binder.getOrigCallingUser());
addIntermediate("finished-unbroadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
index 3378d97..691ba2f 100644
--- a/include/binder/IPCThreadState.h
+++ b/include/binder/IPCThreadState.h
@@ -41,6 +41,7 @@ public:
int getCallingPid();
int getCallingUid();
+ int getOrigCallingUid();
void setStrictModePolicy(int32_t policy);
int32_t getStrictModePolicy() const;
@@ -116,6 +117,7 @@ private:
status_t mLastError;
pid_t mCallingPid;
uid_t mCallingUid;
+ uid_t mOrigCallingUid;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
};
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 629b899..b578a6c 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -371,6 +371,11 @@ int IPCThreadState::getCallingUid()
return mCallingUid;
}
+int IPCThreadState::getOrigCallingUid()
+{
+ return mOrigCallingUid;
+}
+
int64_t IPCThreadState::clearCallingIdentity()
{
int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
@@ -641,6 +646,7 @@ IPCThreadState::IPCThreadState()
{
pthread_setspecific(gTLS, this);
clearCaller();
+ mOrigCallingUid = mCallingUid;
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
@@ -987,6 +993,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd)
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
+ mOrigCallingUid = tr.sender_euid;
int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
if (gDisableBackgroundScheduling) {
@@ -1045,6 +1052,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd)
mCallingPid = origPid;
mCallingUid = origUid;
+ mOrigCallingUid = origUid;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 38c85bb..bcba3c2 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -16,16 +16,23 @@
package com.android.internal.policy.impl;
-import android.app.Activity;
+import com.android.internal.app.ShutdownThread;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.R;
+
+import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
@@ -39,13 +46,9 @@ import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.android.internal.R;
-import com.android.internal.app.ShutdownThread;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-import com.google.android.collect.Lists;
import java.util.ArrayList;
+import java.util.List;
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that
@@ -101,9 +104,10 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = isDeviceProvisioned;
- if (mDialog == null) {
- mDialog = createDialog();
+ if (mDialog != null) {
+ mDialog.dismiss();
}
+ mDialog = createDialog();
prepareDialog();
mDialog.show();
@@ -187,6 +191,31 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mItems.add(mSilentModeAction);
}
+ List<UserInfo> users = mContext.getPackageManager().getUsers();
+ if (users.size() > 1) {
+ for (final UserInfo user : users) {
+ SinglePressAction switchToUser = new SinglePressAction(
+ com.android.internal.R.drawable.ic_menu_cc,
+ user.name != null ? user.name : "Primary") {
+ public void onPress() {
+ try {
+ ActivityManagerNative.getDefault().switchUser(user.id);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't switch user " + re);
+ }
+ }
+
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ public boolean showBeforeProvisioning() {
+ return false;
+ }
+ };
+ mItems.add(switchToUser);
+ }
+ }
mAdapter = new MyAdapter();
final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
@@ -341,12 +370,19 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private static abstract class SinglePressAction implements Action {
private final int mIconResId;
private final int mMessageResId;
+ private final CharSequence mMessage;
protected SinglePressAction(int iconResId, int messageResId) {
mIconResId = iconResId;
mMessageResId = messageResId;
+ mMessage = null;
}
+ protected SinglePressAction(int iconResId, CharSequence message) {
+ mIconResId = iconResId;
+ mMessageResId = 0;
+ mMessage = message;
+ }
public boolean isEnabled() {
return true;
}
@@ -363,7 +399,11 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
v.findViewById(R.id.status).setVisibility(View.GONE);
icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
- messageView.setText(mMessageResId);
+ if (mMessage != null) {
+ messageView.setText(mMessage);
+ } else {
+ messageView.setText(mMessageResId);
+ }
return v;
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 4f81178..081f1f4 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -24,67 +24,35 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.Intent.FilterComparison;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.util.Log;
import android.util.Pair;
import android.util.Slog;
-import android.util.TypedValue;
-import android.util.Xml;
+import android.util.SparseArray;
import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService;
-import com.android.internal.os.AtomicFile;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
-import com.android.internal.widget.IRemoteViewsFactory;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
-import java.util.Set;
+
+/**
+ * Redirects calls to this service to the instance of the service for the appropriate user.
+ */
class AppWidgetService extends IAppWidgetService.Stub
{
private static final String TAG = "AppWidgetService";
- private static final String SETTINGS_FILENAME = "appwidgets.xml";
- private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
-
/*
* When identifying a Host or Provider based on the calling process, use the uid field.
* When identifying a Host or Provider based on a package manager broadcast, use the
@@ -125,11 +93,9 @@ class AppWidgetService extends IAppWidgetService.Stub
* globally and may lead us to leak AppWidgetService instances (if there were more than one).
*/
static class ServiceConnectionProxy implements ServiceConnection {
- private final Pair<Integer, Intent.FilterComparison> mKey;
private final IBinder mConnectionCb;
ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
- mKey = key;
mConnectionCb = connectionCb;
}
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -155,13 +121,6 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
- // Manages active connections to RemoteViewsServices
- private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection>
- mBoundRemoteViewsServices = new HashMap<Pair<Integer,FilterComparison>,ServiceConnection>();
- // Manages persistent references to RemoteViewsServices from different App Widgets
- private final HashMap<FilterComparison, HashSet<Integer>>
- mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
-
Context mContext;
Locale mLocale;
PackageManager mPackageManager;
@@ -171,35 +130,32 @@ class AppWidgetService extends IAppWidgetService.Stub
final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
ArrayList<Host> mHosts = new ArrayList<Host>();
boolean mSafeMode;
- boolean mStateLoaded;
- // These are for debugging only -- widgets are going missing in some rare instances
- ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
- ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+ private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
AppWidgetService(Context context) {
mContext = context;
- mPackageManager = context.getPackageManager();
- mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
+ mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5);
+ AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0);
+ mAppWidgetServices.append(0, primary);
}
public void systemReady(boolean safeMode) {
mSafeMode = safeMode;
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- }
+ mAppWidgetServices.get(0).systemReady(safeMode);
// Register for the boot completed broadcast, so we can send the
- // ENABLE broacasts. If we try to send them now, they time out,
+ // ENABLE broacasts. If we try to send them now, they time out,
// because the system isn't ready to handle them yet.
mContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
// Register for configuration changes so we can update the names
// of the widgets when the locale changes.
- mContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+ mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
+ Intent.ACTION_CONFIGURATION_CHANGED), null, null);
// Register for broadcasts about package install, etc., so we can
// update the provider list.
@@ -216,216 +172,24 @@ class AppWidgetService extends IAppWidgetService.Stub
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
}
- private void ensureStateLoadedLocked() {
- if (!mStateLoaded) {
- loadAppWidgetList();
- loadStateLocked();
- mStateLoaded = true;
- }
- }
-
- private void dumpProvider(Provider p, int index, PrintWriter pw) {
- AppWidgetProviderInfo info = p.info;
- pw.print(" ["); pw.print(index); pw.print("] provider ");
- pw.print(info.provider.flattenToShortString());
- pw.println(':');
- pw.print(" min=("); pw.print(info.minWidth);
- pw.print("x"); pw.print(info.minHeight);
- pw.print(") minResize=("); pw.print(info.minResizeWidth);
- pw.print("x"); pw.print(info.minResizeHeight);
- pw.print(") updatePeriodMillis=");
- pw.print(info.updatePeriodMillis);
- pw.print(" resizeMode=");
- pw.print(info.resizeMode);
- pw.print(" autoAdvanceViewId=");
- pw.print(info.autoAdvanceViewId);
- pw.print(" initialLayout=#");
- pw.print(Integer.toHexString(info.initialLayout));
- pw.print(" zombie="); pw.println(p.zombie);
- }
-
- private void dumpHost(Host host, int index, PrintWriter pw) {
- pw.print(" ["); pw.print(index); pw.print("] hostId=");
- pw.print(host.hostId); pw.print(' ');
- pw.print(host.packageName); pw.print('/');
- pw.print(host.uid); pw.println(':');
- pw.print(" callbacks="); pw.println(host.callbacks);
- pw.print(" instances.size="); pw.print(host.instances.size());
- pw.print(" zombie="); pw.println(host.zombie);
- }
-
- private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
- pw.print(" ["); pw.print(index); pw.print("] id=");
- pw.println(id.appWidgetId);
- pw.print(" hostId=");
- pw.print(id.host.hostId); pw.print(' ');
- pw.print(id.host.packageName); pw.print('/');
- pw.println(id.host.uid);
- if (id.provider != null) {
- pw.print(" provider=");
- pw.println(id.provider.info.provider.flattenToShortString());
- }
- if (id.host != null) {
- pw.print(" host.callbacks="); pw.println(id.host.callbacks);
- }
- if (id.views != null) {
- pw.print(" views="); pw.println(id.views);
- }
- }
-
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- synchronized (mAppWidgetIds) {
- int N = mInstalledProviders.size();
- pw.println("Providers:");
- for (int i=0; i<N; i++) {
- dumpProvider(mInstalledProviders.get(i), i, pw);
- }
-
- N = mAppWidgetIds.size();
- pw.println(" ");
- pw.println("AppWidgetIds:");
- for (int i=0; i<N; i++) {
- dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
- }
-
- N = mHosts.size();
- pw.println(" ");
- pw.println("Hosts:");
- for (int i=0; i<N; i++) {
- dumpHost(mHosts.get(i), i, pw);
- }
-
- N = mDeletedProviders.size();
- pw.println(" ");
- pw.println("Deleted Providers:");
- for (int i=0; i<N; i++) {
- dumpProvider(mDeletedProviders.get(i), i, pw);
- }
-
- N = mDeletedHosts.size();
- pw.println(" ");
- pw.println("Deleted Hosts:");
- for (int i=0; i<N; i++) {
- dumpHost(mDeletedHosts.get(i), i, pw);
- }
- }
- }
-
- public int allocateAppWidgetId(String packageName, int hostId) {
- int callingUid = enforceCallingUid(packageName);
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int appWidgetId = mNextAppWidgetId++;
-
- Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
-
- AppWidgetId id = new AppWidgetId();
- id.appWidgetId = appWidgetId;
- id.host = host;
-
- host.instances.add(id);
- mAppWidgetIds.add(id);
-
- saveStateLocked();
-
- return appWidgetId;
- }
+ public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
+ return getImplForUser().allocateAppWidgetId(packageName, hostId);
}
-
- public void deleteAppWidgetId(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null) {
- deleteAppWidgetLocked(id);
- saveStateLocked();
- }
- }
- }
-
- public void deleteHost(int hostId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int callingUid = getCallingUid();
- Host host = lookupHostLocked(callingUid, hostId);
- if (host != null) {
- deleteHostLocked(host);
- saveStateLocked();
- }
- }
- }
-
- public void deleteAllHosts() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int callingUid = getCallingUid();
- final int N = mHosts.size();
- boolean changed = false;
- for (int i=N-1; i>=0; i--) {
- Host host = mHosts.get(i);
- if (host.uid == callingUid) {
- deleteHostLocked(host);
- changed = true;
- }
- }
- if (changed) {
- saveStateLocked();
- }
- }
+
+ @Override
+ public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
+ getImplForUser().deleteAppWidgetId(appWidgetId);
}
- void deleteHostLocked(Host host) {
- final int N = host.instances.size();
- for (int i=N-1; i>=0; i--) {
- AppWidgetId id = host.instances.get(i);
- deleteAppWidgetLocked(id);
- }
- host.instances.clear();
- mHosts.remove(host);
- mDeletedHosts.add(host);
- // it's gone or going away, abruptly drop the callback connection
- host.callbacks = null;
+ @Override
+ public void deleteHost(int hostId) throws RemoteException {
+ getImplForUser().deleteHost(hostId);
}
- void deleteAppWidgetLocked(AppWidgetId id) {
- // We first unbind all services that are bound to this id
- unbindAppWidgetRemoteViewsServicesLocked(id);
-
- Host host = id.host;
- host.instances.remove(id);
- pruneHostLocked(host);
-
- mAppWidgetIds.remove(id);
-
- Provider p = id.provider;
- if (p != null) {
- p.instances.remove(id);
- if (!p.zombie) {
- // send the broacast saying that this appWidgetId has been deleted
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
- intent.setComponent(p.info.provider);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
- mContext.sendBroadcast(intent);
- if (p.instances.size() == 0) {
- // cancel the future updates
- cancelBroadcasts(p);
-
- // send the broacast saying that the provider is not in use any more
- intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
- }
- }
- }
+ @Override
+ public void deleteAllHosts() throws RemoteException {
+ getImplForUser().deleteAllHosts();
}
void cancelBroadcasts(Provider p) {
@@ -441,617 +205,58 @@ class AppWidgetService extends IAppWidgetService.Stub
}
}
- public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
- mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
- "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
- if (id.provider != null) {
- throw new IllegalArgumentException("appWidgetId " + appWidgetId + " already bound to "
- + id.provider.info.provider);
- }
- Provider p = lookupProviderLocked(provider);
- if (p == null) {
- throw new IllegalArgumentException("not a appwidget provider: " + provider);
- }
- if (p.zombie) {
- throw new IllegalArgumentException("can't bind to a 3rd party provider in"
- + " safe mode: " + provider);
- }
-
- id.provider = p;
- p.instances.add(id);
- int instancesSize = p.instances.size();
- if (instancesSize == 1) {
- // tell the provider that it's ready
- sendEnableIntentLocked(p);
- }
-
- // send an update now -- We need this update now, and just for this appWidgetId.
- // It's less critical when the next one happens, so when we schdule the next one,
- // we add updatePeriodMillis to its start time. That time will have some slop,
- // but that's okay.
- sendUpdateIntentLocked(p, new int[] { appWidgetId });
-
- // schedule the future updates
- registerForBroadcastsLocked(p, getAppWidgetIds(p));
- saveStateLocked();
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- // Binds to a specific RemoteViewsService
- public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
- final ComponentName componentName = intent.getComponent();
- try {
- final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
- PackageManager.GET_PERMISSIONS);
- if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
- throw new SecurityException("Selected service does not require "
- + android.Manifest.permission.BIND_REMOTEVIEWS
- + ": " + componentName);
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("Unknown component " + componentName);
- }
-
- // If there is already a connection made for this service intent, then disconnect from
- // that first. (This does not allow multiple connections to the same service under
- // the same key)
- ServiceConnectionProxy conn = null;
- FilterComparison fc = new FilterComparison(intent);
- Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
- if (mBoundRemoteViewsServices.containsKey(key)) {
- conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- mBoundRemoteViewsServices.remove(key);
- }
-
- // Bind to the RemoteViewsService (which will trigger a callback to the
- // RemoteViewsAdapter.onServiceConnected())
- final long token = Binder.clearCallingIdentity();
- try {
- conn = new ServiceConnectionProxy(key, connection);
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- mBoundRemoteViewsServices.put(key, conn);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
- // when we can call back to the RemoteViewsService later to destroy associated
- // factories.
- incrementAppWidgetServiceRefCount(appWidgetId, fc);
- }
- }
-
- // Unbinds from a specific RemoteViewsService
- public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- // Unbind from the RemoteViewsService (which will trigger a callback to the bound
- // RemoteViewsAdapter)
- Pair<Integer, FilterComparison> key = Pair.create(appWidgetId,
- new FilterComparison(intent));
- if (mBoundRemoteViewsServices.containsKey(key)) {
- // We don't need to use the appWidgetId until after we are sure there is something
- // to unbind. Note that this may mask certain issues with apps calling unbind()
- // more than necessary.
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id == null) {
- throw new IllegalArgumentException("bad appWidgetId");
- }
-
- ServiceConnectionProxy conn =
- (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- mBoundRemoteViewsServices.remove(key);
- } else {
- Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
- }
- }
- }
-
- // Unbinds from a RemoteViewsService when we delete an app widget
- private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
- int appWidgetId = id.appWidgetId;
- // Unbind all connections to Services bound to this AppWidgetId
- Iterator<Pair<Integer, Intent.FilterComparison>> it =
- mBoundRemoteViewsServices.keySet().iterator();
- while (it.hasNext()) {
- final Pair<Integer, Intent.FilterComparison> key = it.next();
- if (key.first.intValue() == appWidgetId) {
- final ServiceConnectionProxy conn = (ServiceConnectionProxy)
- mBoundRemoteViewsServices.get(key);
- conn.disconnect();
- mContext.unbindService(conn);
- it.remove();
- }
- }
-
- // Check if we need to destroy any services (if no other app widgets are
- // referencing the same service)
- decrementAppWidgetServiceRefCount(appWidgetId);
- }
-
- // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
- private void destroyRemoteViewsService(final Intent intent) {
- final ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- final IRemoteViewsFactory cb =
- IRemoteViewsFactory.Stub.asInterface(service);
- try {
- cb.onDestroy(intent);
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (RuntimeException e) {
- e.printStackTrace();
- }
- mContext.unbindService(this);
- }
- @Override
- public void onServiceDisconnected(android.content.ComponentName name) {
- // Do nothing
- }
- };
-
- // Bind to the service and remove the static intent->factory mapping in the
- // RemoteViewsService.
- final long token = Binder.clearCallingIdentity();
- try {
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- // Adds to the ref-count for a given RemoteViewsService intent
- private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
- HashSet<Integer> appWidgetIds = null;
- if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
- appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
- } else {
- appWidgetIds = new HashSet<Integer>();
- mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
- }
- appWidgetIds.add(appWidgetId);
- }
-
- // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
- // the ref-count reaches zero.
- private void decrementAppWidgetServiceRefCount(int appWidgetId) {
- Iterator<FilterComparison> it =
- mRemoteViewsServicesAppWidgets.keySet().iterator();
- while (it.hasNext()) {
- final FilterComparison key = it.next();
- final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
- if (ids.remove(appWidgetId)) {
- // If we have removed the last app widget referencing this service, then we
- // should destroy it and remove it from this set
- if (ids.isEmpty()) {
- destroyRemoteViewsService(key.getIntent());
- it.remove();
- }
- }
- }
- }
-
- public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null && id.provider != null && !id.provider.zombie) {
- return id.provider.info;
- }
- return null;
- }
- }
-
- public RemoteViews getAppWidgetViews(int appWidgetId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
- if (id != null) {
- return id.views;
- }
- return null;
- }
- }
-
- public List<AppWidgetProviderInfo> getInstalledProviders() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- final int N = mInstalledProviders.size();
- ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (!p.zombie) {
- result.add(p.info);
- }
- }
- return result;
- }
- }
-
- public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- updateAppWidgetInstanceLocked(id, views);
- }
- }
- }
-
- public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- updateAppWidgetInstanceLocked(id, views, true);
- }
- }
- }
-
- public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
- if (appWidgetIds == null) {
- return;
- }
- if (appWidgetIds.length == 0) {
- return;
- }
- final int N = appWidgetIds.length;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (int i=0; i<N; i++) {
- AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
- notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
- }
- }
- }
-
- public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Provider p = lookupProviderLocked(provider);
- if (p == null) {
- Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
- return;
- }
- ArrayList<AppWidgetId> instances = p.instances;
- final int callingUid = getCallingUid();
- final int N = instances.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = instances.get(i);
- if (canAccessAppWidgetId(id, callingUid)) {
- updateAppWidgetInstanceLocked(id, views);
- }
- }
- }
- }
-
- void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
- updateAppWidgetInstanceLocked(id, views, false);
- }
-
- void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
- // allow for stale appWidgetIds and other badness
- // lookup also checks that the calling process can access the appWidgetId
- // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
- if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
-
- // We do not want to save this RemoteViews
- if (!isPartialUpdate) id.views = views;
-
- // is anyone listening?
- if (id.host.callbacks != null) {
- try {
- // the lock is held, but this is a oneway call
- id.host.callbacks.updateAppWidget(id.appWidgetId, views);
- } catch (RemoteException e) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this instance.
- id.host.callbacks = null;
- }
- }
- }
- }
-
- void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
- // allow for stale appWidgetIds and other badness
- // lookup also checks that the calling process can access the appWidgetId
- // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
- if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
- // is anyone listening?
- if (id.host.callbacks != null) {
- try {
- // the lock is held, but this is a oneway call
- id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
- } catch (RemoteException e) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this instance.
- id.host.callbacks = null;
- }
- }
-
- // If the host is unavailable, then we call the associated
- // RemoteViewsFactory.onDataSetChanged() directly
- if (id.host.callbacks == null) {
- Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
- for (FilterComparison key : keys) {
- if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
- Intent intent = key.getIntent();
-
- final ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IRemoteViewsFactory cb =
- IRemoteViewsFactory.Stub.asInterface(service);
- try {
- cb.onDataSetChangedAsync();
- } catch (RemoteException e) {
- e.printStackTrace();
- } catch (RuntimeException e) {
- e.printStackTrace();
- }
- mContext.unbindService(this);
- }
- @Override
- public void onServiceDisconnected(android.content.ComponentName name) {
- // Do nothing
- }
- };
-
- // Bind to the service and call onDataSetChanged()
- final long token = Binder.clearCallingIdentity();
- try {
- mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
- }
- }
- }
-
- public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
- List<RemoteViews> updatedViews) {
- int callingUid = enforceCallingUid(packageName);
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
- host.callbacks = callbacks;
-
- updatedViews.clear();
-
- ArrayList<AppWidgetId> instances = host.instances;
- int N = instances.size();
- int[] updatedIds = new int[N];
- for (int i=0; i<N; i++) {
- AppWidgetId id = instances.get(i);
- updatedIds[i] = id.appWidgetId;
- updatedViews.add(id.views);
- }
- return updatedIds;
- }
- }
-
- public void stopListening(int hostId) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Host host = lookupHostLocked(getCallingUid(), hostId);
- if (host != null) {
- host.callbacks = null;
- pruneHostLocked(host);
- }
- }
- }
-
- boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
- if (id.host.uid == callingUid) {
- // Apps hosting the AppWidget have access to it.
- return true;
- }
- if (id.provider != null && id.provider.uid == callingUid) {
- // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
- return true;
- }
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET)
- == PackageManager.PERMISSION_GRANTED) {
- // Apps that can bind have access to all appWidgetIds.
- return true;
- }
- // Nobody else can access it.
- return false;
- }
-
- AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
- int callingUid = getCallingUid();
- final int N = mAppWidgetIds.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = mAppWidgetIds.get(i);
- if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
- return id;
- }
- }
- return null;
- }
-
- Provider lookupProviderLocked(ComponentName provider) {
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.info.provider.equals(provider)) {
- return p;
- }
- }
- return null;
+ @Override
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
+ getImplForUser().bindAppWidgetId(appWidgetId, provider);
}
- Host lookupHostLocked(int uid, int hostId) {
- final int N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host h = mHosts.get(i);
- if (h.uid == uid && h.hostId == hostId) {
- return h;
- }
- }
- return null;
+ @Override
+ public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
+ throws RemoteException {
+ getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
}
- Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
- final int N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host h = mHosts.get(i);
- if (h.hostId == hostId && h.packageName.equals(packageName)) {
- return h;
- }
- }
- Host host = new Host();
- host.packageName = packageName;
- host.uid = uid;
- host.hostId = hostId;
- mHosts.add(host);
- return host;
+ @Override
+ public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
+ List<RemoteViews> updatedViews) throws RemoteException {
+ return getImplForUser().startListening(host, packageName, hostId, updatedViews);
}
- void pruneHostLocked(Host host) {
- if (host.instances.size() == 0 && host.callbacks == null) {
- mHosts.remove(host);
- }
+ // TODO: Call this from PackageManagerService when a user is removed
+ public void removeUser(int userId) {
}
- void loadAppWidgetList() {
- PackageManager pm = mPackageManager;
-
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- addProviderLocked(ri);
+ private AppWidgetServiceImpl getImplForUser() {
+ final int userId = Binder.getOrigCallingUser();
+ AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
+ if (service == null) {
+ Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
+ // TODO: Verify that it's a valid user
+ service = new AppWidgetServiceImpl(mContext, userId);
+ service.systemReady(mSafeMode);
+ // Assume that BOOT_COMPLETED was received, as this is a non-primary user.
+ service.sendInitialBroadcasts();
+ mAppWidgetServices.append(userId, service);
}
- }
- boolean addProviderLocked(ResolveInfo ri) {
- if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- return false;
- }
- if (!ri.activityInfo.isEnabled()) {
- return false;
- }
- Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
- ri.activityInfo.name), ri);
- if (p != null) {
- mInstalledProviders.add(p);
- return true;
- } else {
- return false;
- }
+ return service;
}
- void removeProviderLocked(int index, Provider p) {
- int N = p.instances.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = p.instances.get(i);
- // Call back with empty RemoteViews
- updateAppWidgetInstanceLocked(id, null);
- // Stop telling the host about updates for this from now on
- cancelBroadcasts(p);
- // clear out references to this appWidgetId
- id.host.instances.remove(id);
- mAppWidgetIds.remove(id);
- id.provider = null;
- pruneHostLocked(id.host);
- id.host = null;
- }
- p.instances.clear();
- mInstalledProviders.remove(index);
- mDeletedProviders.add(p);
- // no need to send the DISABLE broadcast, since the receiver is gone anyway
- cancelBroadcasts(p);
+ @Override
+ public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
+ return getImplForUser().getAppWidgetIds(provider);
}
- void sendEnableIntentLocked(Provider p) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
+ @Override
+ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
+ return getImplForUser().getAppWidgetInfo(appWidgetId);
}
- void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
- if (appWidgetIds != null && appWidgetIds.length > 0) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
- intent.setComponent(p.info.provider);
- mContext.sendBroadcast(intent);
- }
+ @Override
+ public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
+ return getImplForUser().getAppWidgetViews(appWidgetId);
}
- void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
- if (p.info.updatePeriodMillis > 0) {
- // if this is the first instance, set the alarm. otherwise,
- // rely on the fact that we've already set it and that
- // PendingIntent.getBroadcast will update the extras.
- boolean alreadyRegistered = p.broadcast != null;
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
- intent.setComponent(p.info.provider);
- long token = Binder.clearCallingIdentity();
- try {
- p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- if (!alreadyRegistered) {
- long period = p.info.updatePeriodMillis;
- if (period < MIN_UPDATE_PERIOD) {
- period = MIN_UPDATE_PERIOD;
- }
- mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + period, period, p.broadcast);
- }
- }
- }
-
static int[] getAppWidgetIds(Provider p) {
int instancesSize = p.instances.size();
int appWidgetIds[] = new int[instancesSize];
@@ -1060,570 +265,70 @@ class AppWidgetService extends IAppWidgetService.Stub
}
return appWidgetIds;
}
-
- public int[] getAppWidgetIds(ComponentName provider) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Provider p = lookupProviderLocked(provider);
- if (p != null && getCallingUid() == p.uid) {
- return getAppWidgetIds(p);
- } else {
- return new int[0];
- }
- }
- }
-
- private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
- Provider p = null;
-
- ActivityInfo activityInfo = ri.activityInfo;
- XmlResourceParser parser = null;
- try {
- parser = activityInfo.loadXmlMetaData(mPackageManager,
- AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
- if (parser == null) {
- Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER + " meta-data for "
- + "AppWidget provider '" + component + '\'');
- return null;
- }
-
- AttributeSet attrs = Xml.asAttributeSet(parser);
-
- int type;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.START_TAG) {
- // drain whitespace, comments, etc.
- }
-
- String nodeName = parser.getName();
- if (!"appwidget-provider".equals(nodeName)) {
- Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
- + " AppWidget provider '" + component + '\'');
- return null;
- }
-
- p = new Provider();
- AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
- info.provider = component;
- p.uid = activityInfo.applicationInfo.uid;
-
- Resources res = mPackageManager.getResourcesForApplication(
- activityInfo.applicationInfo);
-
- TypedArray sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AppWidgetProviderInfo);
- // These dimensions has to be resolved in the application's context.
- // We simply send back the raw complex data, which will be
- // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
- TypedValue value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
- info.minWidth = value != null ? value.data : 0;
- value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
- info.minHeight = value != null ? value.data : 0;
- value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
- info.minResizeWidth = value != null ? value.data : info.minWidth;
- value = sa.peekValue(
- com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
- info.minResizeHeight = value != null ? value.data : info.minHeight;
-
- info.updatePeriodMillis = sa.getInt(
- com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
- info.initialLayout = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
- String className = sa.getString(
- com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
- if (className != null) {
- info.configure = new ComponentName(component.getPackageName(), className);
- }
- info.label = activityInfo.loadLabel(mPackageManager).toString();
- info.icon = ri.getIconResource();
- info.previewImage = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
- info.autoAdvanceViewId = sa.getResourceId(
- com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
- info.resizeMode = sa.getInt(
- com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
- AppWidgetProviderInfo.RESIZE_NONE);
-
- sa.recycle();
- } catch (Exception e) {
- // Ok to catch Exception here, because anything going wrong because
- // of what a client process passes to us should not be fatal for the
- // system process.
- Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
- return null;
- } finally {
- if (parser != null) parser.close();
- }
- return p;
+ @Override
+ public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
+ return getImplForUser().getInstalledProviders();
}
- int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
- PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
- if (pkgInfo == null || pkgInfo.applicationInfo == null) {
- throw new PackageManager.NameNotFoundException();
- }
- return pkgInfo.applicationInfo.uid;
+ @Override
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
+ throws RemoteException {
+ getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
}
- int enforceCallingUid(String packageName) throws IllegalArgumentException {
- int callingUid = getCallingUid();
- int packageUid;
- try {
- packageUid = getUidForPackage(packageName);
- } catch (PackageManager.NameNotFoundException ex) {
- throw new IllegalArgumentException("packageName and uid don't match packageName="
- + packageName);
- }
- if (callingUid != packageUid) {
- throw new IllegalArgumentException("packageName and uid don't match packageName="
- + packageName);
- }
- return callingUid;
+ @Override
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
+ throws RemoteException {
+ getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
}
- void sendInitialBroadcasts() {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.instances.size() > 0) {
- sendEnableIntentLocked(p);
- int[] appWidgetIds = getAppWidgetIds(p);
- sendUpdateIntentLocked(p, appWidgetIds);
- registerForBroadcastsLocked(p, appWidgetIds);
- }
- }
- }
+ @Override
+ public void stopListening(int hostId) throws RemoteException {
+ getImplForUser().stopListening(hostId);
}
- // only call from initialization -- it assumes that the data structures are all empty
- void loadStateLocked() {
- AtomicFile file = savedStateFile();
- try {
- FileInputStream stream = file.openRead();
- readStateFromFileLocked(stream);
-
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to close state FileInputStream " + e);
- }
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "Failed to read state: " + e);
- }
+ @Override
+ public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
+ getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
}
- void saveStateLocked() {
- AtomicFile file = savedStateFile();
- FileOutputStream stream;
- try {
- stream = file.startWrite();
- if (writeStateToFileLocked(stream)) {
- file.finishWrite(stream);
- } else {
- file.failWrite(stream);
- Slog.w(TAG, "Failed to save state, restoring backup.");
- }
- } catch (IOException e) {
- Slog.w(TAG, "Failed open state file for write: " + e);
- }
+ @Override
+ public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
+ getImplForUser().updateAppWidgetIds(appWidgetIds, views);
}
- boolean writeStateToFileLocked(FileOutputStream stream) {
- int N;
-
- try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, "utf-8");
- out.startDocument(null, true);
- out.startTag(null, "gs");
-
- int providerIndex = 0;
- N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- Provider p = mInstalledProviders.get(i);
- if (p.instances.size() > 0) {
- out.startTag(null, "p");
- out.attribute(null, "pkg", p.info.provider.getPackageName());
- out.attribute(null, "cl", p.info.provider.getClassName());
- out.endTag(null, "p");
- p.tag = providerIndex;
- providerIndex++;
- }
- }
-
- N = mHosts.size();
- for (int i=0; i<N; i++) {
- Host host = mHosts.get(i);
- out.startTag(null, "h");
- out.attribute(null, "pkg", host.packageName);
- out.attribute(null, "id", Integer.toHexString(host.hostId));
- out.endTag(null, "h");
- host.tag = i;
- }
-
- N = mAppWidgetIds.size();
- for (int i=0; i<N; i++) {
- AppWidgetId id = mAppWidgetIds.get(i);
- out.startTag(null, "g");
- out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
- out.attribute(null, "h", Integer.toHexString(id.host.tag));
- if (id.provider != null) {
- out.attribute(null, "p", Integer.toHexString(id.provider.tag));
- }
- out.endTag(null, "g");
- }
-
- out.endTag(null, "gs");
-
- out.endDocument();
- return true;
- } catch (IOException e) {
- Slog.w(TAG, "Failed to write state: " + e);
- return false;
- }
+ @Override
+ public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
+ throws RemoteException {
+ getImplForUser().updateAppWidgetProvider(provider, views);
}
- void readStateFromFileLocked(FileInputStream stream) {
- boolean success = false;
-
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, null);
-
- int type;
- int providerIndex = 0;
- HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
- do {
- type = parser.next();
- if (type == XmlPullParser.START_TAG) {
- String tag = parser.getName();
- if ("p".equals(tag)) {
- // TODO: do we need to check that this package has the same signature
- // as before?
- String pkg = parser.getAttributeValue(null, "pkg");
- String cl = parser.getAttributeValue(null, "cl");
-
- final PackageManager packageManager = mContext.getPackageManager();
- try {
- packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
- } catch (PackageManager.NameNotFoundException e) {
- String[] pkgs = packageManager.currentToCanonicalPackageNames(
- new String[] { pkg });
- pkg = pkgs[0];
- }
-
- Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
- if (p == null && mSafeMode) {
- // if we're in safe mode, make a temporary one
- p = new Provider();
- p.info = new AppWidgetProviderInfo();
- p.info.provider = new ComponentName(pkg, cl);
- p.zombie = true;
- mInstalledProviders.add(p);
- }
- if (p != null) {
- // if it wasn't uninstalled or something
- loadedProviders.put(providerIndex, p);
- }
- providerIndex++;
- }
- else if ("h".equals(tag)) {
- Host host = new Host();
-
- // TODO: do we need to check that this package has the same signature
- // as before?
- host.packageName = parser.getAttributeValue(null, "pkg");
- try {
- host.uid = getUidForPackage(host.packageName);
- } catch (PackageManager.NameNotFoundException ex) {
- host.zombie = true;
- }
- if (!host.zombie || mSafeMode) {
- // In safe mode, we don't discard the hosts we don't recognize
- // so that they're not pruned from our list. Otherwise, we do.
- host.hostId = Integer.parseInt(
- parser.getAttributeValue(null, "id"), 16);
- mHosts.add(host);
- }
- }
- else if ("g".equals(tag)) {
- AppWidgetId id = new AppWidgetId();
- id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
- if (id.appWidgetId >= mNextAppWidgetId) {
- mNextAppWidgetId = id.appWidgetId + 1;
- }
-
- String providerString = parser.getAttributeValue(null, "p");
- if (providerString != null) {
- // there's no provider if it hasn't been bound yet.
- // maybe we don't have to save this, but it brings the system
- // to the state it was in.
- int pIndex = Integer.parseInt(providerString, 16);
- id.provider = loadedProviders.get(pIndex);
- if (false) {
- Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
- + pIndex + " which is " + id.provider);
- }
- if (id.provider == null) {
- // This provider is gone. We just let the host figure out
- // that this happened when it fails to load it.
- continue;
- }
- }
-
- int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
- id.host = mHosts.get(hIndex);
- if (id.host == null) {
- // This host is gone.
- continue;
- }
-
- if (id.provider != null) {
- id.provider.instances.add(id);
- }
- id.host.instances.add(id);
- mAppWidgetIds.add(id);
- }
- }
- } while (type != XmlPullParser.END_DOCUMENT);
- success = true;
- } catch (NullPointerException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (IOException e) {
- Slog.w(TAG, "failed parsing " + e);
- } catch (IndexOutOfBoundsException e) {
- Slog.w(TAG, "failed parsing " + e);
- }
-
- if (success) {
- // delete any hosts that didn't manage to get connected (should happen)
- // if it matters, they'll be reconnected.
- for (int i=mHosts.size()-1; i>=0; i--) {
- pruneHostLocked(mHosts.get(i));
- }
- } else {
- // failed reading, clean up
- Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
-
- mAppWidgetIds.clear();
- mHosts.clear();
- final int N = mInstalledProviders.size();
- for (int i=0; i<N; i++) {
- mInstalledProviders.get(i).instances.clear();
- }
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ // Dump the state of all the app widget providers
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.dump(fd, pw, args);
}
}
- AtomicFile savedStateFile() {
- return new AtomicFile(new File("/data/system/" + SETTINGS_FILENAME));
- }
-
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- //Slog.d(TAG, "received " + action);
+ // Slog.d(TAG, "received " + action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- sendInitialBroadcasts();
+ getImplForUser().sendInitialBroadcasts();
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
- Locale revised = Locale.getDefault();
- if (revised == null || mLocale == null ||
- !(revised.equals(mLocale))) {
- mLocale = revised;
-
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- int N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- String pkgName = p.info.provider.getPackageName();
- updateProvidersForPackageLocked(pkgName);
- }
- saveStateLocked();
- }
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.onConfigurationChanged();
}
} else {
- boolean added = false;
- boolean changed = false;
- String pkgList[] = null;
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- added = true;
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- added = false;
- } else {
- Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
- String pkgName = uri.getSchemeSpecificPart();
- if (pkgName == null) {
- return;
- }
- pkgList = new String[] { pkgName };
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
- }
- if (pkgList == null || pkgList.length == 0) {
- return;
- }
- if (added || changed) {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- Bundle extras = intent.getExtras();
- if (changed || (extras != null &&
- extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
- for (String pkgName : pkgList) {
- // The package was just upgraded
- updateProvidersForPackageLocked(pkgName);
- }
- } else {
- // The package was just added
- for (String pkgName : pkgList) {
- addProvidersForPackageLocked(pkgName);
- }
- }
- saveStateLocked();
- }
- } else {
- Bundle extras = intent.getExtras();
- if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
- // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
- } else {
- synchronized (mAppWidgetIds) {
- ensureStateLoadedLocked();
- for (String pkgName : pkgList) {
- removeProvidersForPackageLocked(pkgName);
- saveStateLocked();
- }
- }
- }
- }
+ // TODO: Verify that this only needs to be delivered for the related user and not
+ // all the users
+ getImplForUser().onBroadcastReceived(intent);
}
}
};
-
- void addProvidersForPackageLocked(String pkgName) {
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.setPackage(pkgName);
- List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- ActivityInfo ai = ri.activityInfo;
- if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- continue;
- }
- if (pkgName.equals(ai.packageName)) {
- addProviderLocked(ri);
- }
- }
- }
-
- void updateProvidersForPackageLocked(String pkgName) {
- HashSet<String> keep = new HashSet<String>();
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
- intent.setPackage(pkgName);
- List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
- PackageManager.GET_META_DATA);
-
- // add the missing ones and collect which ones to keep
- int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
- for (int i=0; i<N; i++) {
- ResolveInfo ri = broadcastReceivers.get(i);
- ActivityInfo ai = ri.activityInfo;
- if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
- continue;
- }
- if (pkgName.equals(ai.packageName)) {
- ComponentName component = new ComponentName(ai.packageName, ai.name);
- Provider p = lookupProviderLocked(component);
- if (p == null) {
- if (addProviderLocked(ri)) {
- keep.add(ai.name);
- }
- } else {
- Provider parsed = parseProviderInfoXml(component, ri);
- if (parsed != null) {
- keep.add(ai.name);
- // Use the new AppWidgetProviderInfo.
- p.info = parsed.info;
- // If it's enabled
- final int M = p.instances.size();
- if (M > 0) {
- int[] appWidgetIds = getAppWidgetIds(p);
- // Reschedule for the new updatePeriodMillis (don't worry about handling
- // it specially if updatePeriodMillis didn't change because we just sent
- // an update, and the next one will be updatePeriodMillis from now).
- cancelBroadcasts(p);
- registerForBroadcastsLocked(p, appWidgetIds);
- // If it's currently showing, call back with the new AppWidgetProviderInfo.
- for (int j=0; j<M; j++) {
- AppWidgetId id = p.instances.get(j);
- id.views = null;
- if (id.host != null && id.host.callbacks != null) {
- try {
- id.host.callbacks.providerChanged(id.appWidgetId, p.info);
- } catch (RemoteException ex) {
- // It failed; remove the callback. No need to prune because
- // we know that this host is still referenced by this
- // instance.
- id.host.callbacks = null;
- }
- }
- }
- // Now that we've told the host, push out an update.
- sendUpdateIntentLocked(p, appWidgetIds);
- }
- }
- }
- }
- }
-
- // prune the ones we don't want to keep
- N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- if (pkgName.equals(p.info.provider.getPackageName())
- && !keep.contains(p.info.provider.getClassName())) {
- removeProviderLocked(i, p);
- }
- }
- }
-
- void removeProvidersForPackageLocked(String pkgName) {
- int N = mInstalledProviders.size();
- for (int i=N-1; i>=0; i--) {
- Provider p = mInstalledProviders.get(i);
- if (pkgName.equals(p.info.provider.getPackageName())) {
- removeProviderLocked(i, p);
- }
- }
-
- // Delete the hosts for this package too
- //
- // By now, we have removed any AppWidgets that were in any hosts here,
- // so we don't need to worry about sending DISABLE broadcasts to them.
- N = mHosts.size();
- for (int i=N-1; i>=0; i--) {
- Host host = mHosts.get(i);
- if (pkgName.equals(host.packageName)) {
- deleteHostLocked(host);
- }
- }
- }
}
-
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
new file mode 100644
index 0000000..250386f
--- /dev/null
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -0,0 +1,1606 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.Intent.FilterComparison;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserId;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.widget.RemoteViews;
+
+import com.android.internal.appwidget.IAppWidgetHost;
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.widget.IRemoteViewsAdapterConnection;
+import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.server.am.ActivityManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+class AppWidgetServiceImpl {
+
+ private static final String TAG = "AppWidgetServiceImpl";
+ private static final String SETTINGS_FILENAME = "appwidgets.xml";
+ private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
+
+ /*
+ * When identifying a Host or Provider based on the calling process, use the uid field. When
+ * identifying a Host or Provider based on a package manager broadcast, use the package given.
+ */
+
+ static class Provider {
+ int uid;
+ AppWidgetProviderInfo info;
+ ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+ PendingIntent broadcast;
+ boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+ int tag; // for use while saving state (the index)
+ }
+
+ static class Host {
+ int uid;
+ int hostId;
+ String packageName;
+ ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
+ IAppWidgetHost callbacks;
+ boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
+
+ int tag; // for use while saving state (the index)
+ }
+
+ static class AppWidgetId {
+ int appWidgetId;
+ Provider provider;
+ RemoteViews views;
+ Host host;
+ }
+
+ /**
+ * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. This
+ * needs to be a static inner class since a reference to the ServiceConnection is held globally
+ * and may lead us to leak AppWidgetService instances (if there were more than one).
+ */
+ static class ServiceConnectionProxy implements ServiceConnection {
+ private final IBinder mConnectionCb;
+
+ ServiceConnectionProxy(Pair<Integer, Intent.FilterComparison> key, IBinder connectionCb) {
+ mConnectionCb = connectionCb;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+ .asInterface(mConnectionCb);
+ try {
+ cb.onServiceConnected(service);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ disconnect();
+ }
+
+ public void disconnect() {
+ final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub
+ .asInterface(mConnectionCb);
+ try {
+ cb.onServiceDisconnected();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ // Manages active connections to RemoteViewsServices
+ private final HashMap<Pair<Integer, FilterComparison>, ServiceConnection> mBoundRemoteViewsServices = new HashMap<Pair<Integer, FilterComparison>, ServiceConnection>();
+ // Manages persistent references to RemoteViewsServices from different App Widgets
+ private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
+
+ Context mContext;
+ Locale mLocale;
+ PackageManager mPackageManager;
+ AlarmManager mAlarmManager;
+ ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
+ int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
+ final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
+ ArrayList<Host> mHosts = new ArrayList<Host>();
+ boolean mSafeMode;
+ int mUserId;
+ boolean mStateLoaded;
+
+ // These are for debugging only -- widgets are going missing in some rare instances
+ ArrayList<Provider> mDeletedProviders = new ArrayList<Provider>();
+ ArrayList<Host> mDeletedHosts = new ArrayList<Host>();
+
+ AppWidgetServiceImpl(Context context, int userId) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ mUserId = userId;
+ }
+
+ public void systemReady(boolean safeMode) {
+ mSafeMode = safeMode;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ }
+ }
+
+ void onConfigurationChanged() {
+ Locale revised = Locale.getDefault();
+ if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
+ mLocale = revised;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ String pkgName = p.info.provider.getPackageName();
+ updateProvidersForPackageLocked(pkgName);
+ }
+ saveStateLocked();
+ }
+ }
+ }
+
+ void onBroadcastReceived(Intent intent) {
+ final String action = intent.getAction();
+ boolean added = false;
+ boolean changed = false;
+ String pkgList[] = null;
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ added = true;
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ added = false;
+ } else {
+ Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ String pkgName = uri.getSchemeSpecificPart();
+ if (pkgName == null) {
+ return;
+ }
+ pkgList = new String[] { pkgName };
+ added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+ }
+ if (pkgList == null || pkgList.length == 0) {
+ return;
+ }
+ if (added || changed) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Bundle extras = intent.getExtras();
+ if (changed
+ || (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false))) {
+ for (String pkgName : pkgList) {
+ // The package was just upgraded
+ updateProvidersForPackageLocked(pkgName);
+ }
+ } else {
+ // The package was just added
+ for (String pkgName : pkgList) {
+ addProvidersForPackageLocked(pkgName);
+ }
+ }
+ saveStateLocked();
+ }
+ } else {
+ Bundle extras = intent.getExtras();
+ if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
+ } else {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (String pkgName : pkgList) {
+ removeProvidersForPackageLocked(pkgName);
+ saveStateLocked();
+ }
+ }
+ }
+ }
+ }
+
+ private void dumpProvider(Provider p, int index, PrintWriter pw) {
+ AppWidgetProviderInfo info = p.info;
+ pw.print(" ["); pw.print(index); pw.print("] provider ");
+ pw.print(info.provider.flattenToShortString());
+ pw.println(':');
+ pw.print(" min=("); pw.print(info.minWidth);
+ pw.print("x"); pw.print(info.minHeight);
+ pw.print(") minResize=("); pw.print(info.minResizeWidth);
+ pw.print("x"); pw.print(info.minResizeHeight);
+ pw.print(") updatePeriodMillis=");
+ pw.print(info.updatePeriodMillis);
+ pw.print(" resizeMode=");
+ pw.print(info.resizeMode);
+ pw.print(" autoAdvanceViewId=");
+ pw.print(info.autoAdvanceViewId);
+ pw.print(" initialLayout=#");
+ pw.print(Integer.toHexString(info.initialLayout));
+ pw.print(" zombie="); pw.println(p.zombie);
+ }
+
+ private void dumpHost(Host host, int index, PrintWriter pw) {
+ pw.print(" ["); pw.print(index); pw.print("] hostId=");
+ pw.print(host.hostId); pw.print(' ');
+ pw.print(host.packageName); pw.print('/');
+ pw.print(host.uid); pw.println(':');
+ pw.print(" callbacks="); pw.println(host.callbacks);
+ pw.print(" instances.size="); pw.print(host.instances.size());
+ pw.print(" zombie="); pw.println(host.zombie);
+ }
+
+ private void dumpAppWidgetId(AppWidgetId id, int index, PrintWriter pw) {
+ pw.print(" ["); pw.print(index); pw.print("] id=");
+ pw.println(id.appWidgetId);
+ pw.print(" hostId=");
+ pw.print(id.host.hostId); pw.print(' ');
+ pw.print(id.host.packageName); pw.print('/');
+ pw.println(id.host.uid);
+ if (id.provider != null) {
+ pw.print(" provider=");
+ pw.println(id.provider.info.provider.flattenToShortString());
+ }
+ if (id.host != null) {
+ pw.print(" host.callbacks="); pw.println(id.host.callbacks);
+ }
+ if (id.views != null) {
+ pw.print(" views="); pw.println(id.views);
+ }
+ }
+
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mAppWidgetIds) {
+ int N = mInstalledProviders.size();
+ pw.println("Providers:");
+ for (int i=0; i<N; i++) {
+ dumpProvider(mInstalledProviders.get(i), i, pw);
+ }
+
+ N = mAppWidgetIds.size();
+ pw.println(" ");
+ pw.println("AppWidgetIds:");
+ for (int i=0; i<N; i++) {
+ dumpAppWidgetId(mAppWidgetIds.get(i), i, pw);
+ }
+
+ N = mHosts.size();
+ pw.println(" ");
+ pw.println("Hosts:");
+ for (int i=0; i<N; i++) {
+ dumpHost(mHosts.get(i), i, pw);
+ }
+
+ N = mDeletedProviders.size();
+ pw.println(" ");
+ pw.println("Deleted Providers:");
+ for (int i=0; i<N; i++) {
+ dumpProvider(mDeletedProviders.get(i), i, pw);
+ }
+
+ N = mDeletedHosts.size();
+ pw.println(" ");
+ pw.println("Deleted Hosts:");
+ for (int i=0; i<N; i++) {
+ dumpHost(mDeletedHosts.get(i), i, pw);
+ }
+ }
+ }
+
+ private void ensureStateLoadedLocked() {
+ if (!mStateLoaded) {
+ loadAppWidgetList();
+ loadStateLocked();
+ mStateLoaded = true;
+ }
+ }
+
+ public int allocateAppWidgetId(String packageName, int hostId) {
+ int callingUid = enforceCallingUid(packageName);
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int appWidgetId = mNextAppWidgetId++;
+
+ Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+
+ AppWidgetId id = new AppWidgetId();
+ id.appWidgetId = appWidgetId;
+ id.host = host;
+
+ host.instances.add(id);
+ mAppWidgetIds.add(id);
+
+ saveStateLocked();
+
+ return appWidgetId;
+ }
+ }
+
+ public void deleteAppWidgetId(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null) {
+ deleteAppWidgetLocked(id);
+ saveStateLocked();
+ }
+ }
+ }
+
+ public void deleteHost(int hostId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int callingUid = Binder.getCallingUid();
+ Host host = lookupHostLocked(callingUid, hostId);
+ if (host != null) {
+ deleteHostLocked(host);
+ saveStateLocked();
+ }
+ }
+ }
+
+ public void deleteAllHosts() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ int callingUid = Binder.getCallingUid();
+ final int N = mHosts.size();
+ boolean changed = false;
+ for (int i = N - 1; i >= 0; i--) {
+ Host host = mHosts.get(i);
+ if (host.uid == callingUid) {
+ deleteHostLocked(host);
+ changed = true;
+ }
+ }
+ if (changed) {
+ saveStateLocked();
+ }
+ }
+ }
+
+ void deleteHostLocked(Host host) {
+ final int N = host.instances.size();
+ for (int i = N - 1; i >= 0; i--) {
+ AppWidgetId id = host.instances.get(i);
+ deleteAppWidgetLocked(id);
+ }
+ host.instances.clear();
+ mHosts.remove(host);
+ mDeletedHosts.add(host);
+ // it's gone or going away, abruptly drop the callback connection
+ host.callbacks = null;
+ }
+
+ void deleteAppWidgetLocked(AppWidgetId id) {
+ // We first unbind all services that are bound to this id
+ unbindAppWidgetRemoteViewsServicesLocked(id);
+
+ Host host = id.host;
+ host.instances.remove(id);
+ pruneHostLocked(host);
+
+ mAppWidgetIds.remove(id);
+
+ Provider p = id.provider;
+ if (p != null) {
+ p.instances.remove(id);
+ if (!p.zombie) {
+ // send the broacast saying that this appWidgetId has been deleted
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED);
+ intent.setComponent(p.info.provider);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId);
+ mContext.sendBroadcast(intent);
+ if (p.instances.size() == 0) {
+ // cancel the future updates
+ cancelBroadcasts(p);
+
+ // send the broacast saying that the provider is not in use any more
+ intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+ }
+ }
+ }
+
+ void cancelBroadcasts(Provider p) {
+ if (p.broadcast != null) {
+ mAlarmManager.cancel(p.broadcast);
+ long token = Binder.clearCallingIdentity();
+ try {
+ p.broadcast.cancel();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ p.broadcast = null;
+ }
+ }
+
+ public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
+ "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+ if (id.provider != null) {
+ throw new IllegalArgumentException("appWidgetId " + appWidgetId
+ + " already bound to " + id.provider.info.provider);
+ }
+ Provider p = lookupProviderLocked(provider);
+ if (p == null) {
+ throw new IllegalArgumentException("not a appwidget provider: " + provider);
+ }
+ if (p.zombie) {
+ throw new IllegalArgumentException("can't bind to a 3rd party provider in"
+ + " safe mode: " + provider);
+ }
+
+ Binder.restoreCallingIdentity(ident);
+
+ id.provider = p;
+ p.instances.add(id);
+ int instancesSize = p.instances.size();
+ if (instancesSize == 1) {
+ // tell the provider that it's ready
+ sendEnableIntentLocked(p);
+ }
+
+ // send an update now -- We need this update now, and just for this appWidgetId.
+ // It's less critical when the next one happens, so when we schedule the next one,
+ // we add updatePeriodMillis to its start time. That time will have some slop,
+ // but that's okay.
+ sendUpdateIntentLocked(p, new int[] { appWidgetId });
+
+ // schedule the future updates
+ registerForBroadcastsLocked(p, getAppWidgetIds(p));
+ saveStateLocked();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ // Binds to a specific RemoteViewsService
+ public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+ final ComponentName componentName = intent.getComponent();
+ try {
+ final ServiceInfo si = mContext.getPackageManager().getServiceInfo(componentName,
+ PackageManager.GET_PERMISSIONS);
+ if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
+ throw new SecurityException("Selected service does not require "
+ + android.Manifest.permission.BIND_REMOTEVIEWS + ": " + componentName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown component " + componentName);
+ }
+
+ // If there is already a connection made for this service intent, then disconnect from
+ // that first. (This does not allow multiple connections to the same service under
+ // the same key)
+ ServiceConnectionProxy conn = null;
+ FilterComparison fc = new FilterComparison(intent);
+ Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, fc);
+ if (mBoundRemoteViewsServices.containsKey(key)) {
+ conn = (ServiceConnectionProxy) mBoundRemoteViewsServices.get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ mBoundRemoteViewsServices.remove(key);
+ }
+
+ // Bind to the RemoteViewsService (which will trigger a callback to the
+ // RemoteViewsAdapter.onServiceConnected())
+ final long token = Binder.clearCallingIdentity();
+ try {
+ conn = new ServiceConnectionProxy(key, connection);
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ mBoundRemoteViewsServices.put(key, conn);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ // Add it to the mapping of RemoteViewsService to appWidgetIds so that we can determine
+ // when we can call back to the RemoteViewsService later to destroy associated
+ // factories.
+ incrementAppWidgetServiceRefCount(appWidgetId, fc);
+ }
+ }
+
+ // Unbinds from a specific RemoteViewsService
+ public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ // Unbind from the RemoteViewsService (which will trigger a callback to the bound
+ // RemoteViewsAdapter)
+ Pair<Integer, FilterComparison> key = Pair.create(appWidgetId, new FilterComparison(
+ intent));
+ if (mBoundRemoteViewsServices.containsKey(key)) {
+ // We don't need to use the appWidgetId until after we are sure there is something
+ // to unbind. Note that this may mask certain issues with apps calling unbind()
+ // more than necessary.
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id == null) {
+ throw new IllegalArgumentException("bad appWidgetId");
+ }
+
+ ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+ .get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ mBoundRemoteViewsServices.remove(key);
+ } else {
+ Log.e("AppWidgetService", "Error (unbindRemoteViewsService): Connection not bound");
+ }
+ }
+ }
+
+ // Unbinds from a RemoteViewsService when we delete an app widget
+ private void unbindAppWidgetRemoteViewsServicesLocked(AppWidgetId id) {
+ int appWidgetId = id.appWidgetId;
+ // Unbind all connections to Services bound to this AppWidgetId
+ Iterator<Pair<Integer, Intent.FilterComparison>> it = mBoundRemoteViewsServices.keySet()
+ .iterator();
+ while (it.hasNext()) {
+ final Pair<Integer, Intent.FilterComparison> key = it.next();
+ if (key.first.intValue() == appWidgetId) {
+ final ServiceConnectionProxy conn = (ServiceConnectionProxy) mBoundRemoteViewsServices
+ .get(key);
+ conn.disconnect();
+ mContext.unbindService(conn);
+ it.remove();
+ }
+ }
+
+ // Check if we need to destroy any services (if no other app widgets are
+ // referencing the same service)
+ decrementAppWidgetServiceRefCount(appWidgetId);
+ }
+
+ // Destroys the cached factory on the RemoteViewsService's side related to the specified intent
+ private void destroyRemoteViewsService(final Intent intent) {
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IRemoteViewsFactory cb = IRemoteViewsFactory.Stub.asInterface(service);
+ try {
+ cb.onDestroy(intent);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ mContext.unbindService(this);
+ }
+
+ @Override
+ public void onServiceDisconnected(android.content.ComponentName name) {
+ // Do nothing
+ }
+ };
+
+ // Bind to the service and remove the static intent->factory mapping in the
+ // RemoteViewsService.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // Adds to the ref-count for a given RemoteViewsService intent
+ private void incrementAppWidgetServiceRefCount(int appWidgetId, FilterComparison fc) {
+ HashSet<Integer> appWidgetIds = null;
+ if (mRemoteViewsServicesAppWidgets.containsKey(fc)) {
+ appWidgetIds = mRemoteViewsServicesAppWidgets.get(fc);
+ } else {
+ appWidgetIds = new HashSet<Integer>();
+ mRemoteViewsServicesAppWidgets.put(fc, appWidgetIds);
+ }
+ appWidgetIds.add(appWidgetId);
+ }
+
+ // Subtracts from the ref-count for a given RemoteViewsService intent, prompting a delete if
+ // the ref-count reaches zero.
+ private void decrementAppWidgetServiceRefCount(int appWidgetId) {
+ Iterator<FilterComparison> it = mRemoteViewsServicesAppWidgets.keySet().iterator();
+ while (it.hasNext()) {
+ final FilterComparison key = it.next();
+ final HashSet<Integer> ids = mRemoteViewsServicesAppWidgets.get(key);
+ if (ids.remove(appWidgetId)) {
+ // If we have removed the last app widget referencing this service, then we
+ // should destroy it and remove it from this set
+ if (ids.isEmpty()) {
+ destroyRemoteViewsService(key.getIntent());
+ it.remove();
+ }
+ }
+ }
+ }
+
+ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null && id.provider != null && !id.provider.zombie) {
+ return id.provider.info;
+ }
+ return null;
+ }
+ }
+
+ public RemoteViews getAppWidgetViews(int appWidgetId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
+ if (id != null) {
+ return id.views;
+ }
+ return null;
+ }
+ }
+
+ public List<AppWidgetProviderInfo> getInstalledProviders() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ final int N = mInstalledProviders.size();
+ ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (!p.zombie) {
+ result.add(p.info);
+ }
+ }
+ return result;
+ }
+ }
+
+ public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ updateAppWidgetInstanceLocked(id, views);
+ }
+ }
+ }
+
+ public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ updateAppWidgetInstanceLocked(id, views, true);
+ }
+ }
+ }
+
+ public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+ if (appWidgetIds == null) {
+ return;
+ }
+ if (appWidgetIds.length == 0) {
+ return;
+ }
+ final int N = appWidgetIds.length;
+
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
+ notifyAppWidgetViewDataChangedInstanceLocked(id, viewId);
+ }
+ }
+ }
+
+ public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Provider p = lookupProviderLocked(provider);
+ if (p == null) {
+ Slog.w(TAG, "updateAppWidgetProvider: provider doesn't exist: " + provider);
+ return;
+ }
+ ArrayList<AppWidgetId> instances = p.instances;
+ final int callingUid = Binder.getCallingUid();
+ final int N = instances.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = instances.get(i);
+ if (canAccessAppWidgetId(id, callingUid)) {
+ updateAppWidgetInstanceLocked(id, views);
+ }
+ }
+ }
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
+ updateAppWidgetInstanceLocked(id, views, false);
+ }
+
+ void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views, boolean isPartialUpdate) {
+ // allow for stale appWidgetIds and other badness
+ // lookup also checks that the calling process can access the appWidgetId
+ // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+ if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+
+ // We do not want to save this RemoteViews
+ if (!isPartialUpdate)
+ id.views = views;
+
+ // is anyone listening?
+ if (id.host.callbacks != null) {
+ try {
+ // the lock is held, but this is a oneway call
+ id.host.callbacks.updateAppWidget(id.appWidgetId, views);
+ } catch (RemoteException e) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this instance.
+ id.host.callbacks = null;
+ }
+ }
+ }
+ }
+
+ void notifyAppWidgetViewDataChangedInstanceLocked(AppWidgetId id, int viewId) {
+ // allow for stale appWidgetIds and other badness
+ // lookup also checks that the calling process can access the appWidgetId
+ // drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
+ if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
+ // is anyone listening?
+ if (id.host.callbacks != null) {
+ try {
+ // the lock is held, but this is a oneway call
+ id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
+ } catch (RemoteException e) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this instance.
+ id.host.callbacks = null;
+ }
+ }
+
+ // If the host is unavailable, then we call the associated
+ // RemoteViewsFactory.onDataSetChanged() directly
+ if (id.host.callbacks == null) {
+ Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
+ for (FilterComparison key : keys) {
+ if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
+ Intent intent = key.getIntent();
+
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IRemoteViewsFactory cb = IRemoteViewsFactory.Stub
+ .asInterface(service);
+ try {
+ cb.onDataSetChangedAsync();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
+ mContext.unbindService(this);
+ }
+
+ @Override
+ public void onServiceDisconnected(android.content.ComponentName name) {
+ // Do nothing
+ }
+ };
+
+ // Bind to the service and call onDataSetChanged()
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
+ List<RemoteViews> updatedViews) {
+ int callingUid = enforceCallingUid(packageName);
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
+ host.callbacks = callbacks;
+
+ updatedViews.clear();
+
+ ArrayList<AppWidgetId> instances = host.instances;
+ int N = instances.size();
+ int[] updatedIds = new int[N];
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = instances.get(i);
+ updatedIds[i] = id.appWidgetId;
+ updatedViews.add(id.views);
+ }
+ return updatedIds;
+ }
+ }
+
+ public void stopListening(int hostId) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
+ if (host != null) {
+ host.callbacks = null;
+ pruneHostLocked(host);
+ }
+ }
+ }
+
+ boolean canAccessAppWidgetId(AppWidgetId id, int callingUid) {
+ if (id.host.uid == callingUid) {
+ // Apps hosting the AppWidget have access to it.
+ return true;
+ }
+ if (id.provider != null && id.provider.uid == callingUid) {
+ // Apps providing the AppWidget have access to it (if the appWidgetId has been bound)
+ return true;
+ }
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET) == PackageManager.PERMISSION_GRANTED) {
+ // Apps that can bind have access to all appWidgetIds.
+ return true;
+ }
+ // Nobody else can access it.
+ return false;
+ }
+
+ AppWidgetId lookupAppWidgetIdLocked(int appWidgetId) {
+ int callingUid = Binder.getCallingUid();
+ final int N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ if (id.appWidgetId == appWidgetId && canAccessAppWidgetId(id, callingUid)) {
+ return id;
+ }
+ }
+ return null;
+ }
+
+ Provider lookupProviderLocked(ComponentName provider) {
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.info.provider.equals(provider)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ Host lookupHostLocked(int uid, int hostId) {
+ final int N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host h = mHosts.get(i);
+ if (h.uid == uid && h.hostId == hostId) {
+ return h;
+ }
+ }
+ return null;
+ }
+
+ Host lookupOrAddHostLocked(int uid, String packageName, int hostId) {
+ final int N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host h = mHosts.get(i);
+ if (h.hostId == hostId && h.packageName.equals(packageName)) {
+ return h;
+ }
+ }
+ Host host = new Host();
+ host.packageName = packageName;
+ host.uid = uid;
+ host.hostId = hostId;
+ mHosts.add(host);
+ return host;
+ }
+
+ void pruneHostLocked(Host host) {
+ if (host.instances.size() == 0 && host.callbacks == null) {
+ mHosts.remove(host);
+ }
+ }
+
+ void loadAppWidgetList() {
+ PackageManager pm = mPackageManager;
+
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ addProviderLocked(ri);
+ }
+ }
+
+ boolean addProviderLocked(ResolveInfo ri) {
+ if ((ri.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ return false;
+ }
+ if (!ri.activityInfo.isEnabled()) {
+ return false;
+ }
+ Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
+ ri.activityInfo.name), ri);
+ if (p != null) {
+ mInstalledProviders.add(p);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void removeProviderLocked(int index, Provider p) {
+ int N = p.instances.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = p.instances.get(i);
+ // Call back with empty RemoteViews
+ updateAppWidgetInstanceLocked(id, null);
+ // Stop telling the host about updates for this from now on
+ cancelBroadcasts(p);
+ // clear out references to this appWidgetId
+ id.host.instances.remove(id);
+ mAppWidgetIds.remove(id);
+ id.provider = null;
+ pruneHostLocked(id.host);
+ id.host = null;
+ }
+ p.instances.clear();
+ mInstalledProviders.remove(index);
+ mDeletedProviders.add(p);
+ // no need to send the DISABLE broadcast, since the receiver is gone anyway
+ cancelBroadcasts(p);
+ }
+
+ void sendEnableIntentLocked(Provider p) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+
+ void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) {
+ if (appWidgetIds != null && appWidgetIds.length > 0) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+ intent.setComponent(p.info.provider);
+ mContext.sendBroadcast(intent);
+ }
+ }
+
+ void registerForBroadcastsLocked(Provider p, int[] appWidgetIds) {
+ if (p.info.updatePeriodMillis > 0) {
+ // if this is the first instance, set the alarm. otherwise,
+ // rely on the fact that we've already set it and that
+ // PendingIntent.getBroadcast will update the extras.
+ boolean alreadyRegistered = p.broadcast != null;
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+ intent.setComponent(p.info.provider);
+ long token = Binder.clearCallingIdentity();
+ try {
+ p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (!alreadyRegistered) {
+ long period = p.info.updatePeriodMillis;
+ if (period < MIN_UPDATE_PERIOD) {
+ period = MIN_UPDATE_PERIOD;
+ }
+ mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
+ .elapsedRealtime()
+ + period, period, p.broadcast);
+ }
+ }
+ }
+
+ static int[] getAppWidgetIds(Provider p) {
+ int instancesSize = p.instances.size();
+ int appWidgetIds[] = new int[instancesSize];
+ for (int i = 0; i < instancesSize; i++) {
+ appWidgetIds[i] = p.instances.get(i).appWidgetId;
+ }
+ return appWidgetIds;
+ }
+
+ public int[] getAppWidgetIds(ComponentName provider) {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ Provider p = lookupProviderLocked(provider);
+ if (p != null && Binder.getCallingUid() == p.uid) {
+ return getAppWidgetIds(p);
+ } else {
+ return new int[0];
+ }
+ }
+ }
+
+ private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
+ Provider p = null;
+
+ ActivityInfo activityInfo = ri.activityInfo;
+ XmlResourceParser parser = null;
+ try {
+ parser = activityInfo.loadXmlMetaData(mPackageManager,
+ AppWidgetManager.META_DATA_APPWIDGET_PROVIDER);
+ if (parser == null) {
+ Slog.w(TAG, "No " + AppWidgetManager.META_DATA_APPWIDGET_PROVIDER
+ + " meta-data for " + "AppWidget provider '" + component + '\'');
+ return null;
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // drain whitespace, comments, etc.
+ }
+
+ String nodeName = parser.getName();
+ if (!"appwidget-provider".equals(nodeName)) {
+ Slog.w(TAG, "Meta-data does not start with appwidget-provider tag for"
+ + " AppWidget provider '" + component + '\'');
+ return null;
+ }
+
+ p = new Provider();
+ AppWidgetProviderInfo info = p.info = new AppWidgetProviderInfo();
+ info.provider = component;
+ p.uid = activityInfo.applicationInfo.uid;
+
+ Resources res = mPackageManager
+ .getResourcesForApplication(activityInfo.applicationInfo);
+
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AppWidgetProviderInfo);
+
+ // These dimensions has to be resolved in the application's context.
+ // We simply send back the raw complex data, which will be
+ // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
+ TypedValue value = sa
+ .peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
+ info.minWidth = value != null ? value.data : 0;
+ value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
+ info.minHeight = value != null ? value.data : 0;
+ value = sa.peekValue(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeWidth);
+ info.minResizeWidth = value != null ? value.data : info.minWidth;
+ value = sa.peekValue(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_minResizeHeight);
+ info.minResizeHeight = value != null ? value.data : info.minHeight;
+ info.updatePeriodMillis = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
+ info.initialLayout = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_initialLayout, 0);
+ String className = sa
+ .getString(com.android.internal.R.styleable.AppWidgetProviderInfo_configure);
+ if (className != null) {
+ info.configure = new ComponentName(component.getPackageName(), className);
+ }
+ info.label = activityInfo.loadLabel(mPackageManager).toString();
+ info.icon = ri.getIconResource();
+ info.previewImage = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+ info.autoAdvanceViewId = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
+ info.resizeMode = sa.getInt(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode,
+ AppWidgetProviderInfo.RESIZE_NONE);
+
+ sa.recycle();
+ } catch (Exception e) {
+ // Ok to catch Exception here, because anything going wrong because
+ // of what a client process passes to us should not be fatal for the
+ // system process.
+ Slog.w(TAG, "XML parsing failed for AppWidget provider '" + component + '\'', e);
+ return null;
+ } finally {
+ if (parser != null)
+ parser.close();
+ }
+ return p;
+ }
+
+ int getUidForPackage(String packageName) throws PackageManager.NameNotFoundException {
+ PackageInfo pkgInfo = mPackageManager.getPackageInfo(packageName, 0);
+ if (pkgInfo == null || pkgInfo.applicationInfo == null) {
+ throw new PackageManager.NameNotFoundException();
+ }
+ return pkgInfo.applicationInfo.uid;
+ }
+
+ int enforceCallingUid(String packageName) throws IllegalArgumentException {
+ int callingUid = Binder.getCallingUid();
+ int packageUid;
+ try {
+ packageUid = getUidForPackage(packageName);
+ } catch (PackageManager.NameNotFoundException ex) {
+ throw new IllegalArgumentException("packageName and uid don't match packageName="
+ + packageName);
+ }
+ if (!UserId.isSameApp(callingUid, packageUid)) {
+ throw new IllegalArgumentException("packageName and uid don't match packageName="
+ + packageName);
+ }
+ return callingUid;
+ }
+
+ void sendInitialBroadcasts() {
+ synchronized (mAppWidgetIds) {
+ ensureStateLoadedLocked();
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.instances.size() > 0) {
+ sendEnableIntentLocked(p);
+ int[] appWidgetIds = getAppWidgetIds(p);
+ sendUpdateIntentLocked(p, appWidgetIds);
+ registerForBroadcastsLocked(p, appWidgetIds);
+ }
+ }
+ }
+ }
+
+ // only call from initialization -- it assumes that the data structures are all empty
+ void loadStateLocked() {
+ AtomicFile file = savedStateFile();
+ try {
+ FileInputStream stream = file.openRead();
+ readStateFromFileLocked(stream);
+
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to close state FileInputStream " + e);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Failed to read state: " + e);
+ }
+ }
+
+ void saveStateLocked() {
+ AtomicFile file = savedStateFile();
+ FileOutputStream stream;
+ try {
+ stream = file.startWrite();
+ if (writeStateToFileLocked(stream)) {
+ file.finishWrite(stream);
+ } else {
+ file.failWrite(stream);
+ Slog.w(TAG, "Failed to save state, restoring backup.");
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed open state file for write: " + e);
+ }
+ }
+
+ boolean writeStateToFileLocked(FileOutputStream stream) {
+ int N;
+
+ try {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, "gs");
+
+ int providerIndex = 0;
+ N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider p = mInstalledProviders.get(i);
+ if (p.instances.size() > 0) {
+ out.startTag(null, "p");
+ out.attribute(null, "pkg", p.info.provider.getPackageName());
+ out.attribute(null, "cl", p.info.provider.getClassName());
+ out.endTag(null, "p");
+ p.tag = providerIndex;
+ providerIndex++;
+ }
+ }
+
+ N = mHosts.size();
+ for (int i = 0; i < N; i++) {
+ Host host = mHosts.get(i);
+ out.startTag(null, "h");
+ out.attribute(null, "pkg", host.packageName);
+ out.attribute(null, "id", Integer.toHexString(host.hostId));
+ out.endTag(null, "h");
+ host.tag = i;
+ }
+
+ N = mAppWidgetIds.size();
+ for (int i = 0; i < N; i++) {
+ AppWidgetId id = mAppWidgetIds.get(i);
+ out.startTag(null, "g");
+ out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
+ out.attribute(null, "h", Integer.toHexString(id.host.tag));
+ if (id.provider != null) {
+ out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+ }
+ out.endTag(null, "g");
+ }
+
+ out.endTag(null, "gs");
+
+ out.endDocument();
+ return true;
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to write state: " + e);
+ return false;
+ }
+ }
+
+ void readStateFromFileLocked(FileInputStream stream) {
+ boolean success = false;
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type;
+ int providerIndex = 0;
+ HashMap<Integer, Provider> loadedProviders = new HashMap<Integer, Provider>();
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if ("p".equals(tag)) {
+ // TODO: do we need to check that this package has the same signature
+ // as before?
+ String pkg = parser.getAttributeValue(null, "pkg");
+ String cl = parser.getAttributeValue(null, "cl");
+
+ final PackageManager packageManager = mContext.getPackageManager();
+ try {
+ packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ String[] pkgs = packageManager
+ .currentToCanonicalPackageNames(new String[] { pkg });
+ pkg = pkgs[0];
+ }
+
+ Provider p = lookupProviderLocked(new ComponentName(pkg, cl));
+ if (p == null && mSafeMode) {
+ // if we're in safe mode, make a temporary one
+ p = new Provider();
+ p.info = new AppWidgetProviderInfo();
+ p.info.provider = new ComponentName(pkg, cl);
+ p.zombie = true;
+ mInstalledProviders.add(p);
+ }
+ if (p != null) {
+ // if it wasn't uninstalled or something
+ loadedProviders.put(providerIndex, p);
+ }
+ providerIndex++;
+ } else if ("h".equals(tag)) {
+ Host host = new Host();
+
+ // TODO: do we need to check that this package has the same signature
+ // as before?
+ host.packageName = parser.getAttributeValue(null, "pkg");
+ try {
+ host.uid = getUidForPackage(host.packageName);
+ } catch (PackageManager.NameNotFoundException ex) {
+ host.zombie = true;
+ }
+ if (!host.zombie || mSafeMode) {
+ // In safe mode, we don't discard the hosts we don't recognize
+ // so that they're not pruned from our list. Otherwise, we do.
+ host.hostId = Integer
+ .parseInt(parser.getAttributeValue(null, "id"), 16);
+ mHosts.add(host);
+ }
+ } else if ("g".equals(tag)) {
+ AppWidgetId id = new AppWidgetId();
+ id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);
+ if (id.appWidgetId >= mNextAppWidgetId) {
+ mNextAppWidgetId = id.appWidgetId + 1;
+ }
+
+ String providerString = parser.getAttributeValue(null, "p");
+ if (providerString != null) {
+ // there's no provider if it hasn't been bound yet.
+ // maybe we don't have to save this, but it brings the system
+ // to the state it was in.
+ int pIndex = Integer.parseInt(providerString, 16);
+ id.provider = loadedProviders.get(pIndex);
+ if (false) {
+ Slog.d(TAG, "bound appWidgetId=" + id.appWidgetId + " to provider "
+ + pIndex + " which is " + id.provider);
+ }
+ if (id.provider == null) {
+ // This provider is gone. We just let the host figure out
+ // that this happened when it fails to load it.
+ continue;
+ }
+ }
+
+ int hIndex = Integer.parseInt(parser.getAttributeValue(null, "h"), 16);
+ id.host = mHosts.get(hIndex);
+ if (id.host == null) {
+ // This host is gone.
+ continue;
+ }
+
+ if (id.provider != null) {
+ id.provider.instances.add(id);
+ }
+ id.host.instances.add(id);
+ mAppWidgetIds.add(id);
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "failed parsing " + e);
+ }
+
+ if (success) {
+ // delete any hosts that didn't manage to get connected (should happen)
+ // if it matters, they'll be reconnected.
+ for (int i = mHosts.size() - 1; i >= 0; i--) {
+ pruneHostLocked(mHosts.get(i));
+ }
+ } else {
+ // failed reading, clean up
+ Slog.w(TAG, "Failed to read state, clearing widgets and hosts.");
+
+ mAppWidgetIds.clear();
+ mHosts.clear();
+ final int N = mInstalledProviders.size();
+ for (int i = 0; i < N; i++) {
+ mInstalledProviders.get(i).instances.clear();
+ }
+ }
+ }
+
+ AtomicFile savedStateFile() {
+ int userId = Binder.getOrigCallingUser();
+ File dir = new File("/data/system/users/" + userId);
+ File settingsFile = new File(dir, SETTINGS_FILENAME);
+ if (!dir.exists()) {
+ dir.mkdirs();
+ if (userId == 0) {
+ // Migrate old data
+ File oldFile = new File("/data/system/" + SETTINGS_FILENAME);
+ // Method doesn't throw an exception on failure. Ignore any errors
+ // in moving the file (like non-existence)
+ oldFile.renameTo(settingsFile);
+ }
+ }
+ return new AtomicFile(settingsFile);
+ }
+
+ void addProvidersForPackageLocked(String pkgName) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.setPackage(pkgName);
+ List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ final int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ ActivityInfo ai = ri.activityInfo;
+ if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ continue;
+ }
+ if (pkgName.equals(ai.packageName)) {
+ addProviderLocked(ri);
+ }
+ }
+ }
+
+ void updateProvidersForPackageLocked(String pkgName) {
+ HashSet<String> keep = new HashSet<String>();
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ intent.setPackage(pkgName);
+ List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
+ PackageManager.GET_META_DATA);
+
+ // add the missing ones and collect which ones to keep
+ int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
+ for (int i = 0; i < N; i++) {
+ ResolveInfo ri = broadcastReceivers.get(i);
+ ActivityInfo ai = ri.activityInfo;
+ if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ continue;
+ }
+ if (pkgName.equals(ai.packageName)) {
+ ComponentName component = new ComponentName(ai.packageName, ai.name);
+ Provider p = lookupProviderLocked(component);
+ if (p == null) {
+ if (addProviderLocked(ri)) {
+ keep.add(ai.name);
+ }
+ } else {
+ Provider parsed = parseProviderInfoXml(component, ri);
+ if (parsed != null) {
+ keep.add(ai.name);
+ // Use the new AppWidgetProviderInfo.
+ p.info = parsed.info;
+ // If it's enabled
+ final int M = p.instances.size();
+ if (M > 0) {
+ int[] appWidgetIds = getAppWidgetIds(p);
+ // Reschedule for the new updatePeriodMillis (don't worry about handling
+ // it specially if updatePeriodMillis didn't change because we just sent
+ // an update, and the next one will be updatePeriodMillis from now).
+ cancelBroadcasts(p);
+ registerForBroadcastsLocked(p, appWidgetIds);
+ // If it's currently showing, call back with the new
+ // AppWidgetProviderInfo.
+ for (int j = 0; j < M; j++) {
+ AppWidgetId id = p.instances.get(j);
+ id.views = null;
+ if (id.host != null && id.host.callbacks != null) {
+ try {
+ id.host.callbacks.providerChanged(id.appWidgetId, p.info);
+ } catch (RemoteException ex) {
+ // It failed; remove the callback. No need to prune because
+ // we know that this host is still referenced by this
+ // instance.
+ id.host.callbacks = null;
+ }
+ }
+ }
+ // Now that we've told the host, push out an update.
+ sendUpdateIntentLocked(p, appWidgetIds);
+ }
+ }
+ }
+ }
+ }
+
+ // prune the ones we don't want to keep
+ N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ if (pkgName.equals(p.info.provider.getPackageName())
+ && !keep.contains(p.info.provider.getClassName())) {
+ removeProviderLocked(i, p);
+ }
+ }
+ }
+
+ void removeProvidersForPackageLocked(String pkgName) {
+ int N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ if (pkgName.equals(p.info.provider.getPackageName())) {
+ removeProviderLocked(i, p);
+ }
+ }
+
+ // Delete the hosts for this package too
+ //
+ // By now, we have removed any AppWidgets that were in any hosts here,
+ // so we don't need to worry about sending DISABLE broadcasts to them.
+ N = mHosts.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Host host = mHosts.get(i);
+ if (pkgName.equals(host.packageName)) {
+ deleteHostLocked(host);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 31515e1..a7b08f5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1675,7 +1675,8 @@ class BackupManagerService extends IBackupManager.Stub {
synchronized(mClearDataLock) {
mClearingData = true;
try {
- mActivityManager.clearApplicationUserData(packageName, observer);
+ mActivityManager.clearApplicationUserData(packageName, observer,
+ Binder.getOrigCallingUser());
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 5039294..34a8a02 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -45,6 +45,7 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserId;
import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -1034,7 +1035,7 @@ public class NotificationManagerService extends INotificationManager.Stub
try {
ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
pkg, 0);
- if (ai.uid != uid) {
+ if (!UserId.isSameApp(ai.uid, uid)) {
throw new SecurityException("Calling uid " + uid + " gave package"
+ pkg + " which is owned by uid " + ai.uid);
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3a6a3de..6fd5c07 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -75,6 +75,7 @@ import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -104,6 +105,7 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserId;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Pair;
@@ -111,6 +113,7 @@ import android.util.Slog;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -135,6 +138,7 @@ import java.io.StringWriter;
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -149,7 +153,9 @@ import java.util.concurrent.atomic.AtomicLong;
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
+ private static final String USER_DATA_DIR = "/data/user/";
static final String TAG = "ActivityManager";
+ static final String TAG_MU = "ActivityManagerServiceMU";
static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG;
static final boolean DEBUG_SWITCH = localLOGV || false;
@@ -172,6 +178,7 @@ public final class ActivityManagerService extends ActivityManagerNative
static final boolean DEBUG_CONFIGURATION = localLOGV || false;
static final boolean DEBUG_POWER = localLOGV || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
+ static final boolean DEBUG_MU = localLOGV || false;
static final boolean VALIDATE_TOKENS = false;
static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -781,7 +788,16 @@ public final class ActivityManagerService extends ActivityManagerNative
r.curComponent = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
+ if (r.callingUid != Process.SYSTEM_UID) {
+ info.activityInfo = getActivityInfoForUser(info.activityInfo, UserId
+ .getUserId(r.callingUid));
+ }
r.curReceiver = info.activityInfo;
+ if (DEBUG_MU && r.callingUid > UserId.PER_USER_RANGE) {
+ Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ + info.activityInfo.applicationInfo.uid);
+ }
// Broadcast is being executed, its package can't be stopped.
try {
@@ -1286,17 +1302,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final HashMap<String, ArrayList<Intent>> mStickyBroadcasts =
new HashMap<String, ArrayList<Intent>>();
- /**
- * All currently running services.
- */
- final HashMap<ComponentName, ServiceRecord> mServices =
- new HashMap<ComponentName, ServiceRecord>();
-
- /**
- * All currently running services indexed by the Intent used to start them.
- */
- final HashMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent =
- new HashMap<Intent.FilterComparison, ServiceRecord>();
+ final ServiceMap mServiceMap = new ServiceMap();
/**
* All currently bound service connections. Keys are the IBinder of
@@ -1362,6 +1368,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final HashMap<ComponentName, ContentProviderRecord> mProvidersByClass
= new HashMap<ComponentName, ContentProviderRecord>();
+ final ProviderMap mProviderMap = new ProviderMap();
+
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
@@ -1392,6 +1400,7 @@ public final class ActivityManagerService extends ActivityManagerNative
uid = _uid;
}
}
+
private static ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
/**
@@ -1688,7 +1697,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
mContext, proc, (ActivityRecord)data.get("activity"));
@@ -2587,6 +2596,7 @@ public final class ActivityManagerService extends ActivityManagerNative
processName);
return procs != null ? procs.valueAt(0) : null;
}
+ // uid = applyUserId(uid);
ProcessRecord proc = mProcessNames.get(processName, uid);
return proc;
}
@@ -2716,6 +2726,7 @@ public final class ActivityManagerService extends ActivityManagerNative
try {
int uid = app.info.uid;
+
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
@@ -2827,7 +2838,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- boolean startHomeActivityLocked() {
+ boolean startHomeActivityLocked(int userId) {
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
@@ -2850,6 +2861,8 @@ public final class ActivityManagerService extends ActivityManagerNative
aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
+ aInfo = new ActivityInfo(aInfo);
+ aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid);
if (app == null || app.instrumentationClass == null) {
@@ -2858,11 +2871,10 @@ public final class ActivityManagerService extends ActivityManagerNative
null, null, 0, 0, 0, false, false, null);
}
}
-
-
+
return true;
}
-
+
/**
* Starts the "new version setup screen" if appropriate.
*/
@@ -3026,10 +3038,23 @@ public final class ActivityManagerService extends ActivityManagerNative
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+ int userId = 0;
+ if (intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {
+ // Requesting home, set the identity to the current user
+ // HACK!
+ userId = mCurrentUserId;
+ } else {
+ // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
+ // the current user's userId
+ if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
+ userId = 0;
+ } else {
+ userId = Binder.getOrigCallingUser();
+ }
+ }
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
- grantedUriPermissions, grantedMode, resultTo, resultWho,
- requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
- null, null);
+ grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded,
+ debug, profileFile, profileFd, autoStopProfiler, null, null, userId);
}
public final WaitResult startActivityAndWait(IApplicationThread caller,
@@ -3038,10 +3063,11 @@ public final class ActivityManagerService extends ActivityManagerNative
String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
WaitResult res = new WaitResult();
+ int userId = Binder.getOrigCallingUser();
mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
- res, null);
+ res, null, userId);
return res;
}
@@ -3050,9 +3076,11 @@ public final class ActivityManagerService extends ActivityManagerNative
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, Configuration config) {
- return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
+ int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
- requestCode, onlyIfNeeded, debug, null, null, false, null, config);
+ requestCode, onlyIfNeeded,
+ debug, null, null, false, null, config, Binder.getOrigCallingUser());
+ return ret;
}
public int startActivityIntentSender(IApplicationThread caller,
@@ -3080,9 +3108,9 @@ public final class ActivityManagerService extends ActivityManagerNative
mAppSwitchesAllowedTime = 0;
}
}
-
- return pir.sendInner(0, fillInIntent, resolvedType, null,
+ int ret = pir.sendInner(0, fillInIntent, resolvedType, null,
null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
+ return ret;
}
public boolean startNextMatchingActivity(IBinder callingActivity,
@@ -3185,20 +3213,24 @@ public final class ActivityManagerService extends ActivityManagerNative
// This is so super not safe, that only the system (or okay root)
// can do it.
+ int userId = Binder.getOrigCallingUser();
final int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.myUid()) {
throw new SecurityException(
"startActivityInPackage only available to the system");
}
- return mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
+ int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
null, 0, resultTo, resultWho, requestCode, onlyIfNeeded, false,
- null, null, false, null, null);
+ null, null, false, null, null, userId);
+ return ret;
}
public final int startActivities(IApplicationThread caller,
Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
- return mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo);
+ int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
+ Binder.getOrigCallingUser());
+ return ret;
}
public final int startActivitiesInPackage(int uid,
@@ -3211,8 +3243,9 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(
"startActivityInPackage only available to the system");
}
-
- return mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo);
+ int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
+ UserId.getUserId(uid));
+ return ret;
}
final void addRecentTaskLocked(TaskRecord task) {
@@ -3224,8 +3257,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Remove any existing entries that are the same kind of task.
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
- if ((task.affinity != null && task.affinity.equals(tr.affinity))
- || (task.intent != null && task.intent.filterEquals(tr.intent))) {
+ if (task.userId == tr.userId
+ && ((task.affinity != null && task.affinity.equals(tr.affinity))
+ || (task.intent != null && task.intent.filterEquals(tr.intent)))) {
mRecentTasks.remove(i);
i--;
N--;
@@ -3584,7 +3618,6 @@ public final class ActivityManagerService extends ActivityManagerNative
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
IBinder threadBinder = thread.asBinder();
-
// Find the application record.
for (int i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord rec = mLruProcesses.get(i);
@@ -3961,7 +3994,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
public boolean clearApplicationUserData(final String packageName,
- final IPackageDataObserver observer) {
+ final IPackageDataObserver observer, final int userId) {
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
@@ -3996,7 +4029,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Uri.fromParts("package", packageName, null));
intent.putExtra(Intent.EXTRA_UID, pkgUid);
broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
- null, null, 0, null, null, null, false, false);
+ null, null, 0, null, null, null, false, false, userId);
} catch (RemoteException e) {
}
} finally {
@@ -4091,7 +4124,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
-
+ final int userId = Binder.getOrigCallingUser();
long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
@@ -4099,6 +4132,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
try {
pkgUid = pm.getPackageUid(packageName);
+ // Convert the uid to the one for the calling user
+ pkgUid = UserId.getUid(userId, pkgUid);
} catch (RemoteException e) {
}
if (pkgUid == -1) {
@@ -4181,7 +4216,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
broadcastIntentLocked(null, null, intent, null,
- null, 0, null, null, null, false, false, -1, uid);
+ null, 0, null, null, null, false, false, -1, uid, 0 /* TODO: Verify */);
}
Binder.restoreCallingIdentity(origId);
}
@@ -4241,7 +4276,8 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.putExtra(Intent.EXTRA_UID, uid);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false,
+ MY_PID, Process.SYSTEM_UID, UserId.getUserId(uid));
}
private final boolean killPackageProcessesLocked(String packageName, int uid,
@@ -4345,7 +4381,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
- for (ServiceRecord service : mServices.values()) {
+ int userId = UserId.getUserId(uid);
+ for (ServiceRecord service : mServiceMap.getAllServices(userId)) {
if (service.packageName.equals(name)
&& (service.app == null || evenPersistent || !service.app.persistent)) {
if (!doit) {
@@ -4809,11 +4846,12 @@ public final class ActivityManagerService extends ActivityManagerNative
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
+ /* TODO: Send this to all users that are to be logged in on startup */
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_BOOT_COMPLETED, null),
null, null, 0, null, null,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID);
+ false, false, MY_PID, Process.SYSTEM_UID, Binder.getOrigCallingUser());
}
}
}
@@ -4954,7 +4992,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
.getPackageUid(packageName);
- if (uid != Binder.getCallingUid()) {
+ if (UserId.getAppId(callingUid) != uid) {
String msg = "Permission Denial: getIntentSender() from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid()
@@ -4965,7 +5003,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- return getIntentSenderLocked(type, packageName, callingUid,
+ if (DEBUG_MU)
+ Slog.i(TAG_MU, "Getting intent sender for origCallingUid="
+ + Binder.getOrigCallingUid());
+ return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
token, resultWho, requestCode, intents, resolvedTypes, flags);
} catch (RemoteException e) {
@@ -4977,6 +5018,8 @@ public final class ActivityManagerService extends ActivityManagerNative
IIntentSender getIntentSenderLocked(int type,
String packageName, int callingUid, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags) {
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
ActivityRecord activity = null;
if (type == INTENT_SENDER_ACTIVITY_RESULT) {
activity = mMainStack.isInStackLocked(token);
@@ -5220,7 +5263,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// If there is a uid that owns whatever is being accessed, it has
// blanket access to it regardless of the permissions it requires.
- if (owningUid >= 0 && uid == owningUid) {
+ if (owningUid >= 0 && UserId.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
@@ -5254,7 +5297,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
- return checkComponentPermission(permission, pid, uid, -1, true);
+ return checkComponentPermission(permission, pid, UserId.getAppId(uid), -1, true);
}
/**
@@ -5264,7 +5307,7 @@ public final class ActivityManagerService extends ActivityManagerNative
int checkCallingPermission(String permission) {
return checkPermission(permission,
Binder.getCallingPid(),
- Binder.getCallingUid());
+ UserId.getAppId(Binder.getCallingUid()));
}
/**
@@ -5378,6 +5421,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pid = tlsIdentity.pid;
}
+ uid = UserId.getAppId(uid);
// Our own process gets to do everything.
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
@@ -5420,7 +5464,8 @@ public final class ActivityManagerService extends ActivityManagerNative
String name = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+ UserId.getUserId(callingUid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -5676,7 +5721,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+ UserId.getUserId(callingUid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -5770,7 +5816,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final String authority = uri.getAuthority();
ProviderInfo pi = null;
- ContentProviderRecord cpr = mProvidersByName.get(authority);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(authority,
+ UserId.getUserId(r.info.uid));
if (cpr != null) {
pi = cpr.info;
} else {
@@ -6009,6 +6056,12 @@ public final class ActivityManagerService extends ActivityManagerNative
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum,
int flags) {
+ final int callingUid = Binder.getCallingUid();
+ // If it's the system uid asking, then use the current user id.
+ // TODO: Make sure that there aren't any other legitimate calls from the system uid that
+ // require the entire list.
+ final int callingUserId = callingUid == Process.SYSTEM_UID
+ ? mCurrentUserId : UserId.getUserId(callingUid);
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.GET_TASKS,
"getRecentTasks()");
@@ -6021,11 +6074,14 @@ public final class ActivityManagerService extends ActivityManagerNative
maxNum < N ? maxNum : N);
for (int i=0; i<N && maxNum > 0; i++) {
TaskRecord tr = mRecentTasks.get(i);
+ // Only add calling user's recent tasks
+ if (tr.userId != callingUserId) continue;
// Return the entry if desired by the caller. We always return
// the first entry, because callers always expect this to be the
- // forground app. We may filter others if the caller has
+ // foreground app. We may filter others if the caller has
// not supplied RECENT_WITH_EXCLUDED and there is some reason
// we should exclude the entry.
+
if (i == 0
|| ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
|| (tr.intent == null)
@@ -6114,7 +6170,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// Find any running services associated with this app.
ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>();
- for (ServiceRecord sr : mServices.values()) {
+ for (ServiceRecord sr : mServiceMap.getAllServices(root.userId)) {
if (sr.packageName.equals(component.getPackageName())) {
services.add(sr);
}
@@ -6485,17 +6541,23 @@ public final class ActivityManagerService extends ActivityManagerNative
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
}
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, app.info.uid = " + app.info.uid);
+ int userId = UserId.getUserId(app.info.uid);
if (providers != null) {
final int N = providers.size();
for (int i=0; i<N; i++) {
ProviderInfo cpi =
(ProviderInfo)providers.get(i);
+
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- ContentProviderRecord cpr = mProvidersByClass.get(comp);
+ ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
if (cpr == null) {
cpr = new ContentProviderRecord(cpi, app.info, comp);
- mProvidersByClass.put(comp, cpr);
+ mProviderMap.putProviderByClass(comp, cpr);
}
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "generateApplicationProvidersLocked, cpi.uid = " + cpr.uid);
app.pubProviders.put(cpi.name, cpr);
app.addPackage(cpi.applicationInfo.packageName);
ensurePackageDexOpt(cpi.applicationInfo.packageName);
@@ -6605,8 +6667,8 @@ public final class ActivityManagerService extends ActivityManagerNative
return false;
}
- private final ContentProviderHolder getContentProviderImpl(
- IApplicationThread caller, String name) {
+ private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
+ String name) {
ContentProviderRecord cpr;
ProviderInfo cpi = null;
@@ -6623,7 +6685,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
// First check if this content provider has been published...
- cpr = mProvidersByName.get(name);
+ int userId = UserId.getUserId(r != null ? r.info.uid : Binder.getCallingUid());
+ cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
if (providerRunning) {
cpi = cpr.info;
@@ -6708,6 +6771,9 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
+ cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo,
+ Binder.getOrigCallingUser());
+
String msg;
if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
throw new SecurityException(msg);
@@ -6723,7 +6789,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
- cpr = mProvidersByClass.get(comp);
+ cpr = mProviderMap.getProviderByClass(comp, Binder.getOrigCallingUser());
final boolean firstClass = cpr == null;
if (firstClass) {
try {
@@ -6737,6 +6803,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ cpi.name);
return null;
}
+ ai = getAppInfoForUser(ai, Binder.getOrigCallingUser());
cpr = new ContentProviderRecord(cpi, ai, comp);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
@@ -6805,9 +6872,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
- mProvidersByClass.put(comp, cpr);
+ mProviderMap.putProviderByClass(comp, cpr);
}
- mProvidersByName.put(name, cpr);
+ mProviderMap.putProviderByName(name, cpr);
incProviderCount(r, cpr);
}
}
@@ -6826,6 +6893,10 @@ public final class ActivityManagerService extends ActivityManagerNative
return null;
}
try {
+ if (DEBUG_MU) {
+ Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
+ + cpr.launchingApp);
+ }
cpr.wait();
} catch (InterruptedException ex) {
}
@@ -6843,7 +6914,8 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
- return getContentProviderImpl(caller, name);
+ ContentProviderHolder contentProvider = getContentProviderImpl(caller, name);
+ return contentProvider;
}
private ContentProviderHolder getContentProviderExternal(String name) {
@@ -6856,7 +6928,8 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
public void removeContentProvider(IApplicationThread caller, String name) {
synchronized (this) {
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
if(cpr == null) {
// remove from mProvidersByClass
if (DEBUG_PROVIDER) Slog.v(TAG, name +
@@ -6871,8 +6944,11 @@ public final class ActivityManagerService extends ActivityManagerNative
}
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProvidersByClass.get(comp);
- if (localCpr.proc == r) {
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
+ if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
+ + r.info.processName + " from process "
+ + localCpr.appInfo.processName);
+ if (localCpr.launchingApp == r) {
//should not happen. taken care of as a local provider
Slog.w(TAG, "removeContentProvider called on local provider: "
+ cpr.info.name + " in process " + r.processName);
@@ -6887,7 +6963,8 @@ public final class ActivityManagerService extends ActivityManagerNative
private void removeContentProviderExternal(String name) {
synchronized (this) {
- ContentProviderRecord cpr = mProvidersByName.get(name);
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
+ Binder.getOrigCallingUser());
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -6896,7 +6973,8 @@ public final class ActivityManagerService extends ActivityManagerNative
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProvidersByClass.get(comp);
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
+ Binder.getOrigCallingUser());
localCpr.externals--;
if (localCpr.externals < 0) {
Slog.e(TAG, "Externals < 0 for content provider " + localCpr);
@@ -6913,6 +6991,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
final ProcessRecord r = getRecordForAppLocked(caller);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "ProcessRecord uid = " + r.info.uid);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
@@ -6929,12 +7009,14 @@ public final class ActivityManagerService extends ActivityManagerNative
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
- mProvidersByClass.put(comp, dst);
+ mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProvidersByName.put(names[j], dst);
+ mProviderMap.putProviderByName(names[j], dst);
}
int NL = mLaunchingProviders.size();
@@ -7679,8 +7761,10 @@ public final class ActivityManagerService extends ActivityManagerNative
};
}
Slog.i(TAG, "Sending system update to: " + intent.getComponent());
+ /* TODO: Send this to all users */
broadcastIntentLocked(null, null, intent, null, finisher,
- 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
+ 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+ Process.SYSTEM_UID);
if (finisher != null) {
mWaitingUpdate = true;
}
@@ -9306,8 +9390,14 @@ public final class ActivityManagerService extends ActivityManagerNative
if ("all".equals(name)) {
synchronized (this) {
- for (ServiceRecord r1 : mServices.values()) {
- services.add(r1);
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ services.add(r1);
+ }
+ }
+ } catch (RemoteException re) {
}
}
} else {
@@ -9325,18 +9415,24 @@ public final class ActivityManagerService extends ActivityManagerNative
}
synchronized (this) {
- for (ServiceRecord r1 : mServices.values()) {
- if (componentName != null) {
- if (r1.name.equals(componentName)) {
- services.add(r1);
- }
- } else if (name != null) {
- if (r1.name.flattenToString().contains(name)) {
- services.add(r1);
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ for (ServiceRecord r1 : mServiceMap.getAllServices(user.id)) {
+ if (componentName != null) {
+ if (r1.name.equals(componentName)) {
+ services.add(r1);
+ }
+ } else if (name != null) {
+ if (r1.name.flattenToString().contains(name)) {
+ services.add(r1);
+ }
+ } else if (System.identityHashCode(r1) == objectId) {
+ services.add(r1);
+ }
}
- } else if (System.identityHashCode(r1) == objectId) {
- services.add(r1);
}
+ } catch (RemoteException re) {
}
}
}
@@ -9778,75 +9874,87 @@ public final class ActivityManagerService extends ActivityManagerNative
matcher.build(args, opti);
pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)");
- if (mServices.size() > 0) {
- boolean printed = false;
- long nowReal = SystemClock.elapsedRealtime();
- Iterator<ServiceRecord> it = mServices.values().iterator();
- needSep = false;
- while (it.hasNext()) {
- ServiceRecord r = it.next();
- if (!matcher.match(r, r.name)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
- continue;
- }
- if (!printed) {
- pw.println(" Active services:");
- printed = true;
- }
- if (needSep) {
- pw.println();
- }
- pw.print(" * "); pw.println(r);
- if (dumpAll) {
- r.dump(pw, " ");
- needSep = true;
- } else {
- pw.print(" app="); pw.println(r.app);
- pw.print(" created=");
- TimeUtils.formatDuration(r.createTime, nowReal, pw);
- pw.print(" started="); pw.print(r.startRequested);
- pw.print(" connections="); pw.println(r.connections.size());
- if (r.connections.size() > 0) {
- pw.println(" Connections:");
- for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
- for (int i=0; i<clist.size(); i++) {
- ConnectionRecord conn = clist.get(i);
- pw.print(" ");
- pw.print(conn.binding.intent.intent.getIntent().toShortString(
- false, false, false));
- pw.print(" -> ");
- ProcessRecord proc = conn.binding.client;
- pw.println(proc != null ? proc.toShortString() : "null");
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ if (mServiceMap.getAllServices(user.id).size() > 0) {
+ boolean printed = false;
+ long nowReal = SystemClock.elapsedRealtime();
+ Iterator<ServiceRecord> it = mServiceMap.getAllServices(
+ user.id).iterator();
+ needSep = false;
+ while (it.hasNext()) {
+ ServiceRecord r = it.next();
+ if (!matcher.match(r, r.name)) {
+ continue;
+ }
+ if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) {
+ continue;
+ }
+ if (!printed) {
+ pw.println(" Active services:");
+ printed = true;
+ }
+ if (needSep) {
+ pw.println();
+ }
+ pw.print(" * ");
+ pw.println(r);
+ if (dumpAll) {
+ r.dump(pw, " ");
+ needSep = true;
+ } else {
+ pw.print(" app=");
+ pw.println(r.app);
+ pw.print(" created=");
+ TimeUtils.formatDuration(r.createTime, nowReal, pw);
+ pw.print(" started=");
+ pw.print(r.startRequested);
+ pw.print(" connections=");
+ pw.println(r.connections.size());
+ if (r.connections.size() > 0) {
+ pw.println(" Connections:");
+ for (ArrayList<ConnectionRecord> clist : r.connections.values()) {
+ for (int i = 0; i < clist.size(); i++) {
+ ConnectionRecord conn = clist.get(i);
+ pw.print(" ");
+ pw.print(conn.binding.intent.intent.getIntent()
+ .toShortString(false, false, false));
+ pw.print(" -> ");
+ ProcessRecord proc = conn.binding.client;
+ pw.println(proc != null ? proc.toShortString() : "null");
+ }
+ }
}
}
- }
- }
- if (dumpClient && r.app != null && r.app.thread != null) {
- pw.println(" Client:");
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.thread.dumpService(
- tp.getWriteFd().getFileDescriptor(), r, args);
- tp.setBufferPrefix(" ");
- // Short timeout, since blocking here can
- // deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
+ if (dumpClient && r.app != null && r.app.thread != null) {
+ pw.println(" Client:");
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.thread.dumpService(tp.getWriteFd().getFileDescriptor(),
+ r, args);
+ tp.setBufferPrefix(" ");
+ // Short timeout, since blocking here can
+ // deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(" Failure while dumping the service: " + e);
+ } catch (RemoteException e) {
+ pw.println(" Got a RemoteException while dumping the service");
+ }
+ needSep = true;
}
- } catch (IOException e) {
- pw.println(" Failure while dumping the service: " + e);
- } catch (RemoteException e) {
- pw.println(" Got a RemoteException while dumping the service");
}
- needSep = true;
+ needSep = printed;
}
}
- needSep = printed;
+ } catch (RemoteException re) {
+
}
if (mPendingServices.size() > 0) {
@@ -9956,76 +10064,8 @@ public final class ActivityManagerService extends ActivityManagerNative
matcher.build(args, opti);
pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)");
- if (mProvidersByClass.size() > 0) {
- boolean printed = false;
- Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it
- = mProvidersByClass.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
- ContentProviderRecord r = e.getValue();
- ComponentName comp = e.getKey();
- String cls = comp.getClassName();
- int end = cls.lastIndexOf('.');
- if (end > 0 && end < (cls.length()-2)) {
- cls = cls.substring(end+1);
- }
- if (!matcher.match(r, comp)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(comp.getPackageName())) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Published content providers (by class):");
- printed = true;
- }
- pw.print(" * "); pw.print(cls); pw.print(" (");
- pw.print(comp.flattenToShortString()); pw.println(")");
- if (dumpAll) {
- r.dump(pw, " ");
- } else {
- if (r.proc != null) {
- pw.print(" "); pw.println(r.proc);
- } else {
- pw.println();
- }
- if (r.clients.size() > 0) {
- pw.println(" Clients:");
- for (ProcessRecord cproc : r.clients) {
- pw.print(" - "); pw.println(cproc);
- }
- }
- }
- }
- }
-
- if (dumpAll) {
- if (mProvidersByName.size() > 0) {
- boolean printed = false;
- Iterator<Map.Entry<String, ContentProviderRecord>> it
- = mProvidersByName.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, ContentProviderRecord> e = it.next();
- ContentProviderRecord r = e.getValue();
- if (!matcher.match(r, r.name)) {
- continue;
- }
- if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) {
- continue;
- }
- if (!printed) {
- if (needSep) pw.println(" ");
- needSep = true;
- pw.println(" Authority to provider mappings:");
- printed = true;
- }
- pw.print(" "); pw.print(e.getKey()); pw.println(":");
- pw.print(" "); pw.println(r);
- }
- }
- }
+
+ mProviderMap.dumpProvidersLocked(pw, dumpAll);
if (mLaunchingProviders.size() > 0) {
boolean printed = false;
@@ -10911,10 +10951,10 @@ public final class ActivityManagerService extends ActivityManagerNative
cpr.notifyAll();
}
- mProvidersByClass.remove(cpr.name);
+ mProviderMap.removeProviderByClass(cpr.name, UserId.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProvidersByName.remove(names[j]);
+ mProviderMap.removeProviderByName(names[j], UserId.getUserId(cpr.uid));
}
Iterator<ProcessRecord> cit = cpr.clients.iterator();
@@ -11190,8 +11230,10 @@ public final class ActivityManagerService extends ActivityManagerNative
ArrayList<ActivityManager.RunningServiceInfo> res
= new ArrayList<ActivityManager.RunningServiceInfo>();
- if (mServices.size() > 0) {
- Iterator<ServiceRecord> it = mServices.values().iterator();
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ if (mServiceMap.getAllServices(userId).size() > 0) {
+ Iterator<ServiceRecord> it
+ = mServiceMap.getAllServices(userId).iterator();
while (it.hasNext() && res.size() < maxNum) {
res.add(makeRunningServiceInfoLocked(it.next()));
}
@@ -11211,7 +11253,8 @@ public final class ActivityManagerService extends ActivityManagerNative
public PendingIntent getRunningServiceControlPanel(ComponentName name) {
synchronized (this) {
- ServiceRecord r = mServices.get(name);
+ int userId = UserId.getUserId(Binder.getCallingUid());
+ ServiceRecord r = mServiceMap.getServiceByName(name, userId);
if (r != null) {
for (ArrayList<ConnectionRecord> conn : r.connections.values()) {
for (int i=0; i<conn.size(); i++) {
@@ -11227,7 +11270,7 @@ public final class ActivityManagerService extends ActivityManagerNative
private final ServiceRecord findServiceLocked(ComponentName name,
IBinder token) {
- ServiceRecord r = mServices.get(name);
+ ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
return r == token ? r : null;
}
@@ -11245,11 +11288,11 @@ public final class ActivityManagerService extends ActivityManagerNative
String resolvedType) {
ServiceRecord r = null;
if (service.getComponent() != null) {
- r = mServices.get(service.getComponent());
+ r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
}
if (r == null) {
Intent.FilterComparison filter = new Intent.FilterComparison(service);
- r = mServicesByIntent.get(filter);
+ r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
}
if (r == null) {
@@ -11265,7 +11308,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
- r = mServices.get(name);
+ r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
@@ -11312,11 +11355,17 @@ public final class ActivityManagerService extends ActivityManagerNative
private ServiceLookupResult retrieveServiceLocked(Intent service,
String resolvedType, int callingPid, int callingUid) {
ServiceRecord r = null;
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType
+ + " origCallingUid=" + callingUid);
+
if (service.getComponent() != null) {
- r = mServices.get(service.getComponent());
+ r = mServiceMap.getServiceByName(service.getComponent(), Binder.getOrigCallingUser());
+ }
+ if (r == null) {
+ Intent.FilterComparison filter = new Intent.FilterComparison(service);
+ r = mServiceMap.getServiceByIntent(filter, Binder.getOrigCallingUser());
}
- Intent.FilterComparison filter = new Intent.FilterComparison(service);
- r = mServicesByIntent.get(filter);
if (r == null) {
try {
ResolveInfo rInfo =
@@ -11329,12 +11378,16 @@ public final class ActivityManagerService extends ActivityManagerNative
": not found");
return null;
}
-
+ if (Binder.getOrigCallingUser() > 0) {
+ sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo,
+ Binder.getOrigCallingUser());
+ }
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
- r = mServices.get(name);
+ r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
if (r == null) {
- filter = new Intent.FilterComparison(service.cloneFilter());
+ Intent.FilterComparison filter = new Intent.FilterComparison(
+ service.cloneFilter());
ServiceRestarter res = new ServiceRestarter();
BatteryStatsImpl.Uid.Pkg.Serv ss = null;
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
@@ -11345,8 +11398,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
r = new ServiceRecord(this, ss, name, filter, sInfo, res);
res.setService(r);
- mServices.put(name, r);
- mServicesByIntent.put(filter, r);
+ mServiceMap.putServiceByName(name, UserId.getUserId(r.appInfo.uid), r);
+ mServiceMap.putServiceByIntent(filter, UserId.getUserId(r.appInfo.uid), r);
// Make sure this component isn't in the pending list.
int N = mPendingServices.size();
@@ -11493,7 +11546,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (app.thread == null) {
throw new RemoteException();
}
-
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ + ", ProcessRecord.uid = " + app.info.uid);
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
@@ -11690,6 +11745,8 @@ public final class ActivityManagerService extends ActivityManagerNative
final String appName = r.processName;
ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName);
@@ -11794,8 +11851,8 @@ public final class ActivityManagerService extends ActivityManagerNative
System.identityHashCode(r), r.shortName,
(r.app != null) ? r.app.pid : -1);
- mServices.remove(r.name);
- mServicesByIntent.remove(r.intent);
+ mServiceMap.removeServiceByName(r.name, r.userId);
+ mServiceMap.removeServiceByIntent(r.intent, r.userId);
r.totalRestartCount = 0;
unscheduleServiceRestartLocked(r);
@@ -11909,6 +11966,8 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "startService: " + service + " type=" + resolvedType);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
@@ -11923,6 +11982,8 @@ public final class ActivityManagerService extends ActivityManagerNative
ComponentName startServiceInPackage(int uid,
Intent service, String resolvedType) {
synchronized(this) {
+ if (DEBUG_SERVICE)
+ Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
ComponentName res = startServiceLocked(null, service,
resolvedType, -1, uid);
@@ -12126,6 +12187,9 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
+ if (DEBUG_MU)
+ Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
+ + Binder.getOrigCallingUid());
final ProcessRecord callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
@@ -12168,7 +12232,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType,
- Binder.getCallingPid(), Binder.getCallingUid());
+ Binder.getCallingPid(), Binder.getOrigCallingUid());
if (res == null) {
return 0;
}
@@ -12514,7 +12578,9 @@ public final class ActivityManagerService extends ActivityManagerNative
r.callStart = false;
}
}
-
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
+ + Binder.getOrigCallingUid());
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
@@ -12927,7 +12993,8 @@ public final class ActivityManagerService extends ActivityManagerNative
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle map, String requiredPermission,
- boolean ordered, boolean sticky, int callingPid, int callingUid) {
+ boolean ordered, boolean sticky, int callingPid, int callingUid,
+ int userId) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
@@ -13102,10 +13169,11 @@ public final class ActivityManagerService extends ActivityManagerNative
if (ai != null) {
receivers = new ArrayList();
ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = ai;
+ ri.activityInfo = getActivityInfoForUser(ai, userId);
receivers.add(ri);
}
} else {
+ // TODO: Apply userId
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
@@ -13274,7 +13342,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
- String requiredPermission, boolean serialized, boolean sticky) {
+ String requiredPermission, boolean serialized, boolean sticky, int userId) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
@@ -13285,8 +13353,8 @@ public final class ActivityManagerService extends ActivityManagerNative
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo,
- resultCode, resultData, map, requiredPermission, serialized,
- sticky, callingPid, callingUid);
+ resultCode, resultData, map, requiredPermission, serialized, sticky,
+ callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -13295,21 +13363,21 @@ public final class ActivityManagerService extends ActivityManagerNative
int broadcastIntentInPackage(String packageName, int uid,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
- String requiredPermission, boolean serialized, boolean sticky) {
+ String requiredPermission, boolean serialized, boolean sticky, int userId) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
resultTo, resultCode, resultData, map, requiredPermission,
- serialized, sticky, -1, uid);
+ serialized, sticky, -1, uid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
- public final void unbroadcastIntent(IApplicationThread caller,
- Intent intent) {
+ // TODO: Use the userId; maybe mStickyBroadcasts need to be tied to the user.
+ public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -13514,7 +13582,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
-
// =========================================================
// INSTRUMENTATION
// =========================================================
@@ -13786,12 +13853,12 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID);
+ null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_LOCALE_CHANGED),
null, null, 0, null, null,
- null, false, false, MY_PID, Process.SYSTEM_UID);
+ null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
}
}
}
@@ -15134,8 +15201,157 @@ public final class ActivityManagerService extends ActivityManagerNative
// Multi-user methods
- public boolean switchUser(int userid) {
- // TODO
+ private int mCurrentUserId;
+ private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
+
+ public boolean switchUser(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.myUid()) {
+ Slog.e(TAG, "Trying to switch user from unauthorized app");
+ return false;
+ }
+ if (mCurrentUserId == userId)
+ return true;
+
+ synchronized (this) {
+ // Check if user is already logged in, otherwise check if user exists first before
+ // adding to the list of logged in users.
+ if (mLoggedInUsers.indexOfKey(userId) < 0) {
+ if (!userExists(userId)) {
+ return false;
+ }
+ mLoggedInUsers.append(userId, userId);
+ }
+
+ mCurrentUserId = userId;
+ boolean haveActivities = mMainStack.switchUser(userId);
+ if (!haveActivities) {
+ startHomeActivityLocked(userId);
+ }
+ }
return true;
}
+
+ private boolean userExists(int userId) {
+ try {
+ List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
+ for (UserInfo user : users) {
+ if (user.id == userId) {
+ return true;
+ }
+ }
+ } catch (RemoteException re) {
+ // Won't happen, in same process
+ }
+
+ return false;
+ }
+
+
+ private int applyUserId(int uid, int userId) {
+ return UserId.getUid(userId, uid);
+ }
+
+ private ApplicationInfo getAppInfoForUser(ApplicationInfo info, int userId) {
+ ApplicationInfo newInfo = new ApplicationInfo(info);
+ newInfo.uid = applyUserId(info.uid, userId);
+ if (newInfo.uid >= Process.FIRST_APPLICATION_UID) {
+ newInfo.dataDir = USER_DATA_DIR + userId + "/"
+ + info.packageName;
+ }
+ return newInfo;
+ }
+
+ ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
+ if (aInfo.applicationInfo.uid < Process.FIRST_APPLICATION_UID
+ || userId < 1) {
+ return aInfo;
+ }
+
+ ActivityInfo info = new ActivityInfo(aInfo);
+ info.applicationInfo = getAppInfoForUser(info.applicationInfo, userId);
+ return info;
+ }
+
+ static class ServiceMap {
+
+ private final SparseArray<HashMap<ComponentName, ServiceRecord>> mServicesByNamePerUser
+ = new SparseArray<HashMap<ComponentName, ServiceRecord>>();
+ private final SparseArray<HashMap<Intent.FilterComparison, ServiceRecord>>
+ mServicesByIntentPerUser = new SparseArray<
+ HashMap<Intent.FilterComparison, ServiceRecord>>();
+
+ ServiceRecord getServiceByName(ComponentName name, int callingUser) {
+ // TODO: Deal with global services
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getServiceByName(" + name + "), callingUser = " + callingUser);
+ return getServices(callingUser).get(name);
+ }
+
+ ServiceRecord getServiceByName(ComponentName name) {
+ return getServiceByName(name, -1);
+ }
+
+ ServiceRecord getServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+ // TODO: Deal with global services
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "getServiceByIntent(" + filter + "), callingUser = " + callingUser);
+ return getServicesByIntent(callingUser).get(filter);
+ }
+
+ ServiceRecord getServiceByIntent(Intent.FilterComparison filter) {
+ return getServiceByIntent(filter, -1);
+ }
+
+ void putServiceByName(ComponentName name, int callingUser, ServiceRecord value) {
+ // TODO: Deal with global services
+ getServices(callingUser).put(name, value);
+ }
+
+ void putServiceByIntent(Intent.FilterComparison filter, int callingUser,
+ ServiceRecord value) {
+ // TODO: Deal with global services
+ getServicesByIntent(callingUser).put(filter, value);
+ }
+
+ void removeServiceByName(ComponentName name, int callingUser) {
+ // TODO: Deal with global services
+ ServiceRecord removed = getServices(callingUser).remove(name);
+ if (DEBUG_MU)
+ Slog.v(TAG, "removeServiceByName user=" + callingUser + " name=" + name
+ + " removed=" + removed);
+ }
+
+ void removeServiceByIntent(Intent.FilterComparison filter, int callingUser) {
+ // TODO: Deal with global services
+ ServiceRecord removed = getServicesByIntent(callingUser).remove(filter);
+ if (DEBUG_MU)
+ Slog.v(TAG_MU, "removeServiceByIntent user=" + callingUser + " intent=" + filter
+ + " removed=" + removed);
+ }
+
+ Collection<ServiceRecord> getAllServices(int callingUser) {
+ // TODO: Deal with global services
+ return getServices(callingUser).values();
+ }
+
+ private HashMap<ComponentName, ServiceRecord> getServices(int callingUser) {
+ HashMap map = mServicesByNamePerUser.get(callingUser);
+ if (map == null) {
+ map = new HashMap<ComponentName, ServiceRecord>();
+ mServicesByNamePerUser.put(callingUser, map);
+ }
+ return map;
+ }
+
+ private HashMap<Intent.FilterComparison, ServiceRecord> getServicesByIntent(
+ int callingUser) {
+ HashMap map = mServicesByIntentPerUser.get(callingUser);
+ if (map == null) {
+ map = new HashMap<Intent.FilterComparison, ServiceRecord>();
+ mServicesByIntentPerUser.put(callingUser, map);
+ }
+ return map;
+ }
+ }
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c819114..cdab6c6 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -25,6 +25,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Build;
@@ -34,6 +35,7 @@ import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -55,6 +57,7 @@ final class ActivityRecord {
final IApplicationToken.Stub appToken; // window manager token
final ActivityInfo info; // all about me
final int launchedFromUid; // always the uid who started the activity.
+ final int userId; // Which user is this running for?
final Intent intent; // the original intent that generated us
final ComponentName realActivity; // the intent component, or target of an alias.
final String shortComponentName; // the short component name of the intent
@@ -124,6 +127,7 @@ final class ActivityRecord {
pw.print(prefix); pw.print("packageName="); pw.print(packageName);
pw.print(" processName="); pw.println(processName);
pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
+ pw.print(prefix); pw.print("userId="); pw.print(userId);
pw.print(" app="); pw.println(app);
pw.print(prefix); pw.println(intent.toInsecureString());
pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
@@ -281,6 +285,7 @@ final class ActivityRecord {
appToken = new Token(this);
info = aInfo;
launchedFromUid = _launchedFromUid;
+ userId = UserId.getUserId(aInfo.applicationInfo.uid);
intent = _intent;
shortComponentName = _intent.getComponent().flattenToShortString();
resolvedType = _resolvedType;
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 6c11953..c3ae6a1 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -60,6 +60,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -274,6 +275,8 @@ final class ActivityStack {
int mThumbnailWidth = -1;
int mThumbnailHeight = -1;
+ private int mCurrentUser;
+
static final int SLEEP_TIMEOUT_MSG = 8;
static final int PAUSE_TIMEOUT_MSG = 9;
static final int IDLE_TIMEOUT_MSG = 10;
@@ -365,6 +368,7 @@ final class ActivityStack {
}
final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -377,6 +381,7 @@ final class ActivityStack {
}
final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -398,6 +403,7 @@ final class ActivityStack {
* @return Returns the HistoryRecord of the next activity on the stack.
*/
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+ // TODO: Don't look for any tasks from other users
int i = mHistory.size()-1;
while (i >= 0) {
ActivityRecord r = mHistory.get(i);
@@ -444,10 +450,11 @@ final class ActivityStack {
TaskRecord cp = null;
+ final int userId = UserId.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
- if (!r.finishing && r.task != cp
+ if (!r.finishing && r.task != cp && r.userId == userId
&& r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
cp = r.task;
//Slog.i(TAG, "Comparing existing cls=" + r.task.intent.getComponent().flattenToShortString()
@@ -487,12 +494,13 @@ final class ActivityStack {
if (info.targetActivity != null) {
cls = new ComponentName(info.packageName, info.targetActivity);
}
+ final int userId = UserId.getUserId(info.applicationInfo.uid);
final int N = mHistory.size();
for (int i=(N-1); i>=0; i--) {
ActivityRecord r = mHistory.get(i);
if (!r.finishing) {
- if (r.intent.getComponent().equals(cls)) {
+ if (r.intent.getComponent().equals(cls) && r.userId == userId) {
//Slog.i(TAG, "Found matching class!");
//dump();
//Slog.i(TAG, "For Intent " + intent + " bringing to top: " + r.intent);
@@ -511,6 +519,43 @@ final class ActivityStack {
mService.mHandler.sendMessage(msg);
}
+ /*
+ * Move the activities around in the stack to bring a user to the foreground.
+ * @return whether there are any activities for the specified user.
+ */
+ final boolean switchUser(int userId) {
+ synchronized (mService) {
+ mCurrentUser = userId;
+
+ // Only one activity? Nothing to do...
+ if (mHistory.size() < 2)
+ return false;
+
+ boolean haveActivities = false;
+ // Check if the top activity is from the new user.
+ ActivityRecord top = mHistory.get(mHistory.size() - 1);
+ if (top.userId == userId) return true;
+ // Otherwise, move the user's activities to the top.
+ int N = mHistory.size();
+ int i = 0;
+ while (i < N) {
+ ActivityRecord r = mHistory.get(i);
+ if (r.userId == userId) {
+ ActivityRecord moveToTop = mHistory.remove(i);
+ mHistory.add(moveToTop);
+ // No need to check the top one now
+ N--;
+ haveActivities = true;
+ } else {
+ i++;
+ }
+ }
+ // Transition from the old top to the new top
+ resumeTopActivityLocked(top);
+ return haveActivities;
+ }
+ }
+
final boolean realStartActivityLocked(ActivityRecord r,
ProcessRecord app, boolean andResume, boolean checkConfig)
throws RemoteException {
@@ -1272,7 +1317,7 @@ final class ActivityStack {
// There are no more activities! Let's just start up the
// Launcher...
if (mMainStack) {
- return mService.startHomeActivityLocked();
+ return mService.startHomeActivityLocked(0);
}
}
@@ -1384,6 +1429,7 @@ final class ActivityStack {
// Launching this app's activity, make sure the app is no longer
// considered stopped.
try {
+ // TODO: Apply to the correct userId
AppGlobals.getPackageManager().setPackageStoppedState(
next.packageName, false);
} catch (RemoteException e1) {
@@ -2354,7 +2400,7 @@ final class ActivityStack {
}
}
}
-
+
ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid,
intent, resolvedType, aInfo, mService.mConfiguration,
resultRecord, resultWho, requestCode, componentSpecified);
@@ -2420,7 +2466,8 @@ final class ActivityStack {
int grantedMode, boolean onlyIfNeeded, boolean doResume) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
-
+ final int userId = r.userId;
+
int launchFlags = intent.getFlags();
// We'll invoke onUserLeaving before onPause only if the launching
@@ -2648,7 +2695,7 @@ final class ActivityStack {
// once.
ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity)) {
+ if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
if (top.app != null && top.app.thread != null) {
if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
@@ -2821,12 +2868,12 @@ final class ActivityStack {
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, String profileFile, ParcelFileDescriptor profileFd,
- boolean autoStopProfiler, WaitResult outResult, Configuration config) {
+ boolean autoStopProfiler,
+ WaitResult outResult, Configuration config, int userId) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
-
boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
@@ -2835,6 +2882,7 @@ final class ActivityStack {
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedType, debug,
profileFile, profileFd, autoStopProfiler);
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
synchronized (mService) {
int callingPid;
@@ -2915,6 +2963,7 @@ final class ActivityStack {
PackageManager.MATCH_DEFAULT_ONLY
| ActivityManagerService.STOCK_PM_FLAGS);
aInfo = rInfo != null ? rInfo.activityInfo : null;
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
} catch (RemoteException e) {
aInfo = null;
}
@@ -2977,7 +3026,8 @@ final class ActivityStack {
}
final int startActivities(IApplicationThread caller, int callingUid,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo) {
+ Intent[] intents,
+ String[] resolvedTypes, IBinder resultTo, int userId) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -3022,6 +3072,8 @@ final class ActivityStack {
// Collect information about the target of the Intent.
ActivityInfo aInfo = resolveActivity(intent, resolvedTypes[i], false,
null, null, false);
+ // TODO: New, check if this is correct
+ aInfo = mService.getActivityInfoForUser(aInfo, userId);
if (mMainStack && aInfo != null && (aInfo.applicationInfo.flags
& ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index abd2a1f..3b6a97c 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserId;
import android.util.Slog;
import java.io.PrintWriter;
@@ -248,7 +249,8 @@ class PendingIntentRecord extends IIntentSender.Stub {
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
- requiredPermission, (finishedReceiver != null), false);
+ requiredPermission, (finishedReceiver != null), false, UserId
+ .getUserId(uid));
sendFinish = false;
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
new file mode 100644
index 0000000..44e7ecc
--- /dev/null
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserId;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Keeps track of content providers by authority (name) and class. It separates the mapping by
+ * user and ones that are not user-specific (system providers).
+ */
+public class ProviderMap {
+
+ private static final String TAG = "ProviderMap";
+
+ private static final boolean DBG = false;
+
+ private final HashMap<String, ContentProviderRecord> mGlobalByName
+ = new HashMap<String, ContentProviderRecord>();
+ private final HashMap<ComponentName, ContentProviderRecord> mGlobalByClass
+ = new HashMap<ComponentName, ContentProviderRecord>();
+
+ private final SparseArray<HashMap<String, ContentProviderRecord>> mProvidersByNamePerUser
+ = new SparseArray<HashMap<String, ContentProviderRecord>>();
+ private final SparseArray<HashMap<ComponentName, ContentProviderRecord>> mProvidersByClassPerUser
+ = new SparseArray<HashMap<ComponentName, ContentProviderRecord>>();
+
+ ContentProviderRecord getProviderByName(String name) {
+ return getProviderByName(name, -1);
+ }
+
+ ContentProviderRecord getProviderByName(String name, int userId) {
+ if (DBG) {
+ Slog.i(TAG, "getProviderByName: " + name + " , callingUid = " + Binder.getCallingUid());
+ }
+ // Try to find it in the global list
+ ContentProviderRecord record = mGlobalByName.get(name);
+ if (record != null) {
+ return record;
+ }
+
+ // Check the current user's list
+ return getProvidersByName(userId).get(name);
+ }
+
+ ContentProviderRecord getProviderByClass(ComponentName name) {
+ return getProviderByClass(name, -1);
+ }
+
+ ContentProviderRecord getProviderByClass(ComponentName name, int userId) {
+ if (DBG) {
+ Slog.i(TAG, "getProviderByClass: " + name + ", callingUid = " + Binder.getCallingUid());
+ }
+ // Try to find it in the global list
+ ContentProviderRecord record = mGlobalByClass.get(name);
+ if (record != null) {
+ return record;
+ }
+
+ // Check the current user's list
+ return getProvidersByClass(userId).get(name);
+ }
+
+ void putProviderByName(String name, ContentProviderRecord record) {
+ if (DBG) {
+ Slog.i(TAG, "putProviderByName: " + name + " , callingUid = " + Binder.getCallingUid()
+ + ", record uid = " + record.appInfo.uid);
+ }
+ if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
+ mGlobalByName.put(name, record);
+ } else {
+ final int userId = UserId.getUserId(record.appInfo.uid);
+ getProvidersByName(userId).put(name, record);
+ }
+ }
+
+ void putProviderByClass(ComponentName name, ContentProviderRecord record) {
+ if (DBG) {
+ Slog.i(TAG, "putProviderByClass: " + name + " , callingUid = " + Binder.getCallingUid()
+ + ", record uid = " + record.appInfo.uid);
+ }
+ if (record.appInfo.uid < Process.FIRST_APPLICATION_UID) {
+ mGlobalByClass.put(name, record);
+ } else {
+ final int userId = UserId.getUserId(record.appInfo.uid);
+ getProvidersByClass(userId).put(name, record);
+ }
+ }
+
+ void removeProviderByName(String name, int optionalUserId) {
+ if (mGlobalByName.containsKey(name)) {
+ if (DBG)
+ Slog.i(TAG, "Removing from globalByName name=" + name);
+ mGlobalByName.remove(name);
+ } else {
+ // TODO: Verify this works, i.e., the caller happens to be from the correct user
+ if (DBG)
+ Slog.i(TAG,
+ "Removing from providersByName name=" + name + " user="
+ + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+ getProvidersByName(optionalUserId).remove(name);
+ }
+ }
+
+ void removeProviderByClass(ComponentName name, int optionalUserId) {
+ if (mGlobalByClass.containsKey(name)) {
+ if (DBG)
+ Slog.i(TAG, "Removing from globalByClass name=" + name);
+ mGlobalByClass.remove(name);
+ } else {
+ if (DBG)
+ Slog.i(TAG,
+ "Removing from providersByClass name=" + name + " user="
+ + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
+ getProvidersByClass(optionalUserId).remove(name);
+ }
+ }
+
+ private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
+ final int userId = optionalUserId >= 0
+ ? optionalUserId : Binder.getOrigCallingUser();
+ final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
+ if (map == null) {
+ HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
+ mProvidersByNamePerUser.put(userId, newMap);
+ return newMap;
+ } else {
+ return map;
+ }
+ }
+
+ private HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
+ final int userId = optionalUserId >= 0
+ ? optionalUserId : Binder.getOrigCallingUser();
+ final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
+ if (map == null) {
+ HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
+ mProvidersByClassPerUser.put(userId, newMap);
+ return newMap;
+ } else {
+ return map;
+ }
+ }
+
+ private void dumpProvidersByClassLocked(PrintWriter pw, boolean dumpAll,
+ HashMap<ComponentName, ContentProviderRecord> map) {
+ Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<ComponentName, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
+ if (dumpAll) {
+ pw.print(" * ");
+ pw.println(r);
+ r.dump(pw, " ");
+ } else {
+ pw.print(" * ");
+ pw.print(r.name.toShortString());
+ /*
+ if (r.app != null) {
+ pw.println(":");
+ pw.print(" ");
+ pw.println(r.app);
+ } else {
+ pw.println();
+ }
+ */
+ }
+ }
+ }
+
+ private void dumpProvidersByNameLocked(PrintWriter pw,
+ HashMap<String, ContentProviderRecord> map) {
+ Iterator<Map.Entry<String, ContentProviderRecord>> it = map.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, ContentProviderRecord> e = it.next();
+ ContentProviderRecord r = e.getValue();
+ pw.print(" ");
+ pw.print(e.getKey());
+ pw.print(": ");
+ pw.println(r);
+ }
+ }
+
+ void dumpProvidersLocked(PrintWriter pw, boolean dumpAll) {
+ boolean needSep = false;
+ if (mGlobalByClass.size() > 0) {
+ if (needSep)
+ pw.println(" ");
+ pw.println(" Published content providers (by class):");
+ dumpProvidersByClassLocked(pw, dumpAll, mGlobalByClass);
+ pw.println(" ");
+ }
+
+ if (mProvidersByClassPerUser.size() > 1) {
+ for (int i = 0; i < mProvidersByClassPerUser.size(); i++) {
+ HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(i);
+ pw.println(" User " + mProvidersByClassPerUser.keyAt(i) + ":");
+ dumpProvidersByClassLocked(pw, dumpAll, map);
+ pw.println(" ");
+ }
+ } else if (mProvidersByClassPerUser.size() == 1) {
+ HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.valueAt(0);
+ dumpProvidersByClassLocked(pw, dumpAll, map);
+ }
+ needSep = true;
+
+ if (dumpAll) {
+ pw.println(" ");
+ pw.println(" Authority to provider mappings:");
+ dumpProvidersByNameLocked(pw, mGlobalByName);
+
+ for (int i = 0; i < mProvidersByNamePerUser.size(); i++) {
+ if (i > 0) {
+ pw.println(" User " + mProvidersByNamePerUser.keyAt(i) + ":");
+ }
+ dumpProvidersByNameLocked(pw, mProvidersByNamePerUser.valueAt(i));
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 257113b..75ba947 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -25,11 +25,13 @@ import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.Slog;
import android.util.TimeUtils;
@@ -60,6 +62,7 @@ class ServiceRecord extends Binder {
// all information about the service.
final ApplicationInfo appInfo;
// information about service's app.
+ final int userId; // user that this service is running as
final String packageName; // the package implementing intent's component
final String processName; // process where this component wants to run
final String permission;// permission needed to access service
@@ -289,6 +292,7 @@ class ServiceRecord extends Binder {
this.restarter = restarter;
createTime = SystemClock.elapsedRealtime();
lastActivity = SystemClock.uptimeMillis();
+ userId = UserId.getUserId(appInfo.uid);
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index de3129b..47ec218 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -19,7 +19,9 @@ package com.android.server.am;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.os.UserId;
import java.io.PrintWriter;
@@ -37,6 +39,7 @@ class TaskRecord extends ThumbnailHolder {
boolean askedCompatMode;// Have asked the user about compat mode for this task.
String stringName; // caching of toString() result.
+ int userId; // user for which this task was created
TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
taskId = _taskId;
@@ -84,13 +87,17 @@ class TaskRecord extends ThumbnailHolder {
origActivity = new ComponentName(info.packageName, info.name);
}
}
-
+
if (intent != null &&
(intent.getFlags()&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
// Once we are set to an Intent with this flag, we count this
// task as having a true root activity.
rootWasReset = true;
}
+
+ if (info.applicationInfo != null) {
+ userId = UserId.getUserId(info.applicationInfo.uid);
+ }
}
void dump(PrintWriter pw, String prefix) {
@@ -154,6 +161,8 @@ class TaskRecord extends ThumbnailHolder {
} else {
sb.append(" ??");
}
+ sb.append(" U ");
+ sb.append(userId);
sb.append('}');
return stringName = sb.toString();
}
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 11ccd60..9b1973e 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -277,6 +277,27 @@ class Installer {
return execute(builder.toString());
}
+ /**
+ * Clone all the package data directories from srcUserId to targetUserId. If copyData is true,
+ * some of the data is also copied, otherwise just empty directories are created with the
+ * correct access rights.
+ * @param srcUserId user to copy the data directories from
+ * @param targetUserId user to copy the data directories to
+ * @param copyData whether the data itself is to be copied. If false, empty directories are
+ * created.
+ * @return success/error code
+ */
+ public int cloneUserData(int srcUserId, int targetUserId, boolean copyData) {
+ StringBuilder builder = new StringBuilder("cloneuserdata");
+ builder.append(' ');
+ builder.append(srcUserId);
+ builder.append(' ');
+ builder.append(targetUserId);
+ builder.append(' ');
+ builder.append(copyData ? '1' : '0');
+ return execute(builder.toString());
+ }
+
public boolean ping() {
if (execute("ping") < 0) {
return false;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 090ca64..38c128c 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -92,6 +92,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserId;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
import android.util.EventLog;
@@ -1743,12 +1744,16 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public ActivityInfo getActivityInfo(ComponentName component, int flags) {
+ return getActivityInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
- return PackageParser.generateActivityInfo(a, flags);
+ return PackageParser.generateActivityInfo(a, flags, userId);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
@@ -1758,36 +1763,48 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public ActivityInfo getReceiverInfo(ComponentName component, int flags) {
+ return getReceiverInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLPr(a.info, flags)) {
- return PackageParser.generateActivityInfo(a, flags);
+ return PackageParser.generateActivityInfo(a, flags, userId);
}
}
return null;
}
public ServiceInfo getServiceInfo(ComponentName component, int flags) {
+ return getServiceInfo(component, flags, Binder.getOrigCallingUser());
+ }
+
+ ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
if (s != null && mSettings.isEnabledLPr(s.info, flags)) {
- return PackageParser.generateServiceInfo(s, flags);
+ return PackageParser.generateServiceInfo(s, flags, userId);
}
}
return null;
}
public ProviderInfo getProviderInfo(ComponentName component, int flags) {
+ return getProviderInfo(component, flags, UserId.getUserId(Binder.getCallingUid()));
+ }
+
+ ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
synchronized (mPackages) {
PackageParser.Provider p = mProvidersByComponent.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
if (p != null && mSettings.isEnabledLPr(p.info, flags)) {
- return PackageParser.generateProviderInfo(p, flags);
+ return PackageParser.generateProviderInfo(p, flags, userId);
}
}
return null;
@@ -1850,7 +1867,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public int checkUidPermission(String permName, int uid) {
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
if (obj != null) {
GrantedPermissions gp = (GrantedPermissions)obj;
if (gp.grantedPermissions.contains(permName)) {
@@ -1881,7 +1898,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (permName != null) {
BasePermission bp = findPermissionTreeLP(permName);
if (bp != null) {
- if (bp.uid == Binder.getCallingUid()) {
+ if (bp.uid == UserId.getAppId(Binder.getCallingUid())) {
return bp;
}
throw new SecurityException("Calling uid "
@@ -2010,6 +2027,9 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public int checkUidSignatures(int uid1, int uid2) {
+ // Map to base uids.
+ uid1 = UserId.getAppId(uid1);
+ uid2 = UserId.getAppId(uid2);
// reader
synchronized (mPackages) {
Signature[] s1;
@@ -2067,6 +2087,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
public String[] getPackagesForUid(int uid) {
+ uid = UserId.getAppId(uid);
// reader
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(uid);
@@ -2091,7 +2112,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public String getNameForUid(int uid) {
// reader
synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(uid);
+ Object obj = mSettings.getUserIdLPr(UserId.getAppId(uid));
if (obj instanceof SharedUserSetting) {
final SharedUserSetting sus = (SharedUserSetting) obj;
return sus.name + ":" + sus.userId;
@@ -2110,7 +2131,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName, 0, false);
- if(suid == null) {
+ if (suid == null) {
return -1;
}
return suid.userId;
@@ -2252,6 +2273,9 @@ public class PackageManagerService extends IPackageManager.Stub {
comp = intent.getComponent();
}
}
+
+ final int userId = UserId.getUserId(Binder.getCallingUid());
+
if (comp != null) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ActivityInfo ai = getActivityInfo(comp, flags);
@@ -2603,6 +2627,7 @@ public class PackageManagerService extends IPackageManager.Stub {
Arrays.sort(keys);
int i = getContinuationPoint(keys, lastRead);
final int N = keys.length;
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i < N) {
final String packageName = keys[i++];
@@ -2616,7 +2641,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
- ai = PackageParser.generateApplicationInfo(p, flags);
+ ai = PackageParser.generateApplicationInfo(p, flags, userId);
}
}
@@ -2639,12 +2664,13 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
- finalList.add(PackageParser.generateApplicationInfo(p, flags));
+ finalList.add(PackageParser.generateApplicationInfo(p, flags, userId));
}
}
}
@@ -2660,7 +2686,8 @@ public class PackageManagerService extends IPackageManager.Stub {
&& mSettings.isEnabledLPr(provider.info, flags)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
- ? PackageParser.generateProviderInfo(provider, flags)
+ ? PackageParser.generateProviderInfo(provider, flags,
+ UserId.getUserId(Binder.getCallingUid()))
: null;
}
}
@@ -2674,7 +2701,7 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
.iterator();
-
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
PackageParser.Provider p = entry.getValue();
@@ -2683,7 +2710,7 @@ public class PackageManagerService extends IPackageManager.Stub {
&& (!mSafeMode || (p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)) {
outNames.add(entry.getKey());
- outInfo.add(PackageParser.generateProviderInfo(p, 0));
+ outInfo.add(PackageParser.generateProviderInfo(p, 0, userId));
}
}
}
@@ -2696,19 +2723,21 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator();
+ final int userId = UserId.getUserId(Binder.getCallingUid());
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
if (p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
- && p.info.applicationInfo.uid == uid))
+ && UserId.getAppId(p.info.applicationInfo.uid)
+ == UserId.getAppId(uid)))
&& mSettings.isEnabledLPr(p.info, flags)
&& (!mSafeMode
|| (p.info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
- finalList.add(PackageParser.generateProviderInfo(p, flags));
+ finalList.add(PackageParser.generateProviderInfo(p, flags, userId));
}
}
}
@@ -4461,8 +4490,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.activityInfo = PackageParser.generateActivityInfo(activity,
- mFlags);
+ res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
+ Binder.getOrigCallingUser());
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
@@ -4637,8 +4666,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.serviceInfo = PackageParser.generateServiceInfo(service,
- mFlags);
+ res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
+ Binder.getOrigCallingUser());
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
@@ -4742,8 +4771,10 @@ public class PackageManagerService extends IPackageManager.Stub {
intent.setPackage(targetPkg);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ // TODO: Fix the userId argument
am.broadcastIntent(null, intent, null, finishedReceiver,
- 0, null, null, null, finishedReceiver != null, false);
+ 0, null, null, null, finishedReceiver != null, false,
+ Binder.getOrigCallingUser());
} catch (RemoteException ex) {
}
}
@@ -7584,7 +7615,7 @@ public class PackageManagerService extends IPackageManager.Stub {
"Unknown component: " + packageName
+ "/" + className);
}
- if (!allowedByPermission && (uid != pkgSetting.userId)) {
+ if (!allowedByPermission && (!UserId.isSameApp(uid, pkgSetting.userId))) {
throw new SecurityException(
"Permission Denial: attempt to change component state from pid="
+ Binder.getCallingPid()
@@ -8673,4 +8704,8 @@ public class PackageManagerService extends IPackageManager.Stub {
return mSettings.getVerifierDeviceIdentityLPw();
}
}
+
+ public List<UserInfo> getUsers() {
+ return mUserManager.getUsers();
+ }
}
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 2687728..5eacf4a 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -24,6 +24,7 @@ import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.FileUtils;
import android.os.SystemClock;
+import android.os.UserId;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -169,9 +170,10 @@ public class UserManager {
* </user>
*/
private void writeUser(UserInfo userInfo) {
+ FileOutputStream fos = null;
try {
final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
- final FileOutputStream fos = new FileOutputStream(mUserFile);
+ fos = new FileOutputStream(mUserFile);
final BufferedOutputStream bos = new BufferedOutputStream(fos);
// XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -193,6 +195,13 @@ public class UserManager {
serializer.endDocument();
} catch (IOException ioe) {
Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ioe) {
+ }
+ }
}
}
@@ -205,8 +214,9 @@ public class UserManager {
* </users>
*/
private void writeUserList() {
+ FileOutputStream fos = null;
try {
- final FileOutputStream fos = new FileOutputStream(mUserListFile);
+ fos = new FileOutputStream(mUserListFile);
final BufferedOutputStream bos = new BufferedOutputStream(fos);
// XmlSerializer serializer = XmlUtils.serializerInstance();
@@ -229,6 +239,13 @@ public class UserManager {
serializer.endDocument();
} catch (IOException ioe) {
Slog.e(LOG_TAG, "Error writing user list");
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ioe) {
+ }
+ }
}
}
@@ -330,7 +347,7 @@ public class UserManager {
// Don't do it for the primary user, it will become recursive.
if (userId == 0)
continue;
- mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+ mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
userId);
}
}
@@ -367,6 +384,8 @@ public class UserManager {
/**
* Returns the next available user id, filling in any holes in the ids.
+ * TODO: May not be a good idea to recycle ids, in case it results in confusion
+ * for data and battery stats collection, or unexpected cross-talk.
* @return
*/
private int getNextAvailableId() {
@@ -390,14 +409,8 @@ public class UserManager {
FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
| FileUtils.S_IXOTH, -1, -1);
- // Create the individual data directories
- for (ApplicationInfo app : apps) {
- if (app.uid > android.os.Process.FIRST_APPLICATION_UID
- && app.uid < PackageManager.PER_USER_RANGE) {
- mInstaller.createUserData(app.packageName,
- PackageManager.getUid(id, app.uid), id);
- }
- }
+ mInstaller.cloneUserData(0, id, false);
+
final long stopTime = SystemClock.elapsedRealtime();
Log.i(LOG_TAG,
"Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");