summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuchi Amalapurapu <asuchitra@google.com>2010-02-19 14:27:29 -0800
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-02-19 14:27:29 -0800
commit6c81defa3d1111c36f8b0c9c4e84e8b2c342620f (patch)
tree135f77c35a105adfb00782f991112a61a8c57b34
parent8afbc53578a55cb39f97f959a0b444bf24f317ee (diff)
parent8946dd3355fc1dcbad872c0546e356474d4cc5de (diff)
downloadframeworks_base-6c81defa3d1111c36f8b0c9c4e84e8b2c342620f.zip
frameworks_base-6c81defa3d1111c36f8b0c9c4e84e8b2c342620f.tar.gz
frameworks_base-6c81defa3d1111c36f8b0c9c4e84e8b2c342620f.tar.bz2
Merge "Move package from internal to external and vice versa."
-rw-r--r--Android.mk1
-rw-r--r--api/current.xml11
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java1
-rw-r--r--core/java/android/app/ContextImpl.java10
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/IPackageMoveObserver.aidl27
-rw-r--r--core/java/android/content/pm/PackageManager.java82
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/values/strings.xml5
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java8
-rw-r--r--services/java/com/android/server/PackageManagerService.java505
-rw-r--r--test-runner/android/test/mock/MockPackageManager.java9
-rw-r--r--tests/AndroidTests/AndroidManifest.xml1
-rw-r--r--tests/AndroidTests/res/raw/install_loc_autobin30462 -> 329182 bytes
-rw-r--r--tests/AndroidTests/res/raw/install_loc_internalbin30458 -> 329191 bytes
-rw-r--r--tests/AndroidTests/res/raw/install_loc_sdcardbin30466 -> 329181 bytes
-rw-r--r--tests/AndroidTests/res/raw/install_loc_unspecifiedbin30442 -> 329175 bytes
-rwxr-xr-xtests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java216
18 files changed, 764 insertions, 121 deletions
diff --git a/Android.mk b/Android.mk
index a48ef45..1700965 100644
--- a/Android.mk
+++ b/Android.mk
@@ -111,6 +111,7 @@ LOCAL_SRC_FILES += \
core/java/android/content/pm/IPackageDeleteObserver.aidl \
core/java/android/content/pm/IPackageInstallObserver.aidl \
core/java/android/content/pm/IPackageManager.aidl \
+ core/java/android/content/pm/IPackageMoveObserver.aidl \
core/java/android/content/pm/IPackageStatsObserver.aidl \
core/java/android/database/IContentObserver.aidl \
core/java/android/hardware/ISensorService.aidl \
diff --git a/api/current.xml b/api/current.xml
index 40739da..86043df 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -716,6 +716,17 @@
visibility="public"
>
</field>
+<field name="MOVE_PACKAGE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.MOVE_PACKAGE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="PERSISTENT_ACTIVITY"
type="java.lang.String"
transient="false"
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index ff16c6e..fc5707d 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -600,6 +600,7 @@ public final class Pm {
} else if (opt.equals("-t")) {
installFlags |= PackageManager.INSTALL_ALLOW_TEST;
} else if (opt.equals("-s")) {
+ // Override if -s option is specified.
installFlags |= PackageManager.INSTALL_EXTERNAL;
} else {
System.err.println("Error: Unknown option: " + opt);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index db6a4bf..1d004ee 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -42,6 +42,7 @@ import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
@@ -2489,6 +2490,15 @@ class ContextImpl extends Context {
}
@Override
+ public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ try {
+ mPM.movePackage(packageName, observer, flags);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
public String getInstallerPackageName(String packageName) {
try {
return mPM.getInstallerPackageName(packageName);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 47789a5..7a02a98 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -26,6 +26,7 @@ import android.content.pm.FeatureInfo;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
@@ -309,4 +310,6 @@ interface IPackageManager {
void updateExternalMediaStatus(boolean mounted);
String nextPackageToClean(String lastPackage);
+
+ void movePackage(String packageName, IPackageMoveObserver observer, int flags);
}
diff --git a/core/java/android/content/pm/IPackageMoveObserver.aidl b/core/java/android/content/pm/IPackageMoveObserver.aidl
new file mode 100644
index 0000000..baa1595
--- /dev/null
+++ b/core/java/android/content/pm/IPackageMoveObserver.aidl
@@ -0,0 +1,27 @@
+/*
+**
+** 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;
+
+/**
+ * Callback for moving package resources from the Package Manager.
+ * @hide
+ */
+oneway interface IPackageMoveObserver {
+ void packageMoved(in String packageName, int returnCode);
+}
+
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8576de2..2edb430 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -551,6 +551,69 @@ public abstract class PackageManager {
public static final int DONT_DELETE_DATA = 0x00000001;
/**
+ * Return code that is passed to the {@link IPackageMoveObserver} by
+ * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+ * when the package has been successfully moved by the system.
+ * @hide
+ */
+ public static final int MOVE_SUCCEEDED = 1;
+ /**
+ * Error code that is passed to the {@link IPackageMoveObserver} by
+ * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+ * when the package hasn't been successfully moved by the system
+ * because of insufficient memory on specified media.
+ * @hide
+ */
+ public static final int MOVE_FAILED_INSUFFICIENT_STORAGE = -1;
+
+ /**
+ * Error code that is passed to the {@link IPackageMoveObserver} by
+ * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+ * if the specified package doesn't exist.
+ * @hide
+ */
+ public static final int MOVE_FAILED_DOESNT_EXIST = -2;
+
+ /**
+ * Error code that is passed to the {@link IPackageMoveObserver} by
+ * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+ * if the specified package cannot be moved since its a system package.
+ * @hide
+ */
+ public static final int MOVE_FAILED_SYSTEM_PACKAGE = -3;
+
+ /**
+ * Error code that is passed to the {@link IPackageMoveObserver} by
+ * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+ * if the specified package cannot be moved since its forward locked.
+ * @hide
+ */
+ public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
+
+ /**
+ * Error code that is passed to the {@link IPackageMoveObserver} by
+ * {@link #movePackage(android.net.Uri, IPackageMoveObserver)}
+ * if the specified package cannot be moved to the specified location.
+ * @hide
+ */
+ public static final int MOVE_FAILED_INVALID_LOCATION = -5;
+
+ /**
+ * Flag parameter for {@link #movePackage} to indicate that
+ * the package should be moved to internal storage if its
+ * been installed on external media.
+ * @hide
+ */
+ public static final int MOVE_INTERNAL = 0x00000001;
+
+ /**
+ * Flag parameter for {@link #movePackage} to indicate that
+ * the package should be moved to external media.
+ * @hide
+ */
+ public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a camera facing away
* from the screen.
@@ -1922,4 +1985,23 @@ public abstract class PackageManager {
* Return whether the device has been booted into safe mode.
*/
public abstract boolean isSafeMode();
+
+ /**
+ * Attempts to move package resources from internal to external media or vice versa.
+ * Since this may take a little while, the result will
+ * be posted back to the given observer. This call may fail if the calling context
+ * lacks the {@link android.Manifest.permission#MOVE_PACKAGE} permission, if the
+ * named package cannot be found, or if the named package is a "system package".
+ *
+ * @param packageName The name of the package to delete
+ * @param observer An observer callback to get notified when the package move is
+ * complete. {@link android.content.pm.IPackageMoveObserver#packageMoved(boolean)} will be
+ * called when that happens. observer may be null to indicate that no callback is desired.
+ * @param flags To indicate install location {@link #MOVE_INTERNAL} or
+ * {@link #MOVE_EXTERNAL_MEDIA}
+ *
+ * @hide
+ */
+ public abstract void movePackage(
+ String packageName, IPackageMoveObserver observer, int flags);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 333db05..63584ed 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1041,6 +1041,12 @@
android:description="@string/permdesc_deletePackages"
android:protectionLevel="signatureOrSystem" />
+ <!-- Allows an application to move location of installed package. -->
+ <permission android:name="android.permission.MOVE_PACKAGE"
+ android:label="@string/permlab_movePackage"
+ android:description="@string/permdesc_movePackage"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- Allows an application to change whether an application component (other than its own) is
enabled or not. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cdf38b9..98b8863 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -680,6 +680,11 @@
restricted usually to system process.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_movePackage">Move application resources</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_movePackage">Allows an application to move application resources from internal to external media and vice versa.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readLogs">read system log files</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_readLogs">Allows an application to read from the
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 933a7e5..95ab684 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -102,7 +102,11 @@ public class DefaultContainerService extends IntentService {
File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+ PackageParser.Package pkg = packageParser.parsePackage(sourceFile,
+ archiveFilePath, metrics, 0);
+ // 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;
@@ -115,7 +119,7 @@ public class DefaultContainerService extends IntentService {
return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
} else {
// Implies install on internal storage.
- return 0;
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
}
}
};
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 4326efc..252f2a6 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -44,6 +44,7 @@ import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
+import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
@@ -142,6 +143,9 @@ class PackageManagerService extends IPackageManager.Stub {
FileObserver.CLOSE_WRITE /*| FileObserver.CREATE*/ | FileObserver.MOVED_TO;
private static final int OBSERVER_EVENTS = REMOVE_EVENTS | ADD_EVENTS;
+ // Suffix used during package installation when copying/moving
+ // package apks to install directory.
+ private static final String INSTALL_PACKAGE_SUFFIX = "-";
static final int SCAN_MONITOR = 1<<0;
static final int SCAN_NO_DEX = 1<<1;
@@ -321,8 +325,8 @@ class PackageManagerService extends IPackageManager.Stub {
};
class PackageHandler extends Handler {
- final ArrayList<InstallParams> mPendingInstalls =
- new ArrayList<InstallParams>();
+ final ArrayList<HandlerParams> mPendingInstalls =
+ new ArrayList<HandlerParams>();
// Service Connection to remote media container service to copy
// package uri's from external media onto secure containers
// or internal storage.
@@ -334,20 +338,19 @@ class PackageManagerService extends IPackageManager.Stub {
public void handleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
- InstallParams params = (InstallParams) msg.obj;
+ HandlerParams params = (HandlerParams) msg.obj;
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
if (mContainerService != null) {
// No need to add to pending list. Use remote stub directly
- handleStartCopy(params);
+ params.handleStartCopy(mContainerService);
} else {
if (mContext.bindService(service, mDefContainerConn,
Context.BIND_AUTO_CREATE)) {
mPendingInstalls.add(params);
} else {
Log.e(TAG, "Failed to bind to media container service");
- // Indicate install failure TODO add new error code
- processPendingInstall(createInstallArgs(params),
- PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+ // Indicate service bind error
+ params.handleServiceError();
}
}
break;
@@ -358,9 +361,9 @@ class PackageManagerService extends IPackageManager.Stub {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mPendingInstalls.size() > 0) {
- InstallParams params = mPendingInstalls.remove(0);
+ HandlerParams params = mPendingInstalls.remove(0);
if (params != null) {
- handleStartCopy(params);
+ params.handleStartCopy(mContainerService);
}
}
break;
@@ -421,54 +424,6 @@ class PackageManagerService extends IPackageManager.Stub {
} break;
}
}
-
- // Utility method to initiate copying apk via media
- // container service.
- private void handleStartCopy(InstallParams params) {
- int ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- if (mContainerService != null) {
- // Remote call to find out default install location
- int loc = params.getInstallLocation(mContainerService);
- // Use install location to create InstallArgs and temporary
- // install location
- 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 {
- if ((params.flags & PackageManager.INSTALL_EXTERNAL) == 0){
- if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
- // Set the flag to install on external media.
- params.flags |= PackageManager.INSTALL_EXTERNAL;
- } else {
- // Make sure the flag for installing on external
- // media is unset
- params.flags &= ~PackageManager.INSTALL_EXTERNAL;
- }
- }
- // Disable forward locked apps on sdcard.
- if ((params.flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 &&
- (params.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- // Make sure forward locked apps can only be installed
- // on internal storage
- Log.w(TAG, "Cannot install protected apps on sdcard");
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else {
- ret = PackageManager.INSTALL_SUCCEEDED;
- }
- }
- }
- mHandler.sendEmptyMessage(MCS_UNBIND);
- // Create the file args now.
- InstallArgs args = createInstallArgs(params);
- if (ret == PackageManager.INSTALL_SUCCEEDED) {
- // Create copy only if we are not in an erroneous state.
- args.createCopyFile();
- // Remote call to initiate copy
- ret = args.copyApk(mContainerService);
- }
- processPendingInstall(args, ret);
- }
}
static boolean installOnSd(int flags) {
@@ -2180,8 +2135,8 @@ class PackageManagerService extends IPackageManager.Stub {
int i;
for (i=0; i<files.length; i++) {
File file = new File(dir, files[i]);
- if (files[i] != null && files[i].endsWith(".zip")) {
- // Public resource for forward locked package. Ignore
+ if (!isPackageFilename(files[i])) {
+ // Ignore entries which are not apk's
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
@@ -2936,11 +2891,6 @@ class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
}
- // We don't expect installation to fail beyond this point,
- if ((scanMode&SCAN_MONITOR) != 0) {
- mAppDirs.put(pkg.mPath, pkg);
- }
-
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
@@ -2950,6 +2900,10 @@ class PackageManagerService extends IPackageManager.Stub {
}
synchronized (mPackages) {
+ // We don't expect installation to fail beyond this point,
+ if ((scanMode&SCAN_MONITOR) != 0) {
+ mAppDirs.put(pkg.mPath, pkg);
+ }
// Add the new setting to mSettings
mSettings.insertPackageSettingLP(pkgSetting, pkg);
// Add the new setting to mPackages
@@ -4224,19 +4178,24 @@ class PackageManagerService extends IPackageManager.Stub {
return;
}
+ // Ignore packages that are being installed or
+ // have just been installed.
+ if (ignoreCodePath(fullPathStr)) {
+ return;
+ }
+ PackageParser.Package p = null;
+ synchronized (mPackages) {
+ p = mAppDirs.get(fullPathStr);
+ }
if ((event&REMOVE_EVENTS) != 0) {
- synchronized (mInstallLock) {
- PackageParser.Package p = mAppDirs.get(fullPathStr);
- if (p != null) {
- removePackageLI(p, true);
- removedPackage = p.applicationInfo.packageName;
- removedUid = p.applicationInfo.uid;
- }
+ if (p != null) {
+ removePackageLI(p, true);
+ removedPackage = p.applicationInfo.packageName;
+ removedUid = p.applicationInfo.uid;
}
}
if ((event&ADD_EVENTS) != 0) {
- PackageParser.Package p = mAppDirs.get(fullPathStr);
if (p == null) {
p = scanPackageLI(fullPath,
(mIsRom ? PackageParser.PARSE_IS_SYSTEM : 0) |
@@ -4312,13 +4271,6 @@ class PackageManagerService extends IPackageManager.Stub {
}
args.doPostInstall(res.returnCode);
}
- if (args.observer != null) {
- try {
- args.observer.packageInstalled(res.name, res.returnCode);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
- }
// There appears to be a subtle deadlock condition if the sendPackageBroadcast
// call appears in the synchronized block above.
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
@@ -4345,15 +4297,29 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
Runtime.getRuntime().gc();
+ if (args.observer != null) {
+ try {
+ args.observer.packageInstalled(res.name, res.returnCode);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ }
}
});
}
- static final class InstallParams {
+ interface HandlerParams {
+ void handleStartCopy(IMediaContainerService imcs);
+ void handleServiceError();
+ }
+
+ class InstallParams implements HandlerParams {
final IPackageInstallObserver observer;
int flags;
final Uri packageURI;
final String installerPackageName;
+ private InstallArgs mArgs;
+ private int mRet;
InstallParams(Uri packageURI,
IPackageInstallObserver observer, int flags,
String installerPackageName) {
@@ -4363,13 +4329,118 @@ class PackageManagerService extends IPackageManager.Stub {
this.installerPackageName = installerPackageName;
}
- public int getInstallLocation(IMediaContainerService imcs) {
+ private int getInstallLocation(IMediaContainerService imcs) {
try {
return imcs.getRecommendedInstallLocation(packageURI);
} catch (RemoteException e) {
}
return -1;
}
+
+ public void handleStartCopy(IMediaContainerService imcs) {
+ int ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ if (imcs != null) {
+ // Remote call to find out default install location
+ int loc = getInstallLocation(imcs);
+ // Use install location to create InstallArgs and temporary
+ // install location
+ 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 {
+ if ((flags & PackageManager.INSTALL_EXTERNAL) == 0){
+ if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
+ // Set the flag to install on external media.
+ flags |= PackageManager.INSTALL_EXTERNAL;
+ } else {
+ // Make sure the flag for installing on external
+ // media is unset
+ flags &= ~PackageManager.INSTALL_EXTERNAL;
+ }
+ }
+ // Disable forward locked apps on sdcard.
+ if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 &&
+ (flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ // Make sure forward locked apps can only be installed
+ // on internal storage
+ Log.w(TAG, "Cannot install protected apps on sdcard");
+ ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ } else {
+ ret = PackageManager.INSTALL_SUCCEEDED;
+ }
+ }
+ }
+ // Create the file args now.
+ mArgs = createInstallArgs(this);
+ if (ret == PackageManager.INSTALL_SUCCEEDED) {
+ // Create copy only if we are not in an erroneous state.
+ // Remote call to initiate copy using temporary file
+ ret = mArgs.copyApk(imcs, true);
+ }
+ mRet = ret;
+ mHandler.sendEmptyMessage(MCS_UNBIND);
+ handleReturnCode();
+ }
+
+ void handleReturnCode() {
+ processPendingInstall(mArgs, mRet);
+ }
+
+ public void handleServiceError() {
+ mArgs = createInstallArgs(this);
+ mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ handleReturnCode();
+ }
+ };
+
+ /*
+ * Utility class used in movePackage api.
+ * srcArgs and targetArgs are not set for invalid flags and make
+ * sure to do null checks when invoking methods on them.
+ * We probably want to return ErrorPrams for both failed installs
+ * and moves.
+ */
+ class MoveParams implements HandlerParams {
+ final IPackageMoveObserver observer;
+ final int flags;
+ final String packageName;
+ final InstallArgs srcArgs;
+ final InstallArgs targetArgs;
+ int mRet;
+ MoveParams(InstallArgs srcArgs,
+ IPackageMoveObserver observer,
+ int flags, String packageName) {
+ this.srcArgs = srcArgs;
+ this.observer = observer;
+ this.flags = flags;
+ this.packageName = packageName;
+ if (srcArgs != null) {
+ Uri packageUri = Uri.fromFile(new File(srcArgs.getCodePath()));
+ targetArgs = createInstallArgs(packageUri, flags, packageName);
+ } else {
+ targetArgs = null;
+ }
+ }
+
+ public void handleStartCopy(IMediaContainerService imcs) {
+ // Create the file args now.
+ mRet = targetArgs.copyApk(imcs, false);
+ targetArgs.doPreInstall(mRet);
+ mHandler.sendEmptyMessage(MCS_UNBIND);
+ handleReturnCode();
+ }
+
+ void handleReturnCode() {
+ targetArgs.doPostInstall(mRet);
+ // TODO invoke pending move
+ processPendingMove(this, mRet);
+ }
+
+ public void handleServiceError() {
+ mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ handleReturnCode();
+ }
};
private InstallArgs createInstallArgs(InstallParams params) {
@@ -4388,8 +4459,18 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ private InstallArgs createInstallArgs(Uri packageURI, int flags, String pkgName) {
+ if (installOnSd(flags)) {
+ String cid = getNextCodePath(null, pkgName, "/" + SdInstallArgs.RES_FILE_NAME);
+ return new SdInstallArgs(packageURI, cid);
+ } else {
+ return new FileInstallArgs(packageURI, pkgName);
+ }
+ }
+
static abstract class InstallArgs {
final IPackageInstallObserver observer;
+ // Always refers to PackageManager flags only
final int flags;
final Uri packageURI;
final String installerPackageName;
@@ -4404,7 +4485,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
abstract void createCopyFile();
- abstract int copyApk(IMediaContainerService imcs);
+ abstract int copyApk(IMediaContainerService imcs, boolean temp);
abstract int doPreInstall(int status);
abstract boolean doRename(int status, String pkgName, String oldCodePath);
abstract int doPostInstall(int status);
@@ -4419,6 +4500,7 @@ class PackageManagerService extends IPackageManager.Stub {
File installDir;
String codeFileName;
String resourceFileName;
+ boolean created = false;
FileInstallArgs(InstallParams params) {
super(params.packageURI, params.observer,
@@ -4433,6 +4515,15 @@ class PackageManagerService extends IPackageManager.Stub {
resourceFileName = fullResourcePath;
}
+ FileInstallArgs(Uri packageURI, String pkgName) {
+ super(packageURI, null, 0, null);
+ boolean fwdLocked = isFwdLocked(flags);
+ installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
+ String apkName = getNextCodePath(null, pkgName, ".apk");
+ codeFileName = new File(installDir, apkName + ".apk").getPath();
+ resourceFileName = getResourcePathFromCodePath();
+ }
+
String getCodePath() {
return codeFileName;
}
@@ -4442,11 +4533,29 @@ class PackageManagerService extends IPackageManager.Stub {
installDir = fwdLocked ? mDrmAppPrivateInstallDir : mAppInstallDir;
codeFileName = createTempPackageFile(installDir).getPath();
resourceFileName = getResourcePathFromCodePath();
+ created = true;
}
- int copyApk(IMediaContainerService imcs) {
+ int copyApk(IMediaContainerService imcs, boolean temp) {
+ if (temp) {
+ // Generate temp file name
+ createCopyFile();
+ }
// Get a ParcelFileDescriptor to write to the output file
File codeFile = new File(codeFileName);
+ if (!created) {
+ try {
+ codeFile.createNewFile();
+ // Set permissions
+ if (!setPermissions()) {
+ // Failed setting permissions.
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to create file " + codeFile);
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ }
+ }
ParcelFileDescriptor out = null;
try {
out = ParcelFileDescriptor.open(codeFile,
@@ -4491,7 +4600,7 @@ class PackageManagerService extends IPackageManager.Stub {
codeFileName = desFile.getPath();
resourceFileName = getResourcePathFromCodePath();
// Set permissions
- if (!setPermissions(pkgName)) {
+ if (!setPermissions()) {
// Failed setting permissions.
return false;
}
@@ -4558,7 +4667,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
- private boolean setPermissions(String pkgName) {
+ private boolean setPermissions() {
// TODO Do this in a more elegant way later on. for now just a hack
if (!isFwdLocked(flags)) {
final int filePermissions =
@@ -4594,7 +4703,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
SdInstallArgs(String fullCodePath, String fullResourcePath) {
- super(null, null, ApplicationInfo.FLAG_ON_SDCARD, null);
+ super(null, null, PackageManager.INSTALL_EXTERNAL, null);
// Extract cid from fullCodePath
int eidx = fullCodePath.lastIndexOf("/");
String subStr1 = fullCodePath.substring(0, eidx);
@@ -4604,7 +4713,12 @@ class PackageManagerService extends IPackageManager.Stub {
}
SdInstallArgs(String cid) {
- super(null, null, ApplicationInfo.FLAG_ON_SDCARD, null);
+ super(null, null, PackageManager.INSTALL_EXTERNAL, null);
+ this.cid = cid;
+ }
+
+ SdInstallArgs(Uri packageURI, String cid) {
+ super(packageURI, null, PackageManager.INSTALL_EXTERNAL, null);
this.cid = cid;
}
@@ -4612,7 +4726,10 @@ class PackageManagerService extends IPackageManager.Stub {
cid = getTempContainerId();
}
- int copyApk(IMediaContainerService imcs) {
+ int copyApk(IMediaContainerService imcs, boolean temp) {
+ if (temp) {
+ createCopyFile();
+ }
try {
cachePath = imcs.copyResourceToContainer(
packageURI, cid,
@@ -4771,8 +4888,8 @@ class PackageManagerService extends IPackageManager.Stub {
if (sidx != -1) {
subStr = subStr.substring(sidx + prefix.length());
if (subStr != null) {
- if (subStr.startsWith("-")) {
- subStr = subStr.substring(1);
+ if (subStr.startsWith(INSTALL_PACKAGE_SUFFIX)) {
+ subStr = subStr.substring(INSTALL_PACKAGE_SUFFIX.length());
}
try {
idx = Integer.parseInt(subStr);
@@ -4786,10 +4903,26 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
}
- idxStr = "-" + Integer.toString(idx);
+ idxStr = INSTALL_PACKAGE_SUFFIX + Integer.toString(idx);
return prefix + idxStr;
}
+ // Utility method used to ignore ADD/REMOVE events
+ // by directory observer.
+ private static boolean ignoreCodePath(String fullPathStr) {
+ String apkName = getApkName(fullPathStr);
+ int idx = apkName.lastIndexOf(INSTALL_PACKAGE_SUFFIX);
+ if (idx != -1 && ((idx+1) < apkName.length())) {
+ // Make sure the package ends with a numeral
+ String version = apkName.substring(idx+1);
+ try {
+ Integer.parseInt(version);
+ return true;
+ } catch (NumberFormatException e) {}
+ }
+ return false;
+ }
+
// Utility method that returns the relative package path with respect
// to the installation directory. Like say for /data/data/com.test-1.apk
// string com.test-1 is returned.
@@ -5055,6 +5188,19 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ // Utility method used to move dex files during install.
+ private int moveDexFiles(PackageParser.Package newPackage) {
+ int retCode;
+ if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
+ if (retCode != 0) {
+ Log.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ }
+ }
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
private void updateSettingsLI(PackageParser.Package newPackage,
String installerPackageName, PackageInstalledInfo res) {
String pkgName = newPackage.packageName;
@@ -5066,27 +5212,20 @@ class PackageManagerService extends IPackageManager.Stub {
mSettings.writeLP();
}
- int retCode = 0;
- if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
- retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath);
- if (retCode != 0) {
- Log.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
- res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- return;
- }
- }
- res.returnCode = setPermissionsLI(newPackage);
- if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ if ((res.returnCode = moveDexFiles(newPackage))
+ != PackageManager.INSTALL_SUCCEEDED) {
+ // Discontinue if moving dex files failed.
return;
- } else {
- Log.d(TAG, "New package installed in " + newPackage.mPath);
}
- if(res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ if((res.returnCode = setPermissionsLI(newPackage))
+ != PackageManager.INSTALL_SUCCEEDED) {
if (mInstaller != null) {
mInstaller.rmdex(newPackage.mScanPath);
}
+ return;
+ } else {
+ Log.d(TAG, "New package installed in " + newPackage.mPath);
}
-
synchronized (mPackages) {
grantPermissionsLP(newPackage, true);
res.name = pkgName;
@@ -6765,9 +6904,13 @@ class PackageManagerService extends IPackageManager.Stub {
HashSet<String> loadedPermissions = new HashSet<String>();
GrantedPermissions(int pkgFlags) {
+ setFlags(pkgFlags);
+ }
+
+ void setFlags(int pkgFlags) {
this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) |
- (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
- (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD);
+ (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) |
+ (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD);
}
}
@@ -8662,11 +8805,6 @@ class PackageManagerService extends IPackageManager.Stub {
uidArr[di++] = uidList[i];
}
}
- if (true) {
- for (int j = 0; j < num; j++) {
- Log.i(TAG, "uidArr[" + j + "]=" + uidArr[j]);
- }
- }
}
if (mediaStatus) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
@@ -8779,4 +8917,145 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
}
+
+ public void movePackage(final String packageName,
+ final IPackageMoveObserver observer, final int flags) {
+ if (packageName == null) {
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MOVE_PACKAGE, null);
+ int returnCode = PackageManager.MOVE_SUCCEEDED;
+ int currFlags = 0;
+ int newFlags = 0;
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ returnCode = PackageManager.MOVE_FAILED_DOESNT_EXIST;
+ }
+ // Disable moving fwd locked apps and system packages
+ if (pkg.applicationInfo != null &&
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ Log.w(TAG, "Cannot move system application");
+ returnCode = PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
+ } else if (pkg.applicationInfo != null &&
+ (pkg.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0) {
+ Log.w(TAG, "Cannot move forward locked app.");
+ returnCode = PackageManager.MOVE_FAILED_FORWARD_LOCKED;
+ } else {
+ // Find install location first
+ if ((flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 &&
+ (flags & PackageManager.MOVE_INTERNAL) != 0) {
+ Log.w(TAG, "Ambigous flags specified for move location.");
+ returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
+ } else {
+ newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ?
+ PackageManager.INSTALL_EXTERNAL : 0;
+ currFlags = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0 ?
+ PackageManager.INSTALL_EXTERNAL : 0;
+ if (newFlags == currFlags) {
+ Log.w(TAG, "No move required. Trying to move to same location");
+ returnCode = PackageManager.MOVE_FAILED_INVALID_LOCATION;
+ }
+ }
+ }
+ if (returnCode != PackageManager.MOVE_SUCCEEDED) {
+ processPendingMove(new MoveParams(null, observer, 0, null), returnCode);
+ } else {
+ Message msg = mHandler.obtainMessage(INIT_COPY);
+ InstallArgs srcArgs = createInstallArgs(currFlags, pkg.applicationInfo.sourceDir,
+ pkg.applicationInfo.publicSourceDir);
+ MoveParams mp = new MoveParams(srcArgs, observer, newFlags,
+ packageName);
+ msg.obj = mp;
+ mHandler.sendMessage(msg);
+ }
+ }
+ }
+
+ private void processPendingMove(final MoveParams mp, final int currentStatus) {
+ // Queue up an async operation since the package deletion may take a little while.
+ mHandler.post(new Runnable() {
+ public void run() {
+ mHandler.removeCallbacks(this);
+ int returnCode = currentStatus;
+ boolean moveSucceeded = (returnCode == PackageManager.MOVE_SUCCEEDED);
+ if (moveSucceeded) {
+ int uid = -1;
+ synchronized (mPackages) {
+ uid = mPackages.get(mp.packageName).applicationInfo.uid;
+ }
+ ArrayList<String> pkgList = new ArrayList<String>();
+ pkgList.add(mp.packageName);
+ int uidArr[] = new int[] { uid };
+ // Send resources unavailable broadcast
+ sendResourcesChangedBroadcast(false, pkgList, uidArr);
+
+ // Update package code and resource paths
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(mp.packageName);
+ if (pkg != null) {
+ String oldCodePath = pkg.mPath;
+ String newCodePath = mp.targetArgs.getCodePath();
+ String newResPath = mp.targetArgs.getResourcePath();
+ pkg.mPath = newCodePath;
+ // Move dex files around
+ if (moveDexFiles(pkg)
+ != PackageManager.INSTALL_SUCCEEDED) {
+ // Moving of dex files failed. Set
+ // error code and abort move.
+ pkg.mPath = pkg.mScanPath;
+ returnCode = PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE;
+ moveSucceeded = false;
+ } else {
+ pkg.mScanPath = newCodePath;
+ pkg.applicationInfo.sourceDir = newCodePath;
+ pkg.applicationInfo.publicSourceDir = newResPath;
+ PackageSetting ps = (PackageSetting) pkg.mExtras;
+ ps.codePath = new File(pkg.applicationInfo.sourceDir);
+ ps.codePathString = ps.codePath.getPath();
+ ps.resourcePath = new File(pkg.applicationInfo.publicSourceDir);
+ ps.resourcePathString = ps.resourcePath.getPath();
+ // Set the application info flag correctly.
+ if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_ON_SDCARD;
+ } else {
+ pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_ON_SDCARD;
+ }
+ ps.setFlags(pkg.applicationInfo.flags);
+ mAppDirs.remove(oldCodePath);
+ mAppDirs.put(newCodePath, pkg);
+ // Persist settings
+ mSettings.writeLP();
+ }
+ }
+ }
+ if (moveSucceeded) {
+ // Delete older code
+ synchronized (mInstallLock) {
+ mp.srcArgs.cleanUpResourcesLI();
+ }
+ // Send resources available broadcast
+ sendResourcesChangedBroadcast(true, pkgList, uidArr);
+ Runtime.getRuntime().gc();
+ }
+ }
+ if (!moveSucceeded){
+ // Clean up failed installation
+ if (mp.targetArgs != null) {
+ mp.targetArgs.doPostInstall(
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+ }
+ }
+ IPackageMoveObserver observer = mp.observer;
+ if (observer != null) {
+ try {
+ observer.packageMoved(mp.packageName, returnCode);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+ });
+ }
}
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index f1ba44a..2ccc9bb 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -26,6 +26,7 @@ import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstallObserver;
+import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
@@ -308,6 +309,14 @@ public class MockPackageManager extends PackageManager {
int flags, String installerPackageName) {
throw new UnsupportedOperationException();
}
+
+ /**
+ * @hide - to match hiding in superclass
+ */
+ @Override
+ public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ throw new UnsupportedOperationException();
+ }
@Override
public String getInstallerPackageName(String packageName) {
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index 28f1e73..e06c3a8 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -28,6 +28,7 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
+ <uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ASEC_ACCESS" />
<uses-permission android:name="android.permission.ASEC_CREATE" />
diff --git a/tests/AndroidTests/res/raw/install_loc_auto b/tests/AndroidTests/res/raw/install_loc_auto
index 60dda18..d5d2739 100644
--- a/tests/AndroidTests/res/raw/install_loc_auto
+++ b/tests/AndroidTests/res/raw/install_loc_auto
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_internal b/tests/AndroidTests/res/raw/install_loc_internal
index 1bc33ca..eb6279a 100644
--- a/tests/AndroidTests/res/raw/install_loc_internal
+++ b/tests/AndroidTests/res/raw/install_loc_internal
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_sdcard b/tests/AndroidTests/res/raw/install_loc_sdcard
index 6604e35..c774989 100644
--- a/tests/AndroidTests/res/raw/install_loc_sdcard
+++ b/tests/AndroidTests/res/raw/install_loc_sdcard
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_unspecified b/tests/AndroidTests/res/raw/install_loc_unspecified
index 88bbace..ab226c6 100644
--- a/tests/AndroidTests/res/raw/install_loc_unspecified
+++ b/tests/AndroidTests/res/raw/install_loc_unspecified
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index be9571c..9c5c44d 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -37,6 +37,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageMoveObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -159,6 +160,7 @@ public class PackageManagerTests extends AndroidTestCase {
PackageInstallObserver observer = new PackageInstallObserver();
final boolean received = false;
mContext.registerReceiver(receiver, receiver.filter);
+ final boolean DEBUG = true;
try {
// Wait on observer
synchronized(observer) {
@@ -173,6 +175,7 @@ public class PackageManagerTests extends AndroidTestCase {
throw new Exception("Timed out waiting for packageInstalled callback");
}
if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ Log.i(TAG, "Failed to install with error code = " + observer.returnCode);
return false;
}
// Verify we received the broadcast
@@ -237,7 +240,9 @@ public class PackageManagerTests extends AndroidTestCase {
File sourceFile = new File(archiveFilePath);
DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
- return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+ PackageParser.Package pkg = packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+ packageParser = null;
+ return pkg;
}
private void assertInstall(String pkgName, int flags) {
@@ -297,6 +302,25 @@ public class PackageManagerTests extends AndroidTestCase {
return installFromRawResource("install.apk", R.raw.install, flags, cleanUp,
false, -1);
}
+
+ public void clearSecureContainersForPkg(String pkgName) {
+ IMountService ms = getMs();
+ try {
+ String list[] = ms.getSecureContainerList();
+ if (list != null) {
+ for (String cid : list) {
+ boolean delete = false;
+ // STOPSHIP issues with rename should be fixed.
+ if (cid.contains(pkgName) ||
+ cid.contains("smdltmp")) {
+ Log.i(TAG, "Destroying container " + cid);
+ ms.destroySecureContainer(cid, true);
+ }
+ }
+ }
+ } catch (RemoteException e) {}
+ }
+
/*
* Utility function that reads a apk bundled as a raw resource
* copies it into own data directory and invokes
@@ -310,11 +334,15 @@ public class PackageManagerTests extends AndroidTestCase {
PackageParser.Package pkg = parsePackage(packageURI);
assertNotNull(pkg);
InstallParams ip = null;
+ // Make sure the package doesn't exist
+ getPm().deletePackage(pkg.packageName, null, 0);
+ // Clean up the containers as well
+ if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ clearSecureContainersForPkg(pkg.packageName);
+ }
try {
try {
if (fail) {
- // Make sure it doesn't exist
- getPm().deletePackage(pkg.packageName, null, 0);
assertTrue(invokeInstallPackageFail(packageURI, flags,
pkg.packageName, result));
assertNotInstalled(pkg.packageName);
@@ -811,7 +839,7 @@ public class PackageManagerTests extends AndroidTestCase {
public void testManifestInstallLocationSdcard() {
installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- PackageManager.INSTALL_EXTERNAL, true, false, -1);
+ 0, true, false, -1);
}
public void testManifestInstallLocationAuto() {
@@ -826,10 +854,186 @@ public class PackageManagerTests extends AndroidTestCase {
public void testManifestInstallLocationFwdLockedSdcard() {
installFromRawResource("install.apk", R.raw.install_loc_sdcard,
- PackageManager.INSTALL_FORWARD_LOCK |
- PackageManager.INSTALL_EXTERNAL, true, true,
+ PackageManager.INSTALL_FORWARD_LOCK, true, true,
PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION);
}
+
+ public void xxxtestClearAllSecureContainers() {
+ IMountService ms = getMs();
+ try {
+ String list[] = ms.getSecureContainerList();
+ if (list != null) {
+ for (String cid : list) {
+ Log.i(TAG, "Destroying container " + cid);
+ ms.destroySecureContainer(cid, false);
+ }
+ }
+ } catch (RemoteException e) {}
+ }
+
+ class MoveReceiver extends GenericReceiver {
+ String pkgName;
+ final static int INVALID = -1;
+ final static int REMOVED = 1;
+ final static int ADDED = 2;
+ int removed = INVALID;
+
+ MoveReceiver(String pkgName) {
+ this.pkgName = pkgName;
+ filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ super.setFilter(filter);
+ }
+
+ public boolean notifyNow(Intent intent) {
+ String action = intent.getAction();
+ Log.i(TAG, "MoveReceiver::" + action);
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ String[] list = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ if (list != null) {
+ for (String pkg : list) {
+ if (pkg.equals(pkgName)) {
+ removed = REMOVED;
+ break;
+ }
+ }
+ }
+ removed = REMOVED;
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ if (removed != REMOVED) {
+ return false;
+ }
+ String[] list = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ if (list != null) {
+ for (String pkg : list) {
+ if (pkg.equals(pkgName)) {
+ removed = ADDED;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ private class PackageMoveObserver extends IPackageMoveObserver.Stub {
+ public int returnCode;
+ private boolean doneFlag = false;
+
+ public void packageMoved(String packageName, int returnCode) {
+ synchronized(this) {
+ this.returnCode = returnCode;
+ doneFlag = true;
+ notifyAll();
+ }
+ }
+
+ public boolean isDone() {
+ return doneFlag;
+ }
+ }
+
+ public boolean invokeMovePackage(String pkgName, int flags,
+ GenericReceiver receiver) throws Exception {
+ PackageMoveObserver observer = new PackageMoveObserver();
+ final boolean received = false;
+ mContext.registerReceiver(receiver, receiver.filter);
+ try {
+ // Wait on observer
+ synchronized(observer) {
+ synchronized (receiver) {
+ getPm().movePackage(pkgName, observer, flags);
+ long waitTime = 0;
+ while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ observer.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!observer.isDone()) {
+ throw new Exception("Timed out waiting for pkgmove callback");
+ }
+ if (observer.returnCode != PackageManager.MOVE_SUCCEEDED) {
+ return false;
+ }
+ // Verify we received the broadcast
+ waitTime = 0;
+ while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ receiver.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!receiver.isDone()) {
+ throw new Exception("Timed out waiting for MOVE notifications");
+ }
+ return receiver.received;
+ }
+ }
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ /*
+ * Utility function that reads a apk bundled as a raw resource
+ * copies it into own data directory and invokes
+ * PackageManager api to install first and then replace it
+ * again.
+ */
+ public void moveFromRawResource(int installFlags, int moveFlags,
+ int expRetCode) {
+ // Install first
+ InstallParams ip = sampleInstallFromRawResource(installFlags, false);
+ ApplicationInfo oldAppInfo = null;
+ try {
+ oldAppInfo = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+ } catch (NameNotFoundException e) {
+ failStr("Pkg hasnt been installed correctly");
+ }
+
+ // Create receiver based on expRetCode
+ MoveReceiver receiver = new MoveReceiver(ip.pkg.packageName);
+ try {
+ boolean retCode = invokeMovePackage(ip.pkg.packageName, moveFlags,
+ receiver);
+ if (expRetCode == PackageManager.MOVE_SUCCEEDED) {
+ assertTrue(retCode);
+ ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+ assertNotNull(info);
+ if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) {
+ assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) == 0);
+ } else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0){
+ assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+ }
+ } else {
+ assertFalse(retCode);
+ ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.packageName, 0);
+ assertNotNull(info);
+ assertEquals(oldAppInfo.flags, info.flags);
+ }
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ } finally {
+ cleanUpInstall(ip);
+ }
+ }
+
+ public void testMoveAppInternalToExternal() {
+ moveFromRawResource(0, PackageManager.MOVE_EXTERNAL_MEDIA,
+ PackageManager.MOVE_SUCCEEDED);
+ }
+
+ public void testMoveAppInternalToInternal() {
+ moveFromRawResource(0, PackageManager.MOVE_INTERNAL,
+ PackageManager.MOVE_FAILED_INVALID_LOCATION);
+ }
+
+ public void testMoveAppExternalToExternal() {
+ moveFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.MOVE_EXTERNAL_MEDIA,
+ PackageManager.MOVE_FAILED_INVALID_LOCATION);
+ }
+ public void testMoveAppExternalToInternal() {
+ moveFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.MOVE_INTERNAL,
+ PackageManager.MOVE_SUCCEEDED);
+ }
/*
* TODO's
* check version numbers for upgrades