Merge "Import translations. DO NOT MERGE"
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 27c9c8b..197c1bd 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -231,6 +231,14 @@
}
}, intentFilter);
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ }, userFilter);
}
private UserAccounts initUser(int userId) {
@@ -347,6 +355,28 @@
}
}
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (userId < 1) return;
+
+ UserAccounts accounts;
+ synchronized (mUsers) {
+ accounts = mUsers.get(userId);
+ mUsers.remove(userId);
+ }
+ if (accounts == null) {
+ File dbFile = new File(getDatabaseName(userId));
+ dbFile.delete();
+ return;
+ }
+
+ synchronized (accounts.cacheLock) {
+ accounts.openHelper.close();
+ File dbFile = new File(getDatabaseName(userId));
+ dbFile.delete();
+ }
+ }
+
private List<UserInfo> getAllUsers() {
try {
return AppGlobals.getPackageManager().getUsers();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f38540c..0510de1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1215,6 +1215,18 @@
* @hide
*/
@Override
+ public UserInfo getUser(int userId) {
+ try {
+ return mPM.getUser(userId);
+ } catch (RemoteException re) {
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public boolean removeUser(int id) {
try {
return mPM.removeUser(id);
@@ -1228,7 +1240,10 @@
*/
@Override
public void updateUserName(int id, String name) {
- // TODO:
+ try {
+ mPM.updateUserName(id, name);
+ } catch (RemoteException re) {
+ }
}
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1c9ef38..2a9f1af 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2141,6 +2141,30 @@
public static final String ACTION_PRE_BOOT_COMPLETED =
"android.intent.action.PRE_BOOT_COMPLETED";
+ /**
+ * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USERID that has the
+ * userid of the new user.
+ * @hide
+ */
+ public static final String ACTION_USER_ADDED =
+ "android.intent.action.USER_ADDED";
+
+ /**
+ * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USERID that has
+ * the userid of the user.
+ * @hide
+ */
+ public static final String ACTION_USER_REMOVED =
+ "android.intent.action.USER_REMOVED";
+
+ /**
+ * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USERID that has
+ * the userid of the user to become the current one.
+ * @hide
+ */
+ public static final String ACTION_USER_SWITCHED =
+ "android.intent.action.USER_SWITCHED";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
@@ -2682,6 +2706,13 @@
public static final String EXTRA_LOCAL_ONLY =
"android.intent.extra.LOCAL_ONLY";
+ /**
+ * The userid carried with broadcast intents related to addition, removal and switching of users
+ * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
+ * @hide
+ */
+ public static final String EXTRA_USERID =
+ "android.intent.extra.user_id";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
@@ -6483,7 +6514,12 @@
final String action = getAction();
if (ACTION_SEND.equals(action)) {
- final Uri stream = getParcelableExtra(EXTRA_STREAM);
+ final Uri stream;
+ try {
+ stream = getParcelableExtra(EXTRA_STREAM);
+ } catch (ClassCastException e) {
+ return;
+ }
if (stream != null) {
final ClipData clipData = new ClipData(
null, new String[] { getType() }, new ClipData.Item(stream));
@@ -6493,7 +6529,12 @@
}
} else if (ACTION_SEND_MULTIPLE.equals(action)) {
- final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM);
+ final ArrayList<Uri> streams;
+ try {
+ streams = getParcelableArrayListExtra(EXTRA_STREAM);
+ } catch (ClassCastException e) {
+ return;
+ }
if (streams != null && streams.size() > 0) {
final Uri firstStream = streams.get(0);
final ClipData clipData = new ClipData(
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index b7dfe92..06dfe90 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -326,6 +326,13 @@
}
};
+ private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ };
+
private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
private final SyncHandler mSyncHandler;
@@ -420,6 +427,10 @@
intentFilter.setPriority(100);
context.registerReceiver(mShutdownIntentReceiver, intentFilter);
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserIntentReceiver, intentFilter);
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -905,6 +916,18 @@
}
}
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (userId == -1) return;
+
+ // Clean up the storage engine database
+ mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
+ onAccountsUpdated(null);
+ synchronized (mSyncQueue) {
+ mSyncQueue.removeUser(userId);
+ }
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 06da6fa..c18c86bf 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -117,6 +117,19 @@
return true;
}
+ public void removeUser(int userId) {
+ ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>();
+ for (SyncOperation op : mOperationsMap.values()) {
+ if (op.userId == userId) {
+ opsToRemove.add(op);
+ }
+ }
+
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
+ }
+
/**
* Remove the specified operation if it is in the queue.
* @param operation the operation to remove
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d89d2de..56fd5f8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -358,6 +358,7 @@
UserInfo createUser(in String name, int flags);
boolean removeUser(int userId);
+ void updateUserName(int userId, String name);
void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer,
int flags, in String installerPackageName, in Uri verificationURI,
@@ -370,6 +371,7 @@
boolean isFirstBoot();
List<UserInfo> getUsers();
+ UserInfo getUser(int userId);
void setPermissionEnforcement(String permission, int enforcement);
int getPermissionEnforcement(String permission);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 55426b8..b06b4a5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2153,7 +2153,8 @@
if ((flags & GET_SIGNATURES) != 0) {
packageParser.collectCertificates(pkg, 0);
}
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null);
+ return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, false,
+ COMPONENT_ENABLED_STATE_DEFAULT);
}
/**
@@ -2637,10 +2638,17 @@
public abstract void updateUserFlags(int id, int flags);
/**
- * 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.
- *
+ * Returns the details for the user specified by userId.
+ * @param userId the user id of the user
+ * @return UserInfo for the specified user, or null if no such user exists.
+ * @hide
+ */
+ public abstract UserInfo getUser(int userId);
+
+ /**
+ * 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.
+ *
* @return identity that uniquely identifies current device
* @hide
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 07d231a..eb8536f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -230,6 +230,15 @@
return name.endsWith(".apk");
}
+ public static PackageInfo generatePackageInfo(PackageParser.Package p,
+ int gids[], int flags, long firstInstallTime, long lastUpdateTime,
+ HashSet<String> grantedPermissions) {
+
+ return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
+ grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ UserId.getCallingUserId());
+ }
+
/**
* Generate and return the {@link PackageInfo} for a parsed package.
*
@@ -238,15 +247,15 @@
*/
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions) {
+ HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, UserId.getCallingUserId());
+ grantedPermissions, stopped, enabledState, UserId.getCallingUserId());
}
- static PackageInfo generatePackageInfo(PackageParser.Package p,
+ public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions, int userId) {
+ HashSet<String> grantedPermissions, boolean stopped, int enabledState, int userId) {
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
@@ -254,7 +263,7 @@
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
- pi.applicationInfo = generateApplicationInfo(p, flags);
+ pi.applicationInfo = generateApplicationInfo(p, flags, stopped, enabledState, userId);
pi.installLocation = p.installLocation;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
@@ -290,7 +299,7 @@
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
- userId);
+ stopped, enabledState, userId);
}
}
}
@@ -311,7 +320,8 @@
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, userId);
+ pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
+ stopped, enabledState, userId);
}
}
}
@@ -332,7 +342,8 @@
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, userId);
+ pi.services[j++] = generateServiceInfo(p.services.get(i), flags, stopped,
+ enabledState, userId);
}
}
}
@@ -353,7 +364,8 @@
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, userId);
+ pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, stopped,
+ enabledState, userId);
}
}
}
@@ -3068,11 +3080,11 @@
// For use by package manager to keep track of where it has done dexopt.
public boolean mDidDexOpt;
- // User set enabled state.
- public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-
- // Whether the package has been stopped.
- public boolean mSetStopped = false;
+ // // User set enabled state.
+ // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ //
+ // // Whether the package has been stopped.
+ // public boolean mSetStopped = false;
// Additional data supplied by callers.
public Object mExtras;
@@ -3337,9 +3349,9 @@
}
}
- private static boolean copyNeeded(int flags, Package p, Bundle metaData) {
- if (p.mSetEnabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
- boolean enabled = p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ private static boolean copyNeeded(int flags, Package p, int enabledState, Bundle metaData) {
+ if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ boolean enabled = enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
if (p.applicationInfo.enabled != enabled) {
return true;
}
@@ -3355,30 +3367,32 @@
return false;
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
- return generateApplicationInfo(p, flags, UserId.getCallingUserId());
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
+ int enabledState) {
+ return generateApplicationInfo(p, flags, stopped, enabledState, UserId.getCallingUserId());
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags, int userId) {
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+ boolean stopped, int enabledState, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p, null) && userId == 0) {
+ if (!copyNeeded(flags, p, enabledState, null) && userId == 0) {
// CompatibilityMode is global state. It's safe to modify the instance
// of the package.
if (!sCompatibilityModeEnabled) {
p.applicationInfo.disableCompatibilityMode();
}
- if (p.mSetStopped) {
+ if (stopped) {
p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
p.applicationInfo.enabled = true;
- } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
p.applicationInfo.enabled = false;
}
- p.applicationInfo.enabledSetting = p.mSetEnabled;
+ p.applicationInfo.enabledSetting = enabledState;
return p.applicationInfo;
}
@@ -3397,18 +3411,18 @@
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
- if (p.mSetStopped) {
+ if (stopped) {
p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
- } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
ai.enabled = false;
}
- ai.enabledSetting = p.mSetEnabled;
+ ai.enabledSetting = enabledState;
return ai;
}
@@ -3455,15 +3469,16 @@
}
}
- public static final ActivityInfo generateActivityInfo(Activity a, int flags, int userId) {
+ public static final ActivityInfo generateActivityInfo(Activity a, int flags, boolean stopped,
+ int enabledState, int userId) {
if (a == null) return null;
- if (!copyNeeded(flags, a.owner, a.metaData) && userId == 0) {
+ if (!copyNeeded(flags, a.owner, enabledState, 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, userId);
+ ai.applicationInfo = generateApplicationInfo(a.owner, flags, stopped, enabledState, userId);
return ai;
}
@@ -3488,16 +3503,17 @@
}
}
- public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
+ public static final ServiceInfo generateServiceInfo(Service s, int flags, boolean stopped,
+ int enabledState, int userId) {
if (s == null) return null;
- if (!copyNeeded(flags, s.owner, s.metaData)
+ if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
&& userId == UserId.getUserId(s.info.applicationInfo.uid)) {
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, userId);
+ si.applicationInfo = generateApplicationInfo(s.owner, flags, stopped, enabledState, userId);
return si;
}
@@ -3530,9 +3546,10 @@
}
}
- public static final ProviderInfo generateProviderInfo(Provider p, int flags, int userId) {
+ public static final ProviderInfo generateProviderInfo(Provider p, int flags, boolean stopped,
+ int enabledState, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p.owner, p.metaData)
+ if (!copyNeeded(flags, p.owner, enabledState, p.metaData)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
|| p.info.uriPermissionPatterns == null)
&& userId == 0) {
@@ -3544,7 +3561,7 @@
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags, userId);
+ pi.applicationInfo = generateApplicationInfo(p.owner, flags, stopped, enabledState, userId);
return pi;
}
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index a21d3ee..349113e 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -42,12 +42,7 @@
public static final boolean WEB_BACK_FORWARD_LIST = false;
public static final boolean WEB_SETTINGS = false;
public static final boolean WEB_SYNC_MANAGER = false;
- public static final boolean WEB_TEXT_VIEW = false;
public static final boolean WEB_VIEW = false;
public static final boolean WEB_VIEW_CORE = false;
- /*
- * Set to true to allow the WebTextView to draw on top of the web page in a
- * different color with no background so you can see how the two line up.
- */
- public static final boolean DRAW_WEBTEXTVIEW = false;
+ public static final boolean MEASURE_PAGE_SWAP_FPS = false;
}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index ad644e4..5ae2fe0 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -4717,10 +4717,10 @@
queueFull = nativeSetBaseLayer(mNativeClass, layer, invalRegion,
showVisualIndicator, isPictureAfterFirstLayout);
- if (layer == 0 || isPictureAfterFirstLayout) {
- mWebViewCore.resumeWebKitDraw();
- } else if (queueFull) {
+ if (queueFull) {
mWebViewCore.pauseWebKitDraw();
+ } else {
+ mWebViewCore.resumeWebKitDraw();
}
if (mHTML5VideoViewProxy != null) {
@@ -8615,9 +8615,19 @@
void onPageSwapOccurred(boolean notifyAnimationStarted);
}
+ long mLastSwapTime;
+ double mAverageSwapFps;
+
/** Called by JNI when pages are swapped (only occurs with hardware
* acceleration) */
protected void pageSwapCallback(boolean notifyAnimationStarted) {
+ if (DebugFlags.MEASURE_PAGE_SWAP_FPS) {
+ long now = System.currentTimeMillis();
+ long diff = now - mLastSwapTime;
+ mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2;
+ Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps);
+ mLastSwapTime = now;
+ }
mWebViewCore.resumeWebKitDraw();
if (notifyAnimationStarted) {
mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index b47f71d..afb2992 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2271,6 +2271,7 @@
mFirstLayoutForNonStandardLoad = false;
}
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
+ pauseWebKitDraw();
Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget();
}
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index b04f890..7c937ed 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -20,6 +20,8 @@
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
import com.google.android.mms.util.PduCache;
import com.google.android.mms.util.PduCacheEntry;
import com.google.android.mms.util.SqliteWrapper;
@@ -30,7 +32,11 @@
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteException;
+import android.drm.DrmManagerClient;
import android.net.Uri;
+import android.os.FileUtils;
+import android.provider.MediaStore;
import android.provider.Telephony;
import android.provider.Telephony.Mms;
import android.provider.Telephony.MmsSms;
@@ -42,6 +48,7 @@
import android.util.Log;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -271,10 +278,12 @@
private final Context mContext;
private final ContentResolver mContentResolver;
+ private final DrmManagerClient mDrmManagerClient;
private PduPersister(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+ mDrmManagerClient = new DrmManagerClient(context);
}
/** Get(or create if not exist) an instance of PduPersister */
@@ -761,6 +770,9 @@
throws MmsException {
OutputStream os = null;
InputStream is = null;
+ DrmConvertSession drmConvertSession = null;
+ Uri dataUri = null;
+ String path = null;
try {
byte[] data = part.getData();
@@ -773,9 +785,38 @@
throw new MmsException("unable to update " + uri.toString());
}
} else {
+ boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType);
+ if (isDrm) {
+ if (uri != null) {
+ try {
+ path = convertUriToPath(mContext, uri);
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "drm uri: " + uri + " path: " + path);
+ }
+ File f = new File(path);
+ long len = f.length();
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "drm path: " + path + " len: " + len);
+ }
+ if (len > 0) {
+ // we're not going to re-persist and re-encrypt an already
+ // converted drm file
+ return;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e);
+ }
+ }
+ // We haven't converted the file yet, start the conversion
+ drmConvertSession = DrmConvertSession.open(mContext, contentType);
+ if (drmConvertSession == null) {
+ throw new MmsException("Mimetype " + contentType +
+ " can not be converted.");
+ }
+ }
os = mContentResolver.openOutputStream(uri);
if (data == null) {
- Uri dataUri = part.getDataUri();
+ dataUri = part.getDataUri();
if ((dataUri == null) || (dataUri == uri)) {
Log.w(TAG, "Can't find data for this part.");
return;
@@ -788,13 +829,32 @@
byte[] buffer = new byte[8192];
for (int len = 0; (len = is.read(buffer)) != -1; ) {
- os.write(buffer, 0, len);
+ if (!isDrm) {
+ os.write(buffer, 0, len);
+ } else {
+ byte[] convertedData = drmConvertSession.convert(buffer, len);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
}
} else {
if (LOCAL_LOGV) {
Log.v(TAG, "Saving data to: " + uri);
}
- os.write(data);
+ if (!isDrm) {
+ os.write(data);
+ } else {
+ dataUri = uri;
+ byte[] convertedData = drmConvertSession.convert(data, data.length);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
}
}
} catch (FileNotFoundException e) {
@@ -818,9 +878,67 @@
Log.e(TAG, "IOException while closing: " + is, e);
} // Ignore
}
+ if (drmConvertSession != null) {
+ drmConvertSession.close(path);
+
+ // Reset the permissions on the encrypted part file so everyone has only read
+ // permission.
+ File f = new File(path);
+ ContentValues values = new ContentValues(0);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/resetFilePerm/" + f.getName()),
+ values, null, null);
+ }
}
}
+ /**
+ * This method expects uri in the following format
+ * content://media/<table_name>/<row_index> (or)
+ * file://sdcard/test.mp4
+ * http://test.com/test.mp4
+ *
+ * Here <table_name> shall be "video" or "audio" or "images"
+ * <row_index> the index of the content in given table
+ */
+ static public String convertUriToPath(Context context, Uri uri) {
+ String path = null;
+ if (null != uri) {
+ String scheme = uri.getScheme();
+ if (null == scheme || scheme.equals("") ||
+ scheme.equals(ContentResolver.SCHEME_FILE)) {
+ path = uri.getPath();
+
+ } else if (scheme.equals("http")) {
+ path = uri.toString();
+
+ } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+ String[] projection = new String[] {MediaStore.MediaColumns.DATA};
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(uri, projection, null,
+ null, null);
+ if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) {
+ throw new IllegalArgumentException("Given Uri could not be found" +
+ " in media store");
+ }
+ int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
+ path = cursor.getString(pathIndex);
+ } catch (SQLiteException e) {
+ throw new IllegalArgumentException("Given Uri is not formatted in a way " +
+ "so that it can be found in media store.");
+ } finally {
+ if (null != cursor) {
+ cursor.close();
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Given Uri scheme is not supported");
+ }
+ }
+ return path;
+ }
+
private void updateAddress(
long msgId, int type, EncodedStringValue[] array) {
// Delete old address information and then insert new ones.
diff --git a/core/java/com/google/android/mms/util/DownloadDrmHelper.java b/core/java/com/google/android/mms/util/DownloadDrmHelper.java
new file mode 100644
index 0000000..6852eca
--- /dev/null
+++ b/core/java/com/google/android/mms/util/DownloadDrmHelper.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 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.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+
+public class DownloadDrmHelper {
+ private static final String TAG = "DownloadDrmHelper";
+
+ /** The MIME type of special DRM files */
+ public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ /** The extensions of special DRM files */
+ public static final String EXTENSION_DRM_MESSAGE = ".dm";
+
+ public static final String EXTENSION_INTERNAL_FWDL = ".fl";
+
+ /**
+ * Checks if the Media Type is a DRM Media Type
+ *
+ * @param drmManagerClient A DrmManagerClient
+ * @param mimetype Media Type to check
+ * @return True if the Media Type is DRM else false
+ */
+ public static boolean isDrmMimeType(Context context, String mimetype) {
+ boolean result = false;
+ if (context != null) {
+ try {
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ if (drmClient != null && mimetype != null && mimetype.length() > 0) {
+ result = drmClient.canHandle("", mimetype);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the Media Type needs to be DRM converted
+ *
+ * @param mimetype Media type of the content
+ * @return True if convert is needed else false
+ */
+ public static boolean isDrmConvertNeeded(String mimetype) {
+ return MIMETYPE_DRM_MESSAGE.equals(mimetype);
+ }
+
+ /**
+ * Modifies the file extension for a DRM Forward Lock file NOTE: This
+ * function shouldn't be called if the file shouldn't be DRM converted
+ */
+ public static String modifyDrmFwLockFileExtension(String filename) {
+ if (filename != null) {
+ int extensionIndex;
+ extensionIndex = filename.lastIndexOf(".");
+ if (extensionIndex != -1) {
+ filename = filename.substring(0, extensionIndex);
+ }
+ filename = filename.concat(EXTENSION_INTERNAL_FWDL);
+ }
+ return filename;
+ }
+
+ /**
+ * Gets the original mime type of DRM protected content.
+ *
+ * @param context The context
+ * @param path Path to the file
+ * @param containingMime The current mime type of of the file i.e. the
+ * containing mime type
+ * @return The original mime type of the file if DRM protected else the
+ * currentMime
+ */
+ public static String getOriginalMimeType(Context context, String path, String containingMime) {
+ String result = containingMime;
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ try {
+ if (drmClient.canHandle(path, null)) {
+ result = drmClient.getOriginalMimeType(path);
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG,
+ "Can't get original mime type since path is null or empty string.");
+ } catch (IllegalStateException ex) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ return result;
+ }
+}
diff --git a/core/java/com/google/android/mms/util/DrmConvertSession.java b/core/java/com/google/android/mms/util/DrmConvertSession.java
new file mode 100644
index 0000000..2d8f274
--- /dev/null
+++ b/core/java/com/google/android/mms/util/DrmConvertSession.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 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.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmConvertedStatus;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+import android.provider.Downloads;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+public class DrmConvertSession {
+ private DrmManagerClient mDrmClient;
+ private int mConvertSessionId;
+ private static final String TAG = "DrmConvertSession";
+
+ private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
+ mDrmClient = drmClient;
+ mConvertSessionId = convertSessionId;
+ }
+
+ /**
+ * Start of converting a file.
+ *
+ * @param context The context of the application running the convert session.
+ * @param mimeType Mimetype of content that shall be converted.
+ * @return A convert session or null in case an error occurs.
+ */
+ public static DrmConvertSession open(Context context, String mimeType) {
+ DrmManagerClient drmClient = null;
+ int convertSessionId = -1;
+ if (context != null && mimeType != null && !mimeType.equals("")) {
+ try {
+ drmClient = new DrmManagerClient(context);
+ try {
+ convertSessionId = drmClient.openConvertSession(mimeType);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Conversion of Mimetype: " + mimeType
+ + " is not supported.", e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not access Open DrmFramework.", e);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+
+ if (drmClient == null || convertSessionId < 0) {
+ return null;
+ } else {
+ return new DrmConvertSession(drmClient, convertSessionId);
+ }
+ }
+ /**
+ * Convert a buffer of data to protected format.
+ *
+ * @param buffer Buffer filled with data to convert.
+ * @param size The number of bytes that shall be converted.
+ * @return A Buffer filled with converted data, if execution is ok, in all
+ * other case null.
+ */
+ public byte [] convert(byte[] inBuffer, int size) {
+ byte[] result = null;
+ if (inBuffer != null) {
+ DrmConvertedStatus convertedStatus = null;
+ try {
+ if (size != inBuffer.length) {
+ byte[] buf = new byte[size];
+ System.arraycopy(inBuffer, 0, buf, 0, size);
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, buf);
+ } else {
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer);
+ }
+
+ if (convertedStatus != null &&
+ convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK &&
+ convertedStatus.convertedData != null) {
+ result = convertedStatus.convertedData;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
+ + mConvertSessionId, e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not convert data. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ } else {
+ throw new IllegalArgumentException("Parameter inBuffer is null");
+ }
+ return result;
+ }
+
+ /**
+ * Ends a conversion session of a file.
+ *
+ * @param fileName The filename of the converted file.
+ * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
+ * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
+ * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * occurs when accessing drm framework.
+ * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ */
+ public int close(String filename) {
+ DrmConvertedStatus convertedStatus = null;
+ int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ if (mDrmClient != null && mConvertSessionId >= 0) {
+ try {
+ convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
+ if (convertedStatus == null ||
+ convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
+ convertedStatus.convertedData == null) {
+ result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ } else {
+ RandomAccessFile rndAccessFile = null;
+ try {
+ rndAccessFile = new RandomAccessFile(filename, "rw");
+ rndAccessFile.seek(convertedStatus.offset);
+ rndAccessFile.write(convertedStatus.convertedData);
+ result = Downloads.Impl.STATUS_SUCCESS;
+ } catch (FileNotFoundException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "File: " + filename + " could not be found.", e);
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not access File: " + filename + " .", e);
+ } catch (IllegalArgumentException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not open file in mode: rw", e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Access to File: " + filename +
+ " was denied denied by SecurityManager.", e);
+ } finally {
+ if (rndAccessFile != null) {
+ try {
+ rndAccessFile.close();
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Failed to close File:" + filename
+ + ".", e);
+ }
+ }
+ }
+ }
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not close convertsession. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ }
+ return result;
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d1e3642..5ae12b6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -60,6 +60,9 @@
<protected-broadcast android:name="android.intent.action.REBOOT" />
<protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
+ <protected-broadcast android:name="android.intent.action.USER_ADDED" />
+ <protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
<protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
@@ -1032,15 +1035,6 @@
android:label="@string/permlab_clearAppCache"
android:description="@string/permdesc_clearAppCache" />
- <!-- Allows an application to read the low-level system log files.
- Log entries can contain the user's private information,
- which is why this permission is 'dangerous'. -->
- <permission android:name="android.permission.READ_LOGS"
- android:permissionGroup="android.permission-group.PERSONAL_INFO"
- android:protectionLevel="dangerous"
- android:label="@string/permlab_readLogs"
- android:description="@string/permdesc_readLogs" />
-
<!-- Allows an application to use any media decoder when decoding for playback
@hide -->
<permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
@@ -1076,6 +1070,15 @@
android:label="@string/permlab_dump"
android:description="@string/permdesc_dump" />
+ <!-- Allows an application to read the low-level system log files.
+ Log entries can contain the user's private information,
+ which is why this permission is not available to normal apps. -->
+ <permission android:name="android.permission.READ_LOGS"
+ android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+ android:protectionLevel="signature|system|development"
+ android:label="@string/permlab_readLogs"
+ android:description="@string/permdesc_readLogs" />
+
<!-- Configure an application for debugging. -->
<permission android:name="android.permission.SET_DEBUG_APP"
android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
diff --git a/data/etc/android.hardware.audio.low_latency.xml b/data/etc/android.hardware.audio.low_latency.xml
deleted file mode 100644
index 677ec1c..0000000
--- a/data/etc/android.hardware.audio.low_latency.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<!-- This is the feature indicating low-latency audio, as specified by the
- CDD. ONLY devices that meet the CDD's requirements may declare this
- feature. -->
-<permissions>
- <feature name="android.hardware.audio.low_latency" />
-</permissions>
diff --git a/data/etc/android.hardware.bluetooth.xml b/data/etc/android.hardware.bluetooth.xml
deleted file mode 100644
index 4aa1744..0000000
--- a/data/etc/android.hardware.bluetooth.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<!-- Adds the feature indicating support for the Bluetooth API -->
-<permissions>
- <feature name="android.hardware.bluetooth" />
-</permissions>
diff --git a/data/etc/android.hardware.camera.autofocus.xml b/data/etc/android.hardware.camera.autofocus.xml
deleted file mode 100644
index d6e2b90..0000000
--- a/data/etc/android.hardware.camera.autofocus.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of features for an auto-focus camera. -->
-<permissions>
- <feature name="android.hardware.camera" />
- <feature name="android.hardware.camera.autofocus" />
-</permissions>
diff --git a/data/etc/android.hardware.camera.flash-autofocus.xml b/data/etc/android.hardware.camera.flash-autofocus.xml
deleted file mode 100644
index 55f1900..0000000
--- a/data/etc/android.hardware.camera.flash-autofocus.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of features for a camera with a flash. Note
- that this currently requires having auto-focus as well. -->
-<permissions>
- <feature name="android.hardware.camera" />
- <feature name="android.hardware.camera.autofocus" />
- <feature name="android.hardware.camera.flash" />
-</permissions>
diff --git a/data/etc/android.hardware.camera.front.xml b/data/etc/android.hardware.camera.front.xml
deleted file mode 100644
index a5a6998..0000000
--- a/data/etc/android.hardware.camera.front.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<!-- This is the standard set of features for a front facing camera. -->
-<permissions>
- <feature name="android.hardware.camera.front" />
-</permissions>
diff --git a/data/etc/android.hardware.camera.xml b/data/etc/android.hardware.camera.xml
deleted file mode 100644
index 00a1ed7..0000000
--- a/data/etc/android.hardware.camera.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of features for a non auto-focus camera. -->
-<permissions>
- <feature name="android.hardware.camera" />
-</permissions>
diff --git a/data/etc/android.hardware.faketouch.multitouch.distinct.xml b/data/etc/android.hardware.faketouch.multitouch.distinct.xml
deleted file mode 100644
index b6e8d09..0000000
--- a/data/etc/android.hardware.faketouch.multitouch.distinct.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard set of features for a indirect touch input device that supports
- independently-trackable multiple-finger multitouch. -->
-<permissions>
- <feature name="android.hardware.faketouch.multitouch" />
- <feature name="android.hardware.faketouch.multitouch.distinct" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.faketouch.multitouch.jazzhand.xml b/data/etc/android.hardware.faketouch.multitouch.jazzhand.xml
deleted file mode 100644
index 7f0e70b..0000000
--- a/data/etc/android.hardware.faketouch.multitouch.jazzhand.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard set of features for an indirect touch input device that supports
- independently-trackable multiple-finger multitouch. -->
-<permissions>
- <feature name="android.hardware.faketouch.multitouch" />
- <feature name="android.hardware.faketouch.multitouch.distinct" />
- <feature name="android.hardware.faketouch.multitouch.jazzhand" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.faketouch.xml b/data/etc/android.hardware.faketouch.xml
deleted file mode 100644
index cb99097..0000000
--- a/data/etc/android.hardware.faketouch.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard set of features for a devices that does not have
- a touch screen, but does have some other indirect input device such as
- a track pad. -->
-<permissions>
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.location.gps.xml b/data/etc/android.hardware.location.gps.xml
deleted file mode 100644
index 72ab732..0000000
--- a/data/etc/android.hardware.location.gps.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- These are the location-related features for devices that include GPS. -->
-<permissions>
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
- <feature name="android.hardware.location.gps" />
-</permissions>
diff --git a/data/etc/android.hardware.location.xml b/data/etc/android.hardware.location.xml
deleted file mode 100644
index ab901cf..0000000
--- a/data/etc/android.hardware.location.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- These are the location features for devices that do not include GPS. Note
- that network location is required for all devices. -->
-<permissions>
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
-</permissions>
diff --git a/data/etc/android.hardware.nfc.xml b/data/etc/android.hardware.nfc.xml
deleted file mode 100644
index 81c4a84..0000000
--- a/data/etc/android.hardware.nfc.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<!-- This is the standard feature indicating that the device can communicate
- using Near-Field Communications (NFC). -->
-<permissions>
- <feature name="android.hardware.nfc" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.accelerometer.xml b/data/etc/android.hardware.sensor.accelerometer.xml
deleted file mode 100644
index 22f18b8..0000000
--- a/data/etc/android.hardware.sensor.accelerometer.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- Feature for devices with an accelerometer sensor. -->
-<permissions>
- <feature name="android.hardware.sensor.accelerometer" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.barometer.xml b/data/etc/android.hardware.sensor.barometer.xml
deleted file mode 100644
index ebd392d..0000000
--- a/data/etc/android.hardware.sensor.barometer.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<!-- Feature for devices with barometer. -->
-<permissions>
- <feature name="android.hardware.sensor.barometer" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.compass.xml b/data/etc/android.hardware.sensor.compass.xml
deleted file mode 100644
index 963847d..0000000
--- a/data/etc/android.hardware.sensor.compass.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- Feature for devices with a compass. -->
-<permissions>
- <feature name="android.hardware.sensor.compass" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.gyroscope.xml b/data/etc/android.hardware.sensor.gyroscope.xml
deleted file mode 100644
index fe79632..0000000
--- a/data/etc/android.hardware.sensor.gyroscope.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<!-- Feature for devices with gyroscope. -->
-<permissions>
- <feature name="android.hardware.sensor.gyroscope" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.light.xml b/data/etc/android.hardware.sensor.light.xml
deleted file mode 100644
index 78b0fec..0000000
--- a/data/etc/android.hardware.sensor.light.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- Feature for devices with an ambient light sensor. -->
-<permissions>
- <feature name="android.hardware.sensor.light" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.proximity.xml b/data/etc/android.hardware.sensor.proximity.xml
deleted file mode 100644
index d1948de..0000000
--- a/data/etc/android.hardware.sensor.proximity.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- Feature for devices with a proximity sensor. -->
-<permissions>
- <feature name="android.hardware.sensor.proximity" />
-</permissions>
diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml
deleted file mode 100644
index 082378d5..0000000
--- a/data/etc/android.hardware.telephony.cdma.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of telephony features for a CDMA phone. -->
-<permissions>
- <feature name="android.hardware.telephony" />
- <feature name="android.hardware.telephony.cdma" />
-</permissions>
diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml
deleted file mode 100644
index 7927fa8..0000000
--- a/data/etc/android.hardware.telephony.gsm.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of telephony features for a GSM phone. -->
-<permissions>
- <feature name="android.hardware.telephony" />
- <feature name="android.hardware.telephony.gsm" />
-</permissions>
diff --git a/data/etc/android.hardware.touchscreen.multitouch.distinct.xml b/data/etc/android.hardware.touchscreen.multitouch.distinct.xml
deleted file mode 100644
index 35eeefd..0000000
--- a/data/etc/android.hardware.touchscreen.multitouch.distinct.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of features for a touchscreen that supports
- independently-trackable multiple-finger multitouch. -->
-<permissions>
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.touchscreen.multitouch" />
- <feature name="android.hardware.touchscreen.multitouch.distinct" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml b/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml
deleted file mode 100644
index ed6606d..0000000
--- a/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<!-- This is the standard set of features for a touchscreen that supports
- independently-trackable multiple-finger multitouch. -->
-<permissions>
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.touchscreen.multitouch" />
- <feature name="android.hardware.touchscreen.multitouch.distinct" />
- <feature name="android.hardware.touchscreen.multitouch.jazzhand" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.touchscreen.multitouch.xml b/data/etc/android.hardware.touchscreen.multitouch.xml
deleted file mode 100644
index 1d59a27..0000000
--- a/data/etc/android.hardware.touchscreen.multitouch.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of features for a touchscreen that supports
- basic multitouch capable of gestures but not fully-indendent finger
- tracking. -->
-<permissions>
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.touchscreen.multitouch" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.touchscreen.xml b/data/etc/android.hardware.touchscreen.xml
deleted file mode 100644
index 5b5ddf9..0000000
--- a/data/etc/android.hardware.touchscreen.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of features for a touchscreen that does not
- support multitouch. -->
-<permissions>
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.usb.accessory.xml b/data/etc/android.hardware.usb.accessory.xml
deleted file mode 100644
index 80a0904..0000000
--- a/data/etc/android.hardware.usb.accessory.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard feature indicating that the device supports USB accessories. -->
-<permissions>
- <feature name="android.hardware.usb.accessory" />
- <library name="com.android.future.usb.accessory"
- file="/system/framework/com.android.future.usb.accessory.jar" />
-</permissions>
diff --git a/data/etc/android.hardware.usb.host.xml b/data/etc/android.hardware.usb.host.xml
deleted file mode 100644
index b0ca82c..0000000
--- a/data/etc/android.hardware.usb.host.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard feature indicating that the device can communicate
- with USB devices as the USB host. -->
-<permissions>
- <feature name="android.hardware.usb.host" />
-</permissions>
diff --git a/data/etc/android.hardware.wifi.direct.xml b/data/etc/android.hardware.wifi.direct.xml
deleted file mode 100644
index 78cb474..0000000
--- a/data/etc/android.hardware.wifi.direct.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard feature indicating that the device includes WiFi Direct. -->
-<permissions>
- <feature name="android.hardware.wifi.direct" />
-</permissions>
diff --git a/data/etc/android.hardware.wifi.xml b/data/etc/android.hardware.wifi.xml
deleted file mode 100644
index 512570c..0000000
--- a/data/etc/android.hardware.wifi.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard feature indicating that the device includes WiFi. -->
-<permissions>
- <feature name="android.hardware.wifi" />
-</permissions>
diff --git a/data/etc/android.software.sip.voip.xml b/data/etc/android.software.sip.voip.xml
deleted file mode 100644
index edd06c1..0000000
--- a/data/etc/android.software.sip.voip.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of features for devices that support SIP-based VoIP. -->
-<permissions>
- <feature name="android.software.sip" />
- <feature name="android.software.sip.voip" />
-</permissions>
diff --git a/data/etc/android.software.sip.xml b/data/etc/android.software.sip.xml
deleted file mode 100644
index d9fcaad..0000000
--- a/data/etc/android.software.sip.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- This is the standard set of features for devices that support the SIP API. -->
-<permissions>
- <feature name="android.software.sip" />
-</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
deleted file mode 100644
index 9d2a0cb..0000000
--- a/data/etc/handheld_core_hardware.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<!-- These are the hardware components that all handheld devices
- must include. Devices with optional hardware must also include extra
- hardware files, per the comments below.
-
- Handheld devices include phones, mobile Internet devices (MIDs),
- Personal Media Players (PMPs), small tablets (7" or less), and similar
- devices.
--->
-<permissions>
- <feature name="android.hardware.camera" />
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
- <feature name="android.hardware.sensor.compass" />
- <feature name="android.hardware.sensor.accelerometer" />
- <feature name="android.hardware.bluetooth" />
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.microphone" />
- <feature name="android.hardware.screen.portrait" />
- <feature name="android.hardware.screen.landscape" />
- <!-- devices with GPS must include android.hardware.location.gps.xml -->
- <!-- devices with an autofocus camera and/or flash must include either
- android.hardware.camera.autofocus.xml or
- android.hardware.camera.autofocus-flash.xml -->
- <!-- devices with a front facing camera must include
- android.hardware.camera.front.xml -->
- <!-- devices with WiFi must also include android.hardware.wifi.xml -->
- <!-- devices that support multitouch must include the most appropriate one
- of these files:
-
- If only partial (non-independent) pointers are supported:
- android.hardware.touchscreen.multitouch.xml
-
- If up to 4 independently tracked pointers are supported:
- include android.hardware.touchscreen.multitouch.distinct.xml
-
- If 5 or more independently tracked pointers are supported:
- include android.hardware.touchscreen.multitouch.jazzhand.xml
-
- ONLY ONE of the above should be included. -->
- <!-- devices with an ambient light sensor must also include
- android.hardware.sensor.light.xml -->
- <!-- devices with a proximity sensor must also include
- android.hardware.sensor.proximity.xml -->
- <!-- GSM phones must also include android.hardware.telephony.gsm.xml -->
- <!-- CDMA phones must also include android.hardware.telephony.cdma.xml -->
- <!-- Devices that have low-latency audio stacks suitable for apps like
- VoIP may include android.hardware.audio.low_latency.xml. ONLY apps
- that meet the requirements specified in the CDD may include this. -->
-</permissions>
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
deleted file mode 100644
index bf29fe4..0000000
--- a/data/etc/tablet_core_hardware.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<!-- These are the hardware components that all handheld devices
- must include. Devices with optional hardware must also include extra
- hardware files, per the comments below.
-
- Handheld devices include phones, mobile Internet devices (MIDs),
- Personal Media Players (PMPs), small tablets (7" or less), and similar
- devices.
--->
-<permissions>
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
- <feature name="android.hardware.sensor.compass" />
- <feature name="android.hardware.sensor.accelerometer" />
- <feature name="android.hardware.bluetooth" />
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.touchscreen.multitouch" />
- <feature name="android.hardware.touchscreen.multitouch.distinct" />
- <feature name="android.hardware.microphone" />
- <feature name="android.hardware.screen.portrait" />
- <feature name="android.hardware.screen.landscape" />
- <!-- devices with GPS must include android.hardware.location.gps.xml -->
- <!-- devices with a rear-facing camera must include one of these as appropriate:
- android.hardware.camera.xml or
- android.hardware.camera.autofocus.xml or
- android.hardware.camera.autofocus-flash.xml -->
- <!-- devices with a front facing camera must include
- android.hardware.camera.front.xml -->
- <!-- devices with WiFi must also include android.hardware.wifi.xml -->
- <!-- devices with an ambient light sensor must also include
- android.hardware.sensor.light.xml -->
- <!-- devices with a proximity sensor must also include
- android.hardware.sensor.proximity.xml -->
- <!-- devices with a barometer must also include
- android.hardware.sensor.barometer.xml -->
- <!-- devices with a gyroscope must also include
- android.hardware.sensor.gyroscope.xml -->
- <!-- GSM phones must also include android.hardware.telephony.gsm.xml -->
- <!-- CDMA phones must also include android.hardware.telephony.cdma.xml -->
-</permissions>
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index bcba3c2..4ebabd3 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -23,6 +23,7 @@
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -31,14 +32,18 @@
import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -77,6 +82,8 @@
private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
private boolean mIsWaitingForEcmExit = false;
+ private IWindowManager mIWindowManager;
+
/**
* @param context everything needs a context :(
*/
@@ -119,7 +126,7 @@
* @return A new dialog.
*/
private AlertDialog createDialog() {
- mSilentModeAction = new SilentModeAction(mAudioManager, mHandler);
+ mSilentModeAction = new SilentModeAction(mContext, mAudioManager, mHandler);
mAirplaneModeOn = new ToggleAction(
R.drawable.ic_lock_airplane_mode,
@@ -200,6 +207,7 @@
public void onPress() {
try {
ActivityManagerNative.getDefault().switchUser(user.id);
+ getWindowManager().lockNow();
} catch (RemoteException re) {
Log.e(TAG, "Couldn't switch user " + re);
}
@@ -540,10 +548,15 @@
private final AudioManager mAudioManager;
private final Handler mHandler;
+ private final boolean mHasVibrator;
+ private final Context mContext;
- SilentModeAction(AudioManager audioManager, Handler handler) {
+ SilentModeAction(Context context, AudioManager audioManager, Handler handler) {
mAudioManager = audioManager;
mHandler = handler;
+ mContext = context;
+ Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ mHasVibrator = vibrator != null && vibrator.hasVibrator();
}
private int ringerModeToIndex(int ringerMode) {
@@ -567,6 +580,9 @@
// Set up click handler
itemView.setTag(i);
itemView.setOnClickListener(this);
+ if (itemView.getId() == R.id.option2 && !mHasVibrator) {
+ itemView.setVisibility(View.GONE);
+ }
}
return v;
}
@@ -667,4 +683,12 @@
intent.putExtra("state", on);
mContext.sendBroadcast(intent);
}
+
+ private IWindowManager getWindowManager() {
+ if (mIWindowManager == null) {
+ IBinder b = ServiceManager.getService(Context.WINDOW_SERVICE);
+ mIWindowManager = IWindowManager.Stub.asInterface(b);
+ }
+ return mIWindowManager;
+ }
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index a85b605..eb024e9 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -170,6 +170,15 @@
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent.getIntExtra(Intent.EXTRA_USERID, -1));
+ }
+ }, userFilter);
}
@Override
@@ -192,19 +201,6 @@
getImplForUser().deleteAllHosts();
}
- 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;
- }
- }
-
@Override
public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
getImplForUser().bindAppWidgetId(appWidgetId, provider);
@@ -222,8 +218,15 @@
return getImplForUser().startListening(host, packageName, hostId, updatedViews);
}
- // TODO: Call this from PackageManagerService when a user is removed
- public void removeUser(int userId) {
+ public void onUserRemoved(int userId) {
+ AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
+ if (userId < 1) return;
+
+ if (impl == null) {
+ AppWidgetServiceImpl.getSettingsFile(userId).delete();
+ } else {
+ impl.onUserRemoved();
+ }
}
private AppWidgetServiceImpl getImplForUser() {
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 182a884..b24823e 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -1484,9 +1484,13 @@
}
}
+ static File getSettingsFile(int userId) {
+ return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME);
+ }
+
AtomicFile savedStateFile() {
File dir = new File("/data/system/users/" + mUserId);
- File settingsFile = new File(dir, SETTINGS_FILENAME);
+ File settingsFile = getSettingsFile(mUserId);
if (!dir.exists()) {
dir.mkdirs();
if (mUserId == 0) {
@@ -1500,6 +1504,16 @@
return new AtomicFile(settingsFile);
}
+ void onUserRemoved() {
+ // prune the ones we don't want to keep
+ int N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ cancelBroadcasts(p);
+ }
+ getSettingsFile(mUserId).delete();
+ }
+
void addProvidersForPackageLocked(String pkgName) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.setPackage(pkgName);
diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java
index 062ab74..2e2a278 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -34,6 +34,7 @@
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.UserId;
import android.util.Pair;
import android.util.Slog;
@@ -204,7 +205,7 @@
PackageInfo pi;
try {
pi = mPm.getPackageInfo(pkg, 0);
- if (pi.applicationInfo.uid != uid) {
+ if (!UserId.isSameApp(pi.applicationInfo.uid, uid)) {
throw new SecurityException("Calling uid " + uid
+ " does not own package " + pkg);
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 8ee12bc..6d83f30 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -25,9 +25,11 @@
import android.app.WallpaperInfo;
import android.app.backup.BackupManager;
import android.app.backup.WallpaperBackupHelper;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -36,6 +38,7 @@
import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Environment;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
@@ -401,41 +404,48 @@
wallpaper.wallpaperObserver.stopWatching();
}
}
-
+
public void systemReady() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(0);
switchWallpaper(wallpaper);
wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
wallpaper.wallpaperObserver.startWatching();
- ActivityManagerService ams = (ActivityManagerService) ServiceManager
- .getService(Context.ACTIVITY_SERVICE);
- ams.addUserListener(new ActivityManagerService.UserListener() {
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
@Override
- public void onUserChanged(int userId) {
- switchUser(userId);
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ switchUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ }
}
-
- @Override
- public void onUserAdded(int userId) {
- }
-
- @Override
- public void onUserRemoved(int userId) {
- }
-
- @Override
- public void onUserLoggedOut(int userId) {
- }
-
- });
+ }, userFilter);
}
String getName() {
return mWallpaperMap.get(0).name;
}
+ void removeUser(int userId) {
+ synchronized (mLock) {
+ WallpaperData wallpaper = mWallpaperMap.get(userId);
+ if (wallpaper != null) {
+ wallpaper.wallpaperObserver.stopWatching();
+ mWallpaperMap.remove(userId);
+ }
+ File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
+ wallpaperFile.delete();
+ File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
+ wallpaperInfoFile.delete();
+ }
+ }
+
void switchUser(int userId) {
synchronized (mLock) {
mCurrentUserId = userId;
@@ -861,7 +871,7 @@
}
private static JournaledFile makeJournaledFile(int userId) {
- final String base = "/data/system/users/" + userId + "/" + WALLPAPER_INFO;
+ final String base = getWallpaperDir(userId) + "/" + WALLPAPER_INFO;
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
index a7a46dd..9b4eddc 100644
--- a/services/java/com/android/server/WiredAccessoryObserver.java
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -66,7 +66,7 @@
public String getDevName() { return mDevName; }
public String getDevPath() {
- return String.format("DEVPATH=/devices/virtual/switch/%s", mDevName);
+ return String.format("/devices/virtual/switch/%s", mDevName);
}
public String getSwitchStatePath() {
@@ -158,7 +158,7 @@
init(); // set initial status
for (int i = 0; i < uEventInfo.size(); ++i) {
UEventInfo uei = uEventInfo.get(i);
- startObserving(uei.getDevPath());
+ startObserving("DEVPATH="+uei.getDevPath());
}
}
}
@@ -168,29 +168,20 @@
if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
try {
+ String devPath = event.get("DEVPATH");
String name = event.get("SWITCH_NAME");
int state = Integer.parseInt(event.get("SWITCH_STATE"));
- updateState(name, state);
+ updateState(devPath, name, state);
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
- private synchronized final void updateState(String name, int state)
+ private synchronized final void updateState(String devPath, String name, int state)
{
- // FIXME: When ueventd informs of a change in state for a switch, it does not have to be
- // the case that the name reported by /sys/class/switch/<device>/name is the same as
- // <device>. For normal users of the linux switch class driver, it will be. But it is
- // technically possible to hook the print_name method in the class driver and return a
- // different name each and every time the name sysfs entry is queried.
- //
- // Right now this is not the case for any of the switch implementations used here. I'm not
- // certain anyone would ever choose to implement such a dynamic name, or what it would mean
- // for the implementation at this level, but if it ever happens, we will need to revisit
- // this code.
for (int i = 0; i < uEventInfo.size(); ++i) {
UEventInfo uei = uEventInfo.get(i);
- if (name.equals(uei.getDevName())) {
+ if (devPath.equals(uei.getDevPath())) {
update(name, uei.computeNewHeadsetState(mHeadsetState, state));
return;
}
@@ -213,7 +204,7 @@
curState = Integer.valueOf((new String(buffer, 0, len)).trim());
if (curState > 0) {
- updateState(uei.getDevName(), curState);
+ updateState(uei.getDevPath(), uei.getDevName(), curState);
}
} catch (FileNotFoundException e) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 37a594e..78f17bc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -152,6 +152,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -3582,9 +3583,14 @@
if (doit) {
procs.add(app);
}
- } else if ((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
- || app.processName.equals(packageName)
- || app.processName.startsWith(procNamePrefix)) {
+ // If uid is specified and the uid and process name match
+ // Or, the uid is not specified and the process name matches
+ } else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
+ && (app.processName.equals(packageName)
+ || app.processName.startsWith(procNamePrefix)))
+ || (uid < 0
+ && (app.processName.equals(packageName)
+ || app.processName.startsWith(procNamePrefix)))) {
if (app.setAdj >= minOomAdj) {
if (!doit) {
return true;
@@ -3635,7 +3641,8 @@
for (i=0; i<mMainStack.mHistory.size(); i++) {
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
final boolean samePackage = r.packageName.equals(name);
- if ((samePackage || r.task == lastTask)
+ if (r.userId == userId
+ && (samePackage || r.task == lastTask)
&& (r.app == null || evenPersistent || !r.app.persistent)) {
if (!doit) {
if (r.finishing) {
@@ -3685,7 +3692,7 @@
}
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
- for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(-1).values()) {
+ for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) {
if (provider.info.packageName.equals(name)
&& (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
if (!doit) {
@@ -4118,7 +4125,16 @@
}
}
}, pkgFilter);
-
+
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ }, userFilter);
+
synchronized (this) {
// Ensure that any processes we had put on hold are now started
// up.
@@ -12469,7 +12485,7 @@
if (DEBUG_BROADCAST_LIGHT) Slog.v(
TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
- + " ordered=" + ordered);
+ + " ordered=" + ordered + " userid=" + userId);
if ((resultTo != null) && !ordered) {
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
}
@@ -14593,25 +14609,6 @@
private int mCurrentUserId;
private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
- private ArrayList<UserListener> mUserListeners = new ArrayList<UserListener>(3);
-
- public interface UserListener {
- public void onUserChanged(int userId);
-
- public void onUserAdded(int userId);
-
- public void onUserRemoved(int userId);
-
- public void onUserLoggedOut(int userId);
- }
-
- public void addUserListener(UserListener listener) {
- synchronized (this) {
- if (!mUserListeners.contains(listener)) {
- mUserListeners.add(listener);
- }
- }
- }
public boolean switchUser(int userId) {
final int callingUid = Binder.getCallingUid();
@@ -14622,8 +14619,6 @@
if (mCurrentUserId == userId)
return true;
- ArrayList<UserListener> listeners;
-
synchronized (this) {
// Check if user is already logged in, otherwise check if user exists first before
// adding to the list of logged in users.
@@ -14640,23 +14635,44 @@
startHomeActivityLocked(userId);
}
- listeners = (ArrayList<UserListener>) mUserListeners.clone();
}
- // Inform the listeners
- for (UserListener listener : listeners) {
- listener.onUserChanged(userId);
- }
+
+ // Inform of user switch
+ Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
+ addedIntent.putExtra(Intent.EXTRA_USERID, userId);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+
return true;
}
+ private void onUserRemoved(Intent intent) {
+ int extraUserId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (extraUserId < 1) return;
+
+ // Kill all the processes for the user
+ ArrayList<Pair<String, Integer>> pkgAndUids = new ArrayList<Pair<String,Integer>>();
+ synchronized (this) {
+ HashMap<String,SparseArray<ProcessRecord>> map = mProcessNames.getMap();
+ for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) {
+ SparseArray<ProcessRecord> uids = uidMap.getValue();
+ for (int i = 0; i < uids.size(); i++) {
+ if (UserId.getUserId(uids.keyAt(i)) == extraUserId) {
+ pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
+ }
+ }
+ }
+
+ for (Pair<String,Integer> pkgAndUid : pkgAndUids) {
+ forceStopPackageLocked(pkgAndUid.first, pkgAndUid.second,
+ false, false, true, true, extraUserId);
+ }
+ }
+ }
+
private boolean userExists(int userId) {
try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
- for (UserInfo user : users) {
- if (user.id == userId) {
- return true;
- }
- }
+ UserInfo user = AppGlobals.getPackageManager().getUser(userId);
+ return user != null;
} catch (RemoteException re) {
// Won't happen, in same process
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 067bf28..1593707 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -134,6 +134,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -404,7 +405,7 @@
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
- final UserManager mUserManager;
+ static UserManager sUserManager;
// Stores a list of users whose package restrictions file needs to be updated
private HashSet<Integer> mDirtyUsers = new HashSet<Integer>();
@@ -632,7 +633,7 @@
packages[i] = ent.getKey();
components[i] = ent.getValue();
PackageSetting ps = mSettings.mPackages.get(ent.getKey());
- uids[i] = (ps != null) ? ps.uid : -1;
+ uids[i] = (ps != null) ? ps.appId : -1;
i++;
}
size = i;
@@ -676,14 +677,15 @@
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
- res.pkg.applicationInfo.packageName, null);
+ res.pkg.applicationInfo.packageName, null,
+ UserId.USER_ALL);
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
@@ -820,6 +822,7 @@
}
void scheduleWritePackageRestrictionsLocked(int userId) {
+ if (!sUserManager.exists(userId)) return;
mDirtyUsers.add(userId);
if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
@@ -920,7 +923,7 @@
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- mUserManager = new UserManager(mInstaller, mUserAppDataDir);
+ sUserManager = new UserManager(mInstaller, mUserAppDataDir);
readPermissions();
@@ -1086,7 +1089,7 @@
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
mInstaller.remove(ps.name, 0);
- mUserManager.removePackageForAllUsers(ps.name);
+ sUserManager.removePackageForAllUsers(ps.name);
}
}
}
@@ -1242,7 +1245,7 @@
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
} else {
- mUserManager.removePackageForAllUsers(ps.name);
+ sUserManager.removePackageForAllUsers(ps.name);
}
if (ps.codePath != null) {
if (!ps.codePath.delete()) {
@@ -1506,29 +1509,39 @@
return cur;
}
- PackageInfo generatePackageInfo(PackageParser.Package p, int flags) {
+ PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
+ PackageInfo pi;
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
// The package has been uninstalled but has retained data and resources.
- return PackageParser.generatePackageInfo(p, null, flags, 0, 0, null);
+ pi = PackageParser.generatePackageInfo(p, null, flags, 0, 0, null, false, 0, userId);
+ } else {
+ final PackageSetting ps = (PackageSetting) p.mExtras;
+ if (ps == null) {
+ return null;
+ }
+ final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+ pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
+ ps.getStopped(userId), ps.getEnabled(userId), userId);
+ pi.applicationInfo.enabledSetting = ps.getEnabled(userId);
+ pi.applicationInfo.enabled =
+ pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT
+ || pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
}
- final PackageSetting ps = (PackageSetting)p.mExtras;
- if (ps == null) {
- return null;
- }
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- return PackageParser.generatePackageInfo(p, gp.gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions);
+ return pi;
}
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if (DEBUG_PACKAGE_INFO)
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
- return generatePackageInfo(p, flags);
+ return generatePackageInfo(p, flags, userId);
}
if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
return generatePackageInfoFromSettingsLPw(packageName, flags, userId);
@@ -1563,6 +1576,7 @@
@Override
public int getPackageUid(String packageName, int userId) {
+ if (!sUserManager.exists(userId)) return -1;
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1673,6 +1687,7 @@
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
if (ps.pkg == null) {
@@ -1682,15 +1697,18 @@
}
return null;
}
- return PackageParser.generateApplicationInfo(ps.pkg, flags);
+ return PackageParser.generateApplicationInfo(ps.pkg, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
return null;
}
private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
+ PackageParser.Package pkg = new PackageParser.Package(packageName);
if (ps.pkg == null) {
ps.pkg = new PackageParser.Package(packageName);
ps.pkg.applicationInfo.packageName = packageName;
@@ -1701,15 +1719,16 @@
getDataPathForPackage(ps.pkg.packageName, 0).getPath();
ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
}
- ps.pkg.mSetEnabled = ps.getEnabled(userId);
- ps.pkg.mSetStopped = ps.getStopped(userId);
- return generatePackageInfo(ps.pkg, flags);
+ // ps.pkg.mSetEnabled = ps.getEnabled(userId);
+ // ps.pkg.mSetStopped = ps.getStopped(userId);
+ return generatePackageInfo(ps.pkg, flags, userId);
}
return null;
}
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
// writer
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1717,8 +1736,11 @@
TAG, "getApplicationInfo " + packageName
+ ": " + p);
if (p != null) {
+ PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null) return null;
// Note: isEnabledLP() does not apply here - always return info
- return PackageParser.generateApplicationInfo(p, flags);
+ return PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
+ ps.getEnabled(userId));
}
if ("android".equals(packageName)||"system".equals(packageName)) {
return mAndroidApplication;
@@ -1782,12 +1804,16 @@
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
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, userId)) {
- return PackageParser.generateActivityInfo(a, flags, userId);
+ PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ if (ps == null) return null;
+ return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
@@ -1798,12 +1824,16 @@
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
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, userId)) {
- return PackageParser.generateActivityInfo(a, flags, userId);
+ PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ if (ps == null) return null;
+ return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
}
return null;
@@ -1811,12 +1841,16 @@
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
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, userId)) {
- return PackageParser.generateServiceInfo(s, flags, userId);
+ PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ if (ps == null) return null;
+ return PackageParser.generateServiceInfo(s, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
}
return null;
@@ -1824,12 +1858,16 @@
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
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, userId)) {
- return PackageParser.generateProviderInfo(p, flags, userId);
+ PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ if (ps == null) return null;
+ return PackageParser.generateProviderInfo(p, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
}
return null;
@@ -2253,6 +2291,7 @@
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
return chooseBestActivity(intent, resolvedType, flags, query, userId);
}
@@ -2294,6 +2333,7 @@
ResolveInfo findPreferredActivity(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, int priority, int userId) {
+ if (!sUserManager.exists(userId)) return null;
// writer
synchronized (mPackages) {
if (intent.getSelector() != null) {
@@ -2389,6 +2429,7 @@
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -2427,6 +2468,7 @@
public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
final String resultsAction = intent.getAction();
List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags
@@ -2596,6 +2638,7 @@
@Override
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -2632,6 +2675,7 @@
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId);
+ if (!sUserManager.exists(userId)) return null;
if (query != null) {
if (query.size() >= 1) {
// If there is more than one service with the same priority,
@@ -2645,6 +2689,7 @@
@Override
public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -2723,7 +2768,7 @@
} else {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
- pi = generatePackageInfo(p, flags);
+ pi = generatePackageInfo(p, flags, userId);
}
}
@@ -2743,6 +2788,7 @@
@Override
public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags,
String lastRead, int userId) {
+ if (!sUserManager.exists(userId)) return null;
final ParceledListSlice<ApplicationInfo> list = new ParceledListSlice<ApplicationInfo>();
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
final String[] keys;
@@ -2763,15 +2809,16 @@
final String packageName = keys[i++];
ApplicationInfo ai = null;
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
if (listUninstalled) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
ai = generateApplicationInfoFromSettingsLPw(ps.name, flags, userId);
}
} else {
final PackageParser.Package p = mPackages.get(packageName);
- if (p != null) {
- ai = PackageParser.generateApplicationInfo(p, flags, userId);
+ if (p != null && ps != null) {
+ ai = PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
}
@@ -2794,13 +2841,17 @@
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
- final int userId = UserId.getUserId(Binder.getCallingUid());
+ final int userId = UserId.getCallingUserId();
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, userId));
+ PackageSetting ps = mSettings.mPackages.get(p.packageName);
+ finalList.add(PackageParser.generateApplicationInfo(p, flags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId));
}
}
}
@@ -2810,14 +2861,21 @@
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
// reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProviders.get(name);
+ PackageSetting ps = provider != null
+ ? mSettings.mPackages.get(provider.owner.packageName)
+ : null;
return provider != null
&& mSettings.isEnabledLPr(provider.info, flags, userId)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
- ? PackageParser.generateProviderInfo(provider, flags, userId)
+ ? PackageParser.generateProviderInfo(provider, flags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId)
: null;
}
}
@@ -2831,16 +2889,20 @@
synchronized (mPackages) {
final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
.iterator();
- final int userId = UserId.getUserId(Binder.getCallingUid());
+ final int userId = UserId.getCallingUserId();
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
PackageParser.Provider p = entry.getValue();
+ PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
if (p.syncable
&& (!mSafeMode || (p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)) {
outNames.add(entry.getKey());
- outInfo.add(PackageParser.generateProviderInfo(p, 0, userId));
+ outInfo.add(PackageParser.generateProviderInfo(p, 0,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId));
}
}
}
@@ -2857,6 +2919,7 @@
UserId.getUserId(uid) : UserId.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
+ PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
if (p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
@@ -2867,7 +2930,10 @@
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
- finalList.add(PackageParser.generateProviderInfo(p, flags, userId));
+ finalList.add(PackageParser.generateProviderInfo(p, flags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId));
}
}
}
@@ -3511,7 +3577,7 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- pkg.applicationInfo.uid = pkgSetting.uid;
+ pkg.applicationInfo.uid = pkgSetting.appId;
pkg.mExtras = pkgSetting;
if (!verifySignaturesLP(pkgSetting, pkg)) {
@@ -3618,7 +3684,7 @@
if (ret >= 0) {
// TODO: Kill the processes first
// Remove the data directories for all users
- mUserManager.removePackageForAllUsers(pkgName);
+ sUserManager.removePackageForAllUsers(pkgName);
// Old data gone!
String msg = "System package " + pkg.packageName
+ " has changed from uid: "
@@ -3639,7 +3705,7 @@
return null;
}
// Create data directories for all users
- mUserManager.installPackageForAllUsers(pkgName,
+ sUserManager.installPackageForAllUsers(pkgName,
pkg.applicationInfo.uid);
}
if (!recovered) {
@@ -3681,7 +3747,7 @@
return null;
}
// Create data directories for all users
- mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
+ sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
if (dataPath.exists()) {
pkg.applicationInfo.dataDir = dataPath.getPath();
@@ -4510,12 +4576,14 @@
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
+ if (!sUserManager.exists(userId)) return null;
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
@@ -4523,6 +4591,7 @@
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
int flags, ArrayList<PackageParser.Activity> packageActivities, int userId) {
+ if (!sUserManager.exists(userId)) return null;
if (packageActivities == null) {
return null;
}
@@ -4605,6 +4674,7 @@
@Override
protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) {
+ if (!sUserManager.exists(userId)) return true;
PackageParser.Package p = filter.activity.owner;
if (p != null) {
PackageSetting ps = (PackageSetting)p.mExtras;
@@ -4626,6 +4696,7 @@
@Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match, int userId) {
+ if (!sUserManager.exists(userId)) return null;
if (!mSettings.isEnabledLPr(info.activity.info, mFlags, userId)) {
return null;
}
@@ -4635,7 +4706,11 @@
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags, userId);
+ PackageSetting ps = (PackageSetting) activity.owner.mExtras;
+ res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId);
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
@@ -4696,6 +4771,7 @@
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
@@ -4703,6 +4779,7 @@
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
int flags, ArrayList<PackageParser.Service> packageServices, int userId) {
+ if (!sUserManager.exists(userId)) return null;
if (packageServices == null) {
return null;
}
@@ -4780,6 +4857,7 @@
@Override
protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) {
+ if (!sUserManager.exists(userId)) return true;
PackageParser.Package p = filter.service.owner;
if (p != null) {
PackageSetting ps = (PackageSetting)p.mExtras;
@@ -4802,6 +4880,7 @@
@Override
protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter,
int match, int userId) {
+ if (!sUserManager.exists(userId)) return null;
final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter;
if (!mSettings.isEnabledLPr(info.service.info, mFlags, userId)) {
return null;
@@ -4812,7 +4891,11 @@
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags, userId);
+ PackageSetting ps = (PackageSetting) service.owner.mExtras;
+ res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId);
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
@@ -4903,23 +4986,32 @@
};
static final void sendPackageBroadcast(String action, String pkg,
- Bundle extras, String targetPkg, IIntentReceiver finishedReceiver) {
+ Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, int userId) {
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- final Intent intent = new Intent(action,
- pkg != null ? Uri.fromParts("package", pkg, null) : null);
- if (extras != null) {
- intent.putExtras(extras);
+ int[] userIds = userId == UserId.USER_ALL
+ ? sUserManager.getUserIds()
+ : new int[] {userId};
+ for (int id : userIds) {
+ final Intent intent = new Intent(action,
+ pkg != null ? Uri.fromParts("package", pkg, null) : null);
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ if (targetPkg != null) {
+ intent.setPackage(targetPkg);
+ }
+ // Modify the UID when posting to other users
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (uid > 0 && id > 0) {
+ uid = UserId.getUid(id, UserId.getAppId(uid));
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ }
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ am.broadcastIntent(null, intent, null, finishedReceiver,
+ 0, null, null, null, finishedReceiver != null, false, id);
}
- if (targetPkg != null) {
- 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,
- Binder.getOrigCallingUser());
} catch (RemoteException ex) {
}
}
@@ -5062,13 +5154,13 @@
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
}
if (addedPackage != null) {
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, addedUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
}
}
@@ -7092,11 +7184,11 @@
extras.putBoolean(Intent.EXTRA_REPLACING, true);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, packageName, null);
+ null, packageName, null, UserId.USER_ALL);
}
}
// Force a gc here.
@@ -7129,14 +7221,15 @@
}
if (removedPackage != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
if (fullRemove && !replacing) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
}
}
if (removedUid >= 0) {
- sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null);
+ sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
+ UserId.getUserId(removedUid));
}
}
}
@@ -7168,7 +7261,7 @@
// we don't consider this to be a failure of the core package deletion
} else {
// TODO: Kill the processes first
- mUserManager.removePackageForAllUsers(packageName);
+ sUserManager.removePackageForAllUsers(packageName);
}
schedulePackageCleaning(packageName);
}
@@ -7732,12 +7825,14 @@
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return;
setEnabledSetting(appPackageName, null, newState, flags, userId);
}
@Override
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return;
setEnabledSetting(componentName.getPackageName(),
componentName.getClassName(), newState, flags, userId);
}
@@ -7776,11 +7871,11 @@
+ "/" + className);
}
// Allow root and verify that userId is not being specified by a different user
- if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.uid)) {
+ if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.appId)) {
throw new SecurityException(
"Permission Denial: attempt to change component state from pid="
+ Binder.getCallingPid()
- + ", uid=" + uid + ", package uid=" + pkgSetting.uid);
+ + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
}
if (className == null) {
// We're dealing with an application/package level state change
@@ -7789,7 +7884,7 @@
return;
}
pkgSetting.setEnabled(newState, userId);
- pkgSetting.pkg.mSetEnabled = newState;
+ // pkgSetting.pkg.mSetEnabled = newState;
} else {
// We're dealing with a component level state change
// First, verify that this is a valid class name.
@@ -7825,7 +7920,7 @@
}
}
mSettings.writePackageRestrictionsLPr(userId);
- packageUid = pkgSetting.uid;
+ packageUid = UserId.getUid(userId, pkgSetting.appId);
components = mPendingBroadcasts.get(packageName);
final boolean newPackage = components == null;
if (newPackage) {
@@ -7873,10 +7968,12 @@
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
+ UserId.getUserId(packageUid));
}
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
+ if (!sUserManager.exists(userId)) return;
final int uid = Binder.getCallingUid();
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
@@ -7900,6 +7997,7 @@
@Override
public int getApplicationEnabledSetting(String packageName, int userId) {
+ if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
checkValidCaller(uid, userId);
// reader
@@ -7910,6 +8008,7 @@
@Override
public int getComponentEnabledSetting(ComponentName componentName, int userId) {
+ if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
checkValidCaller(uid, userId);
// reader
@@ -8383,7 +8482,7 @@
+ " at code path: " + ps.codePathString);
// We do have a valid package installed on sdcard
processCids.put(args, ps.codePathString);
- int uid = ps.uid;
+ int uid = ps.appId;
if (uid != -1) {
uidList[num++] = uid;
}
@@ -8436,7 +8535,7 @@
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver);
+ sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserId.USER_ALL);
}
}
@@ -8860,8 +8959,12 @@
// TODO(kroot): Add a real permission for creating users
enforceSystemOrRoot("Only the system can create users");
- // TODO(kroot): fix this API
- UserInfo userInfo = mUserManager.createUser(name, flags, new ArrayList<ApplicationInfo>());
+ UserInfo userInfo = sUserManager.createUser(name, flags);
+ if (userInfo != null) {
+ Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+ }
return userInfo;
}
@@ -8869,13 +8972,34 @@
// TODO(kroot): Add a real permission for removing users
enforceSystemOrRoot("Only the system can remove users");
- if (userId == 0) {
+ if (userId == 0 || !sUserManager.exists(userId)) {
return false;
}
- mUserManager.removeUser(userId);
+
+ cleanUpUser(userId);
+
+ if (sUserManager.removeUser(userId)) {
+ // Let other services shutdown any activity
+ Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+ addedIntent.putExtra(Intent.EXTRA_USERID, userId);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+ }
+ sUserManager.removePackageFolders(userId);
return true;
}
+ private void cleanUpUser(int userId) {
+ // Disable all the packages for the user first
+ synchronized (mPackages) {
+ Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
+ for (Entry<String, PackageSetting> entry : entries) {
+ entry.getValue().removeUser(userId);
+ }
+ if (mDirtyUsers.remove(userId));
+ mSettings.removeUserLPr(userId);
+ }
+ }
+
@Override
public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
mContext.enforceCallingOrSelfPermission(
@@ -8887,8 +9011,22 @@
}
}
+ @Override
public List<UserInfo> getUsers() {
- return mUserManager.getUsers();
+ enforceSystemOrRoot("Only the system can query users");
+ return sUserManager.getUsers();
+ }
+
+ @Override
+ public UserInfo getUser(int userId) {
+ enforceSystemOrRoot("Only the system can remove users");
+ return sUserManager.getUser(userId);
+ }
+
+ @Override
+ public void updateUserName(int userId, String name) {
+ enforceSystemOrRoot("Only the system can rename users");
+ sUserManager.updateUserName(userId, name);
}
@Override
diff --git a/services/java/com/android/server/pm/PackageSetting.java b/services/java/com/android/server/pm/PackageSetting.java
index 48ed9bf..f7f0870 100644
--- a/services/java/com/android/server/pm/PackageSetting.java
+++ b/services/java/com/android/server/pm/PackageSetting.java
@@ -24,7 +24,7 @@
* Settings data for a particular package we know about.
*/
final class PackageSetting extends PackageSettingBase {
- int uid;
+ int appId;
PackageParser.Package pkg;
SharedUserSetting sharedUser;
@@ -41,7 +41,7 @@
PackageSetting(PackageSetting orig) {
super(orig);
- uid = orig.uid;
+ appId = orig.appId;
pkg = orig.pkg;
sharedUser = orig.sharedUser;
}
@@ -50,6 +50,6 @@
public String toString() {
return "PackageSetting{"
+ Integer.toHexString(System.identityHashCode(this))
- + " " + name + "/" + uid + "}";
+ + " " + name + "/" + appId + "}";
}
}
\ No newline at end of file
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index b7cf8d6..56f2166 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -273,4 +273,13 @@
return COMPONENT_ENABLED_STATE_DEFAULT;
}
}
+
+ void removeUser(int userId) {
+ enabled.delete(userId);
+ stopped.delete(userId);
+ enabledComponents.delete(userId);
+ disabledComponents.delete(userId);
+ notLaunched.delete(userId);
+ }
+
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index b541c8c..bb7f4fc 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -275,7 +275,7 @@
p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
- p.nativeLibraryPathString, p.uid, p.versionCode, p.pkgFlags);
+ p.nativeLibraryPathString, p.appId, p.versionCode, p.pkgFlags);
mDisabledSysPackages.remove(name);
return ret;
}
@@ -284,7 +284,7 @@
String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
PackageSetting p = mPackages.get(name);
if (p != null) {
- if (p.uid == uid) {
+ if (p.appId == uid) {
return p;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -293,7 +293,7 @@
}
p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
vc, pkgFlags);
- p.uid = uid;
+ p.appId = uid;
if (addUserIdLPw(uid, p, name)) {
mPackages.put(name, p);
return p;
@@ -407,7 +407,7 @@
p.copyFrom(origPackage);
p.signatures = s;
p.sharedUser = origPackage.sharedUser;
- p.uid = origPackage.uid;
+ p.appId = origPackage.appId;
p.origPackage = origPackage;
mRenamedPackages.put(name, origPackage.name);
name = origPackage.name;
@@ -435,7 +435,7 @@
}
}
if (sharedUser != null) {
- p.uid = sharedUser.userId;
+ p.appId = sharedUser.userId;
} else {
// Clone the setting here for disabled system packages
PackageSetting dis = mDisabledSysPackages.get(name);
@@ -447,7 +447,7 @@
if (dis.signatures.mSignatures != null) {
p.signatures.mSignatures = dis.signatures.mSignatures.clone();
}
- p.uid = dis.uid;
+ p.appId = dis.appId;
// Clone permissions
p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
// Clone component info
@@ -464,14 +464,14 @@
}
}
// Add new setting to list of user ids
- addUserIdLPw(p.uid, p, name);
+ addUserIdLPw(p.appId, p, name);
} else {
// Assign new user id
- p.uid = newUserIdLPw(p);
+ p.appId = newUserIdLPw(p);
}
}
}
- if (p.uid < 0) {
+ if (p.appId < 0) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + name + " could not be assigned a valid uid");
return null;
@@ -539,9 +539,9 @@
+ p.sharedUser + " but is now " + sharedUser
+ "; I am not changing its files so it will probably fail!");
p.sharedUser.packages.remove(p);
- } else if (p.uid != sharedUser.userId) {
+ } else if (p.appId != sharedUser.userId) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Package " + p.name + " was user id " + p.uid
+ "Package " + p.name + " was user id " + p.appId
+ " but is now user " + sharedUser
+ " with id " + sharedUser.userId
+ "; I am not changing its files so it will probably fail!");
@@ -549,7 +549,7 @@
sharedUser.packages.add(p);
p.sharedUser = sharedUser;
- p.uid = sharedUser.userId;
+ p.appId = sharedUser.userId;
}
}
@@ -614,8 +614,8 @@
return p.sharedUser.userId;
}
} else {
- removeUserIdLPw(p.uid);
- return p.uid;
+ removeUserIdLPw(p.appId);
+ return p.appId;
}
}
return -1;
@@ -628,7 +628,7 @@
p.sharedUser.packages.remove(p);
p.sharedUser.packages.add(newp);
} else {
- replaceUserIdLPw(p.uid, newp);
+ replaceUserIdLPw(p.appId, newp);
}
}
mPackages.put(name, newp);
@@ -1317,9 +1317,9 @@
serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
}
if (pkg.sharedUser == null) {
- serializer.attribute(null, "userId", Integer.toString(pkg.uid));
+ serializer.attribute(null, "userId", Integer.toString(pkg.appId));
} else {
- serializer.attribute(null, "sharedUserId", Integer.toString(pkg.uid));
+ serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
}
serializer.startTag(null, "perms");
if (pkg.sharedUser == null) {
@@ -1364,9 +1364,9 @@
serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
if (pkg.sharedUser == null) {
- serializer.attribute(null, "userId", Integer.toString(pkg.uid));
+ serializer.attribute(null, "userId", Integer.toString(pkg.appId));
} else {
- serializer.attribute(null, "sharedUserId", Integer.toString(pkg.uid));
+ serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
}
if (pkg.uidError) {
serializer.attribute(null, "uidError", "true");
@@ -1607,7 +1607,7 @@
final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
while (disabledIt.hasNext()) {
final PackageSetting disabledPs = disabledIt.next();
- final Object id = getUserIdLPr(disabledPs.uid);
+ final Object id = getUserIdLPr(disabledPs.appId);
if (id != null && id instanceof SharedUserSetting) {
disabledPs.sharedUser = (SharedUserSetting) id;
}
@@ -1753,10 +1753,10 @@
}
}
String idStr = parser.getAttributeValue(null, "userId");
- ps.uid = idStr != null ? Integer.parseInt(idStr) : 0;
- if (ps.uid <= 0) {
+ ps.appId = idStr != null ? Integer.parseInt(idStr) : 0;
+ if (ps.appId <= 0) {
String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
- ps.uid = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
+ ps.appId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
}
int outerDepth = parser.getDepth();
int type;
@@ -2164,6 +2164,13 @@
}
}
+ void removeUserLPr(int userId) {
+ File file = getUserPackagesStateFile(userId);
+ file.delete();
+ file = getUserPackagesStateBackupFile(userId);
+ file.delete();
+ }
+
// Returns -1 if we could not find an available UserId to assign
private int newUserIdLPw(Object obj) {
// Let's be stupidly inefficient for now...
@@ -2265,11 +2272,11 @@
if (pkgSetting == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- if (!allowedByPermission && (appId != pkgSetting.uid)) {
+ if (!allowedByPermission && (appId != pkgSetting.appId)) {
throw new SecurityException(
"Permission Denial: attempt to change stopped state from pid="
+ Binder.getCallingPid()
- + ", uid=" + uid + ", package uid=" + pkgSetting.uid);
+ + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
}
if (DEBUG_STOPPED) {
if (stopped) {
@@ -2285,7 +2292,7 @@
if (pkgSetting.installerPackageName != null) {
PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
pkgSetting.name, null,
- pkgSetting.installerPackageName, null);
+ pkgSetting.installerPackageName, null, userId);
}
pkgSetting.setNotLaunched(false, userId);
}
@@ -2369,7 +2376,7 @@
pw.println(ps.name);
}
- pw.print(" userId="); pw.print(ps.uid);
+ pw.print(" userId="); pw.print(ps.appId);
pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
pw.print(" sharedUser="); pw.println(ps.sharedUser);
pw.print(" pkg="); pw.println(ps.pkg);
@@ -2513,7 +2520,7 @@
pw.println(ps.name);
}
pw.print(" userId=");
- pw.println(ps.uid);
+ pw.println(ps.appId);
pw.print(" sharedUser=");
pw.println(ps.sharedUser);
pw.print(" codePath=");
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 959e570..4e9e666 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import android.content.pm.ApplicationInfo;
@@ -58,7 +59,7 @@
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
- private SparseArray<UserInfo> mUsers;
+ private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
private final File mUsersDir;
private final File mUserListFile;
@@ -91,11 +92,36 @@
}
public List<UserInfo> getUsers() {
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
- for (int i = 0; i < mUsers.size(); i++) {
- users.add(mUsers.valueAt(i));
+ synchronized (mUsers) {
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ for (int i = 0; i < mUsers.size(); i++) {
+ users.add(mUsers.valueAt(i));
+ }
+ return users;
}
- return users;
+ }
+
+ public UserInfo getUser(int userId) {
+ synchronized (mUsers) {
+ UserInfo info = mUsers.get(userId);
+ return info;
+ }
+ }
+
+ public boolean exists(int userId) {
+ synchronized (mUsers) {
+ return ArrayUtils.contains(mUserIds, userId);
+ }
+ }
+
+ public void updateUserName(int userId, String name) {
+ synchronized (mUsers) {
+ UserInfo info = mUsers.get(userId);
+ if (name != null && !name.equals(info.name)) {
+ info.name = name;
+ writeUserLocked(info);
+ }
+ }
}
/**
@@ -108,9 +134,14 @@
}
private void readUserList() {
- mUsers = new SparseArray<UserInfo>();
+ synchronized (mUsers) {
+ readUserListLocked();
+ }
+ }
+
+ private void readUserListLocked() {
if (!mUserListFile.exists()) {
- fallbackToSingleUser();
+ fallbackToSingleUserLocked();
return;
}
FileInputStream fis = null;
@@ -126,7 +157,7 @@
if (type != XmlPullParser.START_TAG) {
Slog.e(LOG_TAG, "Unable to read user list");
- fallbackToSingleUser();
+ fallbackToSingleUserLocked();
return;
}
@@ -139,11 +170,11 @@
}
}
}
- updateUserIds();
+ updateUserIdsLocked();
} catch (IOException ioe) {
- fallbackToSingleUser();
+ fallbackToSingleUserLocked();
} catch (XmlPullParserException pe) {
- fallbackToSingleUser();
+ fallbackToSingleUserLocked();
} finally {
if (fis != null) {
try {
@@ -154,15 +185,15 @@
}
}
- private void fallbackToSingleUser() {
+ private void fallbackToSingleUserLocked() {
// Create the primary user
UserInfo primary = new UserInfo(0, "Primary",
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
mUsers.put(0, primary);
- updateUserIds();
+ updateUserIdsLocked();
- writeUserList();
- writeUser(primary);
+ writeUserListLocked();
+ writeUserLocked(primary);
}
/*
@@ -172,7 +203,7 @@
* <name>Primary</name>
* </user>
*/
- private void writeUser(UserInfo userInfo) {
+ private void writeUserLocked(UserInfo userInfo) {
FileOutputStream fos = null;
try {
final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
@@ -216,7 +247,7 @@
* <user id="2"></user>
* </users>
*/
- private void writeUserList() {
+ private void writeUserListLocked() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mUserListFile);
@@ -309,17 +340,19 @@
return null;
}
- public UserInfo createUser(String name, int flags, List<ApplicationInfo> apps) {
+ public UserInfo createUser(String name, int flags) {
int userId = getNextAvailableId();
UserInfo userInfo = new UserInfo(userId, name, flags);
File userPath = new File(mBaseUserPath, Integer.toString(userId));
- if (!createPackageFolders(userId, userPath, apps)) {
+ if (!createPackageFolders(userId, userPath)) {
return null;
}
- mUsers.put(userId, userInfo);
- writeUserList();
- writeUser(userInfo);
- updateUserIds();
+ synchronized (mUsers) {
+ mUsers.put(userId, userInfo);
+ writeUserListLocked();
+ writeUserLocked(userInfo);
+ updateUserIdsLocked();
+ }
return userInfo;
}
@@ -328,7 +361,13 @@
* after the user's processes have been terminated.
* @param id the user's id
*/
- public void removeUser(int id) {
+ public boolean removeUser(int id) {
+ synchronized (mUsers) {
+ return removeUserLocked(id);
+ }
+ }
+
+ private boolean removeUserLocked(int id) {
// Remove from the list
UserInfo userInfo = mUsers.get(id);
if (userInfo != null) {
@@ -338,11 +377,11 @@
File userFile = new File(mUsersDir, id + ".xml");
userFile.delete();
// Update the user list
- writeUserList();
- // Remove the data directories for all packages for this user
- removePackageFolders(id);
- updateUserIds();
+ writeUserListLocked();
+ updateUserIdsLocked();
+ return true;
}
+ return false;
}
public void installPackageForAllUsers(String packageName, int uid) {
@@ -376,7 +415,7 @@
/**
* Caches the list of user ids in an array, adjusting the array size when necessary.
*/
- private void updateUserIds() {
+ private void updateUserIdsLocked() {
if (mUserIds == null || mUserIds.length != mUsers.size()) {
mUserIds = new int[mUsers.size()];
}
@@ -402,11 +441,10 @@
return i;
}
- private boolean createPackageFolders(int id, File userPath, final List<ApplicationInfo> apps) {
+ private boolean createPackageFolders(int id, File userPath) {
// mInstaller may not be available for unit-tests.
- if (mInstaller == null || apps == null) return true;
+ if (mInstaller == null) return true;
- final long startTime = SystemClock.elapsedRealtime();
// Create the user path
userPath.mkdir();
FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
@@ -414,13 +452,10 @@
mInstaller.cloneUserData(0, id, false);
- final long stopTime = SystemClock.elapsedRealtime();
- Log.i(LOG_TAG,
- "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
return true;
}
- private boolean removePackageFolders(int id) {
+ boolean removePackageFolders(int id) {
// mInstaller may not be available for unit-tests.
if (mInstaller == null) return true;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index e8188e7..d736ac1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -54,7 +54,7 @@
public void testAddUser() throws Exception {
final UserManager details = mUserManager;
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
assertTrue(userInfo != null);
List<UserInfo> list = details.getUsers();
@@ -73,8 +73,8 @@
public void testAdd2Users() throws Exception {
final UserManager details = mUserManager;
- UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
- UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN, null);
+ UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN);
assertTrue(user1 != null);
assertTrue(user2 != null);
@@ -87,7 +87,7 @@
public void testRemoveUser() throws Exception {
final UserManager details = mUserManager;
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
details.removeUser(userInfo.id);
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 351c771..5610134 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -45,6 +45,7 @@
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.RemoteException;
import java.util.List;
@@ -530,6 +531,14 @@
* @hide
*/
@Override
+ public UserInfo getUser(int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public boolean removeUser(int id) {
throw new UnsupportedOperationException();
}
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
index d761214..b7874f7 100644
--- a/voip/java/android/net/rtp/AudioStream.java
+++ b/voip/java/android/net/rtp/AudioStream.java
@@ -158,7 +158,7 @@
if (type < 96 || type > 127) {
throw new IllegalArgumentException("Invalid type");
}
- if (type == mCodec.type) {
+ if (mCodec != null && type == mCodec.type) {
throw new IllegalArgumentException("The type is used by codec");
}
}