Keep track of whether an app is installed for each user.
This add a new per-user state for an app, indicating whether
it is installed for that user.
All system apps are always installed for all users (we still
use disable to "uninstall" them).
Now when you call into the package manager to install an app,
it will only install the app for that user unless you supply
a flag saying to install for all users. Only being installed
for the user is just the normal install state, but all other
users have marked in their state for that app that it is not
installed.
When you call the package manager APIs for information about
apps, uninstalled apps are treated as really being not visible
(somewhat more-so than disabled apps), unless you use the
GET_UNINSTALLED_PACKAGES flag.
If another user calls to install an app that is already installed,
just not for them, then the normal install process takes place
but in addition that user's installed state is toggled on.
The package manager will not send PACKAGE_ADDED, PACKAGE_REMOVED,
PACKAGE_REPLACED etc broadcasts to users who don't have a package
installed or not being involved in a change in the install state.
There are a few things that are not quite right with this -- for
example if you go through a full install (with a new apk) of an
app for one user who doesn't have it already installed, you will
still get the PACKAGED_REPLACED messages even though this is
technically the first install for your user. I'm not sure how
much of an issue this is.
When you call the existing API to uninstall an app, this toggles
the installed state of the app for that user to be off. Only if
that is the last user user that has the app uinstalled will it
actually be removed from the device. Again there is a new flag
you can pass in to force the app to be uninstalled for all users.
Also fixed issues with cleaning external storage of apps, which
was not dealing with multiple users. We now keep track of cleaning
each user for each package.
Change-Id: I00e66452b149defc08c5e0183fa673f532465ed5
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 3197a63..adc9434 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -697,7 +697,8 @@
IApplicationThread app = ApplicationThreadNative.asInterface(b);
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
- ComponentName cn = startService(app, service, resolvedType);
+ int userId = data.readInt();
+ ComponentName cn = startService(app, service, resolvedType, userId);
reply.writeNoException();
ComponentName.writeToParcel(cn, reply);
return true;
@@ -709,7 +710,8 @@
IApplicationThread app = ApplicationThreadNative.asInterface(b);
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
- int res = stopService(app, service, resolvedType);
+ int userId = data.readInt();
+ int res = stopService(app, service, resolvedType, userId);
reply.writeNoException();
reply.writeInt(res);
return true;
@@ -2523,7 +2525,7 @@
}
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType) throws RemoteException
+ String resolvedType, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2531,6 +2533,7 @@
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
+ data.writeInt(userId);
mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
ComponentName res = ComponentName.readFromParcel(reply);
@@ -2539,7 +2542,7 @@
return res;
}
public int stopService(IApplicationThread caller, Intent service,
- String resolvedType) throws RemoteException
+ String resolvedType, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2547,6 +2550,7 @@
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
+ data.writeInt(userId);
mRemote.transact(STOP_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
int res = reply.readInt();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f3f75ce..0f10c4f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -45,6 +45,7 @@
import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -997,6 +998,21 @@
}
@Override
+ public int installExistingPackage(String packageName)
+ throws NameNotFoundException {
+ try {
+ int res = mPM.installExistingPackage(packageName);
+ if (res == INSTALL_FAILED_INVALID_URI) {
+ throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+ }
+ return res;
+ } catch (RemoteException e) {
+ // Should never happen!
+ throw new NameNotFoundException("Package " + packageName + " doesn't exist");
+ }
+ }
+
+ @Override
public void verifyPendingInstall(int id, int response) {
try {
mPM.verifyPendingInstall(id, response);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 0ae4d06..32086d7 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1220,11 +1220,21 @@
@Override
public ComponentName startService(Intent service) {
+ return startServiceAsUser(service, Process.myUserHandle());
+ }
+
+ @Override
+ public boolean stopService(Intent service) {
+ return stopServiceAsUser(service, Process.myUserHandle());
+ }
+
+ @Override
+ public ComponentName startServiceAsUser(Intent service, UserHandle user) {
try {
service.setAllowFds(false);
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service,
- service.resolveTypeIfNeeded(getContentResolver()));
+ service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
if (cn != null && cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
@@ -1237,12 +1247,12 @@
}
@Override
- public boolean stopService(Intent service) {
+ public boolean stopServiceAsUser(Intent service, UserHandle user) {
try {
service.setAllowFds(false);
int res = ActivityManagerNative.getDefault().stopService(
mMainThread.getApplicationThread(), service,
- service.resolveTypeIfNeeded(getContentResolver()));
+ service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to stop service " + service);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index a6d1995..c3e911e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -133,9 +133,9 @@
public PendingIntent getRunningServiceControlPanel(ComponentName service)
throws RemoteException;
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType) throws RemoteException;
+ String resolvedType, int userId) throws RemoteException;
public int stopService(IApplicationThread caller, Intent service,
- String resolvedType) throws RemoteException;
+ String resolvedType, int userId) throws RemoteException;
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) throws RemoteException;
public void setServiceForeground(ComponentName className, IBinder token,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1460bf5..dc6d93f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1414,6 +1414,16 @@
public abstract boolean stopService(Intent service);
/**
+ * @hide like {@link #startService(Intent)} but for a specific user.
+ */
+ public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
+
+ /**
+ * @hide like {@link #stopService(Intent)} but for a specific user.
+ */
+ public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
+
+ /**
* Connect to an application service, creating it if needed. This defines
* a dependency between your application and the service. The given
* <var>conn</var> will receive the service object when it is created and be
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 3a13725..4bbe44e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -410,6 +410,18 @@
return mBase.stopService(name);
}
+ /** @hide */
+ @Override
+ public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+ return mBase.startServiceAsUser(service, user);
+ }
+
+ /** @hide */
+ @Override
+ public boolean stopServiceAsUser(Intent name, UserHandle user) {
+ return mBase.stopServiceAsUser(name, user);
+ }
+
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index cbabc7c..1a82d58 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -302,6 +302,12 @@
public static final int FLAG_SUPPORTS_RTL = 1<<22;
/**
+ * Value for {@link #flags}: true if the application is currently
+ * installed for the calling user.
+ */
+ public static final int FLAG_INSTALLED = 1<<23;
+
+ /**
* Value for {@link #flags}: Set to true if the application has been
* installed using the forward lock option.
*
@@ -334,7 +340,8 @@
* {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
* {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_SUPPORTS_XLARGE_SCREENS},
* {@link #FLAG_RESIZEABLE_FOR_SCREENS},
- * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE}
+ * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}, {@link #FLAG_VM_SAFE_MODE},
+ * {@link #FLAG_INSTALLED}.
*/
public int flags = 0;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0be8b83..0e1fe3e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -32,6 +32,7 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ManifestDigest;
+import android.content.pm.PackageCleanItem;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.PermissionGroupInfo;
@@ -351,7 +352,7 @@
*/
void updateExternalMediaStatus(boolean mounted, boolean reportStatus);
- String nextPackageToClean(String lastPackage);
+ PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage);
void movePackage(String packageName, IPackageMoveObserver observer, int flags);
@@ -369,6 +370,8 @@
in VerificationParams verificationParams,
in ContainerEncryptionParams encryptionParams);
+ int installExistingPackage(String packageName);
+
void verifyPendingInstall(int id, int verificationCode);
void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java
index f5e72e0..75505bc 100644
--- a/core/java/android/content/pm/ManifestDigest.java
+++ b/core/java/android/content/pm/ManifestDigest.java
@@ -1,3 +1,19 @@
+/*
+ * 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 android.content.pm;
import android.os.Parcel;
diff --git a/core/java/android/content/pm/PackageCleanItem.aidl b/core/java/android/content/pm/PackageCleanItem.aidl
new file mode 100644
index 0000000..9bb203e
--- /dev/null
+++ b/core/java/android/content/pm/PackageCleanItem.aidl
@@ -0,0 +1,18 @@
+/* Copyright 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 android.content.pm;
+
+parcelable PackageCleanItem;
diff --git a/core/java/android/content/pm/PackageCleanItem.java b/core/java/android/content/pm/PackageCleanItem.java
new file mode 100644
index 0000000..eea3b9c
--- /dev/null
+++ b/core/java/android/content/pm/PackageCleanItem.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class PackageCleanItem {
+ public final String packageName;
+ public final boolean andCode;
+
+ public PackageCleanItem(String packageName, boolean andCode) {
+ this.packageName = packageName;
+ this.andCode = andCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ try {
+ if (obj != null) {
+ PackageCleanItem other = (PackageCleanItem)obj;
+ return packageName.equals(other.packageName) && andCode == other.andCode;
+ }
+ } catch (ClassCastException e) {
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + packageName.hashCode();
+ result = 31 * result + (andCode ? 1 : 0);
+ return result;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(packageName);
+ dest.writeInt(andCode ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<PackageCleanItem> CREATOR
+ = new Parcelable.Creator<PackageCleanItem>() {
+ public PackageCleanItem createFromParcel(Parcel source) {
+ return new PackageCleanItem(source);
+ }
+
+ public PackageCleanItem[] newArray(int size) {
+ return new PackageCleanItem[size];
+ }
+ };
+
+ private PackageCleanItem(Parcel source) {
+ packageName = source.readString();
+ andCode = source.readInt() != 0;
+ }
+}
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 9625944..a1566da 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -32,6 +32,11 @@
public String packageName;
/**
+ * The android:versionCode of the package.
+ */
+ public int versionCode;
+
+ /**
* Specifies the recommended install location. Can be one of
* {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage
* {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media
@@ -58,6 +63,7 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
+ dest.writeInt(versionCode);
dest.writeInt(recommendedInstallLocation);
dest.writeInt(installLocation);
@@ -82,6 +88,7 @@
private PackageInfoLite(Parcel source) {
packageName = source.readString();
+ versionCode = source.readInt();
recommendedInstallLocation = source.readInt();
installLocation = source.readInt();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b3e98e7..0d99d3f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -310,6 +310,23 @@
public static final int INSTALL_FROM_ADB = 0x00000020;
/**
+ * Flag parameter for {@link #installPackage} to indicate that this install
+ * should immediately be visible to all users.
+ *
+ * @hide
+ */
+ public static final int INSTALL_ALL_USERS = 0x00000040;
+
+ /**
+ * Flag parameter for {@link #installPackage} to indicate that it is okay
+ * to install an update to an app where the newly installed app has a lower
+ * version code than the currently installed app.
+ *
+ * @hide
+ */
+ public static final int INSTALL_ALLOW_DOWNGRADE = 0x00000080;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
@@ -529,6 +546,14 @@
public static final int INSTALL_FAILED_UID_CHANGED = -24;
/**
+ * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package has an older version code than the currently installed package.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25;
+
+ /**
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser was given a path that is not a file, or does not end with the expected
@@ -625,7 +650,15 @@
*
* @hide
*/
- public static final int DONT_DELETE_DATA = 0x00000001;
+ public static final int DELETE_KEEP_DATA = 0x00000001;
+
+ /**
+ * Flag parameter for {@link #deletePackage} to indicate that you want the
+ * package deleted for all users.
+ *
+ * @hide
+ */
+ public static final int DELETE_ALL_USERS = 0x00000002;
/**
* Return code for when package deletion succeeds. This is passed to the
@@ -2175,8 +2208,8 @@
if ((flags & GET_SIGNATURES) != 0) {
packageParser.collectCertificates(pkg, 0);
}
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, false,
- COMPONENT_ENABLED_STATE_DEFAULT);
+ PackageUserState state = new PackageUserState();
+ return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
}
/**
@@ -2267,6 +2300,14 @@
ContainerEncryptionParams encryptionParams);
/**
+ * If there is already an application with the given package name installed
+ * on the system for other users, also install it for the calling user.
+ * @hide
+ */
+ public abstract int installExistingPackage(String packageName)
+ throws NameNotFoundException;
+
+ /**
* Allows a package listening to the
* {@link Intent#ACTION_PACKAGE_NEEDS_VERIFICATION package verification
* broadcast} to respond to the package manager. The response must include
@@ -2337,7 +2378,8 @@
* @param observer An observer callback to get notified when the package deletion is
* complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
* called when that happens. observer may be null to indicate that no callback is desired.
- * @param flags - possible values: {@link #DONT_DELETE_DATA}
+ * @param flags - possible values: {@link #DELETE_KEEP_DATA},
+ * {@link #DELETE_ALL_USERS}.
*
* @hide
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ac75040..237f5c5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -203,11 +203,14 @@
*/
public static class PackageLite {
public final String packageName;
+ public final int versionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
- public PackageLite(String packageName, int installLocation, List<VerifierInfo> verifiers) {
+ public PackageLite(String packageName, int versionCode,
+ int installLocation, List<VerifierInfo> verifiers) {
this.packageName = packageName;
+ this.versionCode = versionCode;
this.installLocation = installLocation;
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
}
@@ -243,14 +246,15 @@
return name.endsWith(".apk");
}
+ /*
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
HashSet<String> grantedPermissions) {
-
+ PackageUserState state = new PackageUserState();
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
- UserHandle.getCallingUserId());
+ grantedPermissions, state, UserHandle.getCallingUserId());
}
+ */
/**
* Generate and return the {@link PackageInfo} for a parsed package.
@@ -260,23 +264,30 @@
*/
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
+ HashSet<String> grantedPermissions, PackageUserState state) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, stopped, enabledState, UserHandle.getCallingUserId());
+ grantedPermissions, state, UserHandle.getCallingUserId());
+ }
+
+ private static boolean checkUseInstalled(int flags, PackageUserState state) {
+ return state.installed || ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0);
}
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions, boolean stopped, int enabledState, int userId) {
+ HashSet<String> grantedPermissions, PackageUserState state, int userId) {
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
pi.versionCode = p.mVersionCode;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
- pi.applicationInfo = generateApplicationInfo(p, flags, stopped, enabledState, userId);
+ pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
pi.installLocation = p.installLocation;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
@@ -312,7 +323,7 @@
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
- stopped, enabledState, userId);
+ state, userId);
}
}
}
@@ -334,7 +345,7 @@
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
- stopped, enabledState, userId);
+ state, userId);
}
}
}
@@ -355,8 +366,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, stopped,
- enabledState, userId);
+ pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
+ state, userId);
}
}
}
@@ -377,8 +388,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, stopped,
- enabledState, userId);
+ pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
+ state, userId);
}
}
}
@@ -840,11 +851,19 @@
return null;
}
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
+ int versionCode = 0;
+ int numFound = 0;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
PARSE_DEFAULT_INSTALL_LOCATION);
+ numFound++;
+ } else if (attr.equals("versionCode")) {
+ versionCode = attrs.getAttributeIntValue(i, 0);
+ numFound++;
+ }
+ if (numFound >= 2) {
break;
}
}
@@ -867,7 +886,7 @@
}
}
- return new PackageLite(pkgName.intern(), installLocation, verifiers);
+ return new PackageLite(pkgName.intern(), versionCode, installLocation, verifiers);
}
/**
@@ -3458,13 +3477,25 @@
}
}
- 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;
+ private static boolean copyNeeded(int flags, Package p,
+ PackageUserState state, Bundle metaData, int userId) {
+ if (userId != 0) {
+ // We always need to copy for other users, since we need
+ // to fix up the uid.
+ return true;
+ }
+ if (state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ boolean enabled = state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
if (p.applicationInfo.enabled != enabled) {
return true;
}
}
+ if (!state.installed) {
+ return true;
+ }
+ if (state.stopped) {
+ return true;
+ }
if ((flags & PackageManager.GET_META_DATA) != 0
&& (metaData != null || p.mAppMetaData != null)) {
return true;
@@ -3476,32 +3507,34 @@
return false;
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
- int enabledState) {
- return generateApplicationInfo(p, flags, stopped, enabledState, UserHandle.getCallingUserId());
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+ PackageUserState state) {
+ return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId());
}
public static ApplicationInfo generateApplicationInfo(Package p, int flags,
- boolean stopped, int enabledState, int userId) {
+ PackageUserState state, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p, enabledState, null) && userId == 0) {
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
+ if (!copyNeeded(flags, p, state, null, userId)) {
// CompatibilityMode is global state. It's safe to modify the instance
// of the package.
if (!sCompatibilityModeEnabled) {
p.applicationInfo.disableCompatibilityMode();
}
- if (stopped) {
- p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
- } else {
- p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
- }
- if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ // Make sure we report as installed. Also safe to do, since the
+ // default state should be installed (we will always copy if we
+ // need to report it is not installed).
+ p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
p.applicationInfo.enabled = true;
- } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
p.applicationInfo.enabled = false;
}
- p.applicationInfo.enabledSetting = enabledState;
+ p.applicationInfo.enabledSetting = state.enabled;
return p.applicationInfo;
}
@@ -3520,18 +3553,23 @@
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
- if (stopped) {
+ if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (state.installed) {
+ ai.flags |= ApplicationInfo.FLAG_INSTALLED;
+ } else {
+ ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+ }
+ if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
- } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
ai.enabled = false;
}
- ai.enabledSetting = enabledState;
+ ai.enabledSetting = state.enabled;
return ai;
}
@@ -3578,16 +3616,19 @@
}
}
- public static final ActivityInfo generateActivityInfo(Activity a, int flags, boolean stopped,
- int enabledState, int userId) {
+ public static final ActivityInfo generateActivityInfo(Activity a, int flags,
+ PackageUserState state, int userId) {
if (a == null) return null;
- if (!copyNeeded(flags, a.owner, enabledState, a.metaData) && userId == 0) {
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
+ if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
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, stopped, enabledState, userId);
+ ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);
return ai;
}
@@ -3612,17 +3653,19 @@
}
}
- public static final ServiceInfo generateServiceInfo(Service s, int flags, boolean stopped,
- int enabledState, int userId) {
+ public static final ServiceInfo generateServiceInfo(Service s, int flags,
+ PackageUserState state, int userId) {
if (s == null) return null;
- if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
- && userId == UserHandle.getUserId(s.info.applicationInfo.uid)) {
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
+ if (!copyNeeded(flags, s.owner, state, s.metaData, userId)) {
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, stopped, enabledState, userId);
+ si.applicationInfo = generateApplicationInfo(s.owner, flags, state, userId);
return si;
}
@@ -3655,13 +3698,15 @@
}
}
- public static final ProviderInfo generateProviderInfo(Provider p, int flags, boolean stopped,
- int enabledState, int userId) {
+ public static final ProviderInfo generateProviderInfo(Provider p, int flags,
+ PackageUserState state, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p.owner, enabledState, p.metaData)
+ if (!checkUseInstalled(flags, state)) {
+ return null;
+ }
+ if (!copyNeeded(flags, p.owner, state, p.metaData, userId)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
- || p.info.uriPermissionPatterns == null)
- && userId == 0) {
+ || p.info.uriPermissionPatterns == null)) {
return p.info;
}
// Make shallow copies so we can store the metadata safely
@@ -3670,7 +3715,7 @@
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags, stopped, enabledState, userId);
+ pi.applicationInfo = generateApplicationInfo(p.owner, flags, state, userId);
return pi;
}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
new file mode 100644
index 0000000..1a71bfb
--- /dev/null
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -0,0 +1,50 @@
+/*
+ * 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 android.content.pm;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+
+import java.util.HashSet;
+
+/**
+ * Per-user state information about a package.
+ */
+public class PackageUserState {
+ public boolean stopped;
+ public boolean notLaunched;
+ public boolean installed;
+ public int enabled;
+
+ public HashSet<String> disabledComponents;
+ public HashSet<String> enabledComponents;
+
+ public PackageUserState() {
+ installed = true;
+ enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+
+ public PackageUserState(PackageUserState o) {
+ installed = o.installed;
+ stopped = o.stopped;
+ notLaunched = o.notLaunched;
+ enabled = o.enabled;
+ disabledComponents = o.disabledComponents != null
+ ? new HashSet<String>(o.disabledComponents) : null;
+ enabledComponents = o.enabledComponents != null
+ ? new HashSet<String>(o.enabledComponents) : null;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 0843d85..d33bd80 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -28,6 +28,9 @@
/** @hide A user id to indicate all users on the device */
public static final int USER_ALL = -1;
+ /** @hide A user handle to indicate all users on the device */
+ public static final UserHandle ALL = new UserHandle(USER_ALL);
+
/** @hide A user id to indicate the currently active user */
public static final int USER_CURRENT = -2;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 246b0c9..c5e7d9d 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -49,6 +49,7 @@
public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4;
public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
+ public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;
private static final boolean localLOGV = true;
private static final String TAG = "PackageHelper";
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 6e1b9d6..a1fd14e 100755
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -864,7 +864,7 @@
public void deleteFromRawResource(int iFlags, int dFlags) {
InstallParams ip = sampleInstallFromRawResource(iFlags, false);
- boolean retainData = ((dFlags & PackageManager.DONT_DELETE_DATA) != 0);
+ boolean retainData = ((dFlags & PackageManager.DELETE_KEEP_DATA) != 0);
GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
DeleteObserver observer = new DeleteObserver();
try {
@@ -914,12 +914,12 @@
@LargeTest
public void testDeleteNormalInternalRetainData() {
- deleteFromRawResource(0, PackageManager.DONT_DELETE_DATA);
+ deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
}
@LargeTest
public void testDeleteFwdLockedInternalRetainData() {
- deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DONT_DELETE_DATA);
+ deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DELETE_KEEP_DATA);
}
@LargeTest
@@ -929,7 +929,7 @@
return;
}
- deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DONT_DELETE_DATA);
+ deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
}
/* sdcard mount/unmount tests ******/
@@ -1656,7 +1656,7 @@
false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
// Delete the package now retaining data.
GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
- invokeDeletePackage(ip.pkg.packageName, PackageManager.DONT_DELETE_DATA, receiver);
+ invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
assertTrue(invokeMovePackageFail(ip.pkg.packageName, moveFlags, result));
} catch (Exception e) {
failStr(e);
@@ -2532,7 +2532,7 @@
GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
try {
- invokeDeletePackage(ip.pkg.packageName, PackageManager.DONT_DELETE_DATA, receiver);
+ invokeDeletePackage(ip.pkg.packageName, PackageManager.DELETE_KEEP_DATA, receiver);
} catch (Exception e) {
failStr(e);
}