summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/PackageInfo.java2
-rwxr-xr-xcore/java/android/content/pm/PackageInfoLite.aidl20
-rw-r--r--core/java/android/content/pm/PackageInfoLite.java63
-rw-r--r--core/java/android/content/pm/PackageParser.java77
-rwxr-xr-xcore/java/com/android/internal/app/IMediaContainerService.aidl3
-rw-r--r--core/java/com/android/internal/content/PackageHelper.java2
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java153
-rw-r--r--services/java/com/android/server/PackageManagerService.java69
-rwxr-xr-xtests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java77
9 files changed, 366 insertions, 100 deletions
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index c003355..0964425 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -151,7 +151,7 @@ public class PackageInfo implements Parcelable {
*/
public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2;
/**
- * The launch mode style requested by the activity. From the
+ * The install location requested by the activity. From the
* {@link android.R.attr#installLocation} attribute, one of
* {@link #INSTALL_LOCATION_AUTO},
* {@link #INSTALL_LOCATION_INTERNAL_ONLY},
diff --git a/core/java/android/content/pm/PackageInfoLite.aidl b/core/java/android/content/pm/PackageInfoLite.aidl
new file mode 100755
index 0000000..2c80942
--- /dev/null
+++ b/core/java/android/content/pm/PackageInfoLite.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/WindowManager.aidl
+**
+** Copyright 2007, 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 PackageInfoLite;
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
new file mode 100644
index 0000000..2f38ece
--- /dev/null
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -0,0 +1,63 @@
+package android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Basic information about a package as specified in its manifest.
+ * Utility class used in PackageManager methods
+ * @hide
+ */
+public class PackageInfoLite implements Parcelable {
+ /**
+ * The name of this package. From the <manifest> tag's "name"
+ * attribute.
+ */
+ public String packageName;
+
+ /**
+ * 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
+ * {@link PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors
+ * {@link PackageHelper.RECOMMEND_FAILED_INVALID_APK} for parse errors.
+ */
+ public int recommendedInstallLocation;
+ public int installLocation;
+
+ public PackageInfoLite() {
+ }
+
+ public String toString() {
+ return "PackageInfoLite{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + packageName + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(packageName);
+ dest.writeInt(recommendedInstallLocation);
+ dest.writeInt(installLocation);
+ }
+
+ public static final Parcelable.Creator<PackageInfoLite> CREATOR
+ = new Parcelable.Creator<PackageInfoLite>() {
+ public PackageInfoLite createFromParcel(Parcel source) {
+ return new PackageInfoLite(source);
+ }
+
+ public PackageInfoLite[] newArray(int size) {
+ return new PackageInfoLite[size];
+ }
+ };
+
+ private PackageInfoLite(Parcel source) {
+ packageName = source.readString();
+ recommendedInstallLocation = source.readInt();
+ installLocation = source.readInt();
+ }
+} \ No newline at end of file
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 7a0337cd..5da7fd1 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -136,7 +136,20 @@ public class PackageParser {
enabledRes = _enabledRes;
}
}
-
+
+ /* Light weight package info.
+ * @hide
+ */
+ public static class PackageLite {
+ public String packageName;
+ public int installLocation;
+ public String mScanPath;
+ public PackageLite(String packageName, int installLocation) {
+ this.packageName = packageName;
+ this.installLocation = installLocation;
+ }
+ }
+
private ParsePackageItemArgs mParseInstrumentationArgs;
private ParseComponentArgs mParseActivityArgs;
private ParseComponentArgs mParseActivityAliasArgs;
@@ -562,7 +575,14 @@ public class PackageParser {
return true;
}
- public static String parsePackageName(String packageFilePath, int flags) {
+ /*
+ * Utility method that retrieves just the package name and install
+ * location from the apk location at the given file path.
+ * @param packageFilePath file location of the apk
+ * @param flags Special parse flags
+ * @return PackageLite object with package information.
+ */
+ public static PackageLite parsePackageLite(String packageFilePath, int flags) {
XmlResourceParser parser = null;
AssetManager assmgr = null;
try {
@@ -577,9 +597,9 @@ public class PackageParser {
}
AttributeSet attrs = parser;
String errors[] = new String[1];
- String packageName = null;
+ PackageLite packageLite = null;
try {
- packageName = parsePackageName(parser, attrs, flags, errors);
+ packageLite = parsePackageLite(parser, attrs, flags, errors);
} catch (IOException e) {
Log.w(TAG, packageFilePath, e);
} catch (XmlPullParserException e) {
@@ -588,11 +608,11 @@ public class PackageParser {
if (parser != null) parser.close();
if (assmgr != null) assmgr.close();
}
- if (packageName == null) {
- Log.e(TAG, "parsePackageName error: " + errors[0]);
+ if (packageLite == null) {
+ Log.e(TAG, "parsePackageLite error: " + errors[0]);
return null;
}
- return packageName;
+ return packageLite;
}
private static String validateName(String name, boolean requiresSeparator) {
@@ -656,6 +676,49 @@ public class PackageParser {
return pkgName.intern();
}
+ private static PackageLite parsePackageLite(XmlPullParser parser,
+ AttributeSet attrs, int flags, String[] outError)
+ throws IOException, XmlPullParserException {
+
+ int type;
+ while ((type=parser.next()) != parser.START_TAG
+ && type != parser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != parser.START_TAG) {
+ outError[0] = "No start tag found";
+ return null;
+ }
+ if ((flags&PARSE_CHATTY) != 0 && Config.LOGV) Log.v(
+ TAG, "Root element name: '" + parser.getName() + "'");
+ if (!parser.getName().equals("manifest")) {
+ outError[0] = "No <manifest> tag";
+ return null;
+ }
+ String pkgName = attrs.getAttributeValue(null, "package");
+ if (pkgName == null || pkgName.length() == 0) {
+ outError[0] = "<manifest> does not specify package";
+ return null;
+ }
+ String nameError = validateName(pkgName, true);
+ if (nameError != null && !"android".equals(pkgName)) {
+ outError[0] = "<manifest> specifies bad package name \""
+ + pkgName + "\": " + nameError;
+ return null;
+ }
+ int installLocation = PackageInfo.INSTALL_LOCATION_AUTO;
+ for (int i = 0; i < attrs.getAttributeCount(); i++) {
+ String attr = attrs.getAttributeName(i);
+ if (attr.equals("installLocation")) {
+ installLocation = attrs.getAttributeIntValue(i,
+ PackageInfo.INSTALL_LOCATION_AUTO);
+ break;
+ }
+ }
+ return new PackageLite(pkgName.intern(), installLocation);
+ }
+
/**
* Temporary.
*/
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index c0e9587..fd1fd58 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -18,6 +18,7 @@ package com.android.internal.app;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import android.content.pm.PackageInfoLite;
interface IMediaContainerService {
String copyResourceToContainer(in Uri packageURI,
@@ -25,5 +26,5 @@ interface IMediaContainerService {
String key, String resFileName);
boolean copyResource(in Uri packageURI,
in ParcelFileDescriptor outStream);
- int getRecommendedInstallLocation(in Uri fileUri);
+ PackageInfoLite getMinimalPackageInfo(in Uri fileUri);
} \ No newline at end of file
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 04a10b9..de6a175 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -36,6 +36,8 @@ public class PackageHelper {
public static final int RECOMMEND_INSTALL_EXTERNAL = 2;
public static final int RECOMMEND_FAILED_INSUFFICIENT_STORAGE = -1;
public static final int RECOMMEND_FAILED_INVALID_APK = -2;
+ public static final int RECOMMEND_FAILED_INVALID_LOCATION = -3;
+ public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4;
private static final boolean localLOGV = true;
private static final String TAG = "PackageHelper";
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 02e1f07..4635f48 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -5,6 +5,7 @@ import com.android.internal.content.PackageHelper;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Package;
@@ -28,6 +29,7 @@ import java.io.IOException;
import java.io.InputStream;
import android.os.FileUtils;
+import android.os.storage.IMountService;
import android.provider.Settings;
/*
@@ -86,46 +88,51 @@ public class DefaultContainerService extends IntentService {
* specified by file uri location.
* @param fileUri the uri of resource to be copied. Should be a
* file uri
- * @return Returns
- * PackageHelper.RECOMMEND_INSTALL_INTERNAL to install on internal storage
- * PackageHelper.RECOMMEND_INSTALL_EXTERNAL to install on external media
- * PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE for storage errors
- * PackageHelper.RECOMMEND_FAILED_INVALID_APK for parse errors.
+ * @return Returns PackageInfoLite object containing
+ * the package info and recommended app location.
*/
- public int getRecommendedInstallLocation(final Uri fileUri) {
+ public PackageInfoLite getMinimalPackageInfo(final Uri fileUri) {
+ PackageInfoLite ret = new PackageInfoLite();
if (fileUri == null) {
Log.i(TAG, "Invalid package uri " + fileUri);
- return PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ return ret;
}
String scheme = fileUri.getScheme();
if (scheme != null && !scheme.equals("file")) {
Log.w(TAG, "Falling back to installing on internal storage only");
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ return ret;
}
String archiveFilePath = fileUri.getPath();
PackageParser packageParser = new PackageParser(archiveFilePath);
File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- PackageParser.Package pkg = packageParser.parsePackage(sourceFile,
- archiveFilePath, metrics, 0);
+ PackageParser.PackageLite pkg = packageParser.parsePackageLite(
+ archiveFilePath, 0);
+ ret.packageName = pkg.packageName;
+ ret.installLocation = pkg.installLocation;
// Nuke the parser reference right away and force a gc
Runtime.getRuntime().gc();
packageParser = null;
if (pkg == null) {
Log.w(TAG, "Failed to parse package");
- return PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
+ return ret;
}
- int loc = recommendAppInstallLocation(pkg);
+ ret.packageName = pkg.packageName;
+ int loc = recommendAppInstallLocation(pkg.installLocation, archiveFilePath);
if (loc == PackageManager.INSTALL_EXTERNAL) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
} else if (loc == ERR_LOC) {
Log.i(TAG, "Failed to install insufficient storage");
- return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
} else {
// Implies install on internal storage.
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
+ return ret;
}
};
@@ -171,62 +178,37 @@ public class DefaultContainerService extends IntentService {
String codePath = packageURI.getPath();
File codeFile = new File(codePath);
String newCachePath = null;
- final int CREATE_FAILED = 1;
- final int COPY_FAILED = 2;
- final int FINALIZE_FAILED = 3;
- final int PASS = 4;
- int errCode = CREATE_FAILED;
// Create new container
if ((newCachePath = PackageHelper.createSdDir(codeFile,
- newCid, key, Process.myUid())) != null) {
- if (localLOGV) Log.i(TAG, "Created container for " + newCid
- + " at path : " + newCachePath);
- File resFile = new File(newCachePath, resFileName);
- errCode = COPY_FAILED;
- // Copy file from codePath
- if (FileUtils.copyFile(new File(codePath), resFile)) {
- if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile);
- errCode = FINALIZE_FAILED;
- if (PackageHelper.finalizeSdDir(newCid)) {
- if (localLOGV) Log.i(TAG, "Finalized container " + newCid);
- errCode = PASS;
- }
- }
- }
- // Print error based on errCode
- String errMsg = "";
- switch (errCode) {
- case CREATE_FAILED:
- errMsg = "CREATE_FAILED";
- break;
- case COPY_FAILED:
- errMsg = "COPY_FAILED";
- if (localLOGV) Log.i(TAG, "Destroying " + newCid +
- " at path " + newCachePath + " after " + errMsg);
- PackageHelper.destroySdDir(newCid);
- break;
- case FINALIZE_FAILED:
- errMsg = "FINALIZE_FAILED";
- if (localLOGV) Log.i(TAG, "Destroying " + newCid +
- " at path " + newCachePath + " after " + errMsg);
- PackageHelper.destroySdDir(newCid);
- break;
- default:
- errMsg = "PASS";
- if (PackageHelper.isContainerMounted(newCid)) {
- if (localLOGV) Log.i(TAG, "Unmounting " + newCid +
- " at path " + newCachePath + " after " + errMsg);
- // Force a gc to avoid being killed.
- Runtime.getRuntime().gc();
- PackageHelper.unMountSdDir(newCid);
- } else {
- if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted");
- }
- break;
+ newCid, key, Process.myUid())) == null) {
+ Log.e(TAG, "Failed to create container " + newCid);
+ return null;
}
- if (errCode != PASS) {
+ if (localLOGV) Log.i(TAG, "Created container for " + newCid
+ + " at path : " + newCachePath);
+ File resFile = new File(newCachePath, resFileName);
+ if (!FileUtils.copyFile(new File(codePath), resFile)) {
+ Log.e(TAG, "Failed to copy " + codePath + " to " + resFile);
+ // Clean up container
+ PackageHelper.destroySdDir(newCid);
return null;
}
+ if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile);
+ if (!PackageHelper.finalizeSdDir(newCid)) {
+ Log.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath);
+ // Clean up container
+ PackageHelper.destroySdDir(newCid);
+ }
+ if (localLOGV) Log.i(TAG, "Finalized container " + newCid);
+ if (PackageHelper.isContainerMounted(newCid)) {
+ if (localLOGV) Log.i(TAG, "Unmounting " + newCid +
+ " at path " + newCachePath);
+ // Force a gc to avoid being killed.
+ Runtime.getRuntime().gc();
+ PackageHelper.unMountSdDir(newCid);
+ } else {
+ if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted");
+ }
return newCachePath;
}
@@ -307,29 +289,28 @@ public class DefaultContainerService extends IntentService {
private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
private static final int ERR_LOC = -1;
- public int recommendAppInstallLocation(Package pkg) {
+ private int recommendAppInstallLocation(int installLocation,
+ String archiveFilePath) {
// Initial implementation:
// Package size = code size + cache size + data size
// If code size > 1 MB, install on SD card.
// Else install on internal NAND flash, unless space on NAND is less than 10%
-
- if (pkg == null) {
- return ERR_LOC;
+ String status = Environment.getExternalStorageState();
+ long availSDSize = -1;
+ if (status.equals(Environment.MEDIA_MOUNTED)) {
+ StatFs sdStats = new StatFs(
+ Environment.getExternalStorageDirectory().getPath());
+ availSDSize = (long)sdStats.getAvailableBlocks() *
+ (long)sdStats.getBlockSize();
}
+ StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath());
+ long totalInternalSize = (long)internalStats.getBlockCount() *
+ (long)internalStats.getBlockSize();
+ long availInternalSize = (long)internalStats.getAvailableBlocks() *
+ (long)internalStats.getBlockSize();
- StatFs internalFlashStats = new StatFs(Environment.getDataDirectory().getPath());
- StatFs sdcardStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
-
- long totalInternalFlashSize = (long)internalFlashStats.getBlockCount() *
- (long)internalFlashStats.getBlockSize();
- long availInternalFlashSize = (long)internalFlashStats.getAvailableBlocks() *
- (long)internalFlashStats.getBlockSize();
- long availSDSize = (long)sdcardStats.getAvailableBlocks() *
- (long)sdcardStats.getBlockSize();
-
- double pctNandFree = (double)availInternalFlashSize / (double)totalInternalFlashSize;
+ double pctNandFree = (double)availInternalSize / (double)totalInternalSize;
- final String archiveFilePath = pkg.mScanPath;
File apkFile = new File(archiveFilePath);
long pkgLen = apkFile.length();
@@ -339,15 +320,15 @@ public class DefaultContainerService extends IntentService {
// For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now.
long reqInternalSize = 0;
boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
- boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize);
+ boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize);
boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk &&
- (reqInternalSize < availInternalFlashSize);
+ (reqInternalSize < availInternalSize);
boolean fitsOnInt = intThresholdOk && intAvailOk;
// Consider application flags preferences as well...
- boolean installOnlyOnSd = (pkg.installLocation ==
+ boolean installOnlyOnSd = (installLocation ==
PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
- boolean installOnlyInternal = (pkg.installLocation ==
+ boolean installOnlyInternal = (installLocation ==
PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
if (installOnlyInternal) {
// If set explicitly in manifest,
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 1ff0244..371fa37 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -51,6 +51,7 @@ import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -78,12 +79,14 @@ import android.os.Environment;
import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.StatFs;
import android.os.storage.StorageResultCode;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.security.SystemKeyStore;
import android.util.*;
import android.view.Display;
@@ -4568,21 +4571,79 @@ class PackageManagerService extends IPackageManager.Stub {
this.installerPackageName = installerPackageName;
}
+ private int installLocationPolicy(PackageInfoLite pkgLite, int flags) {
+ String packageName = pkgLite.packageName;
+ int installLocation = pkgLite.installLocation;
+ boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg != null) {
+ if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ // Check for updated system application.
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (onSd) {
+ Log.w(TAG, "Cannot install update to system app on sdcard");
+ return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
+ }
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ } else {
+ // When replacing apps make sure we honour
+ // the existing app location if not overwritten by other options
+ boolean prevOnSd = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0;
+ if (onSd) {
+ // Install flag overrides everything.
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ // If current upgrade does not specify install location.
+ if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ // Application explicitly specified internal.
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ // App explictly prefers external. Let policy decide
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+ // Prefer previous location
+ return prevOnSd ? PackageHelper.RECOMMEND_INSTALL_EXTERNAL:
+ PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
+ }
+ } else {
+ // Invalid install. Return error code
+ return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
+ }
+ }
+ }
+ // All the special cases have been taken care of.
+ // Return result based on recommended install location.
+ if (onSd) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ return pkgLite.recommendedInstallLocation;
+ }
+
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
+ boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
+ boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;
// Dont need to invoke getInstallLocation for forward locked apps.
- if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
- flags &= ~PackageManager.INSTALL_EXTERNAL;
+ if (fwdLocked && onSd) {
+ Log.w(TAG, "Cannot install fwd locked apps on sdcard");
+ ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
// Remote call to find out default install location
- int loc = mContainerService.getRecommendedInstallLocation(packageURI);
+ PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI);
+ int loc = installLocationPolicy(pkgLite, flags);
// Use install location to create InstallArgs and temporary
// install location
- if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){
+ if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION){
+ ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS){
+ ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){
ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
ret = PackageManager.INSTALL_FAILED_INVALID_APK;
} else {
+ // Override install location with flags
if ((flags & PackageManager.INSTALL_EXTERNAL) == 0){
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 50eca02..0b69020 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -932,11 +932,86 @@ public class PackageManagerTests extends AndroidTestCase {
0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
}
+ public void testManifestInstallLocationFwdLockedFlagSdcard() {
+ installFromRawResource("install.apk", R.raw.install_loc_unspecified,
+ PackageManager.INSTALL_FORWARD_LOCK |
+ PackageManager.INSTALL_EXTERNAL, true, true,
+ PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ PackageInfo.INSTALL_LOCATION_AUTO);
+ }
+
public void testManifestInstallLocationFwdLockedSdcard() {
installFromRawResource("install.apk", R.raw.install_loc_sdcard,
PackageManager.INSTALL_FORWARD_LOCK, true, false,
-1,
- PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+ PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ }
+
+ private void replaceManifestLocation(int iFlags, int rFlags) {
+ InstallParams ip = sampleInstallFromRawResource(iFlags, false);
+ GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
+ int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
+ try {
+ assertEquals(invokeInstallPackage(ip.packageURI, replaceFlags,
+ ip.pkg.packageName, receiver), true);
+ assertInstall(ip.pkg, replaceFlags, ip.pkg.installLocation);
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ } finally {
+ cleanUpInstall(ip);
+ }
+ }
+
+ public void testReplaceFlagInternalSdcard() {
+ replaceManifestLocation(0, PackageManager.INSTALL_EXTERNAL);
+ }
+
+ public void testReplaceFlagSdcardInternal() {
+ replaceManifestLocation(PackageManager.INSTALL_EXTERNAL, 0);
+ }
+
+ public void testManifestInstallLocationReplaceInternalSdcard() {
+ int iFlags = 0;
+ int iApk = R.raw.install_loc_unspecified;
+ int rFlags = 0;
+ int rApk = R.raw.install_loc_sdcard;
+ InstallParams ip = installFromRawResource("install.apk", iApk,
+ iFlags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_AUTO);
+ GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
+ int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
+ try {
+ InstallParams rp = installFromRawResource("install.apk", rApk,
+ rFlags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ assertInstall(rp.pkg, replaceFlags, rp.pkg.installLocation);
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ } finally {
+ cleanUpInstall(ip);
+ }
+ }
+
+ public void testManifestInstallLocationReplaceSdcardInternal() {
+ int iFlags = 0;
+ int iApk = R.raw.install_loc_sdcard;
+ int rFlags = 0;
+ int rApk = R.raw.install_loc_unspecified;
+ InstallParams ip = installFromRawResource("install.apk", iApk,
+ iFlags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
+ int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
+ try {
+ InstallParams rp = installFromRawResource("install.apk", rApk,
+ rFlags, false,
+ false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+ assertInstall(rp.pkg, replaceFlags, ip.pkg.installLocation);
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ } finally {
+ cleanUpInstall(ip);
+ }
}
public void xxxtestClearAllSecureContainers() {