diff options
author | Suchi Amalapurapu <asuchitra@google.com> | 2010-02-19 14:27:29 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-02-19 14:27:29 -0800 |
commit | 6c81defa3d1111c36f8b0c9c4e84e8b2c342620f (patch) | |
tree | 135f77c35a105adfb00782f991112a61a8c57b34 | |
parent | 8afbc53578a55cb39f97f959a0b444bf24f317ee (diff) | |
parent | 8946dd3355fc1dcbad872c0546e356474d4cc5de (diff) | |
download | frameworks_base-6c81defa3d1111c36f8b0c9c4e84e8b2c342620f.zip frameworks_base-6c81defa3d1111c36f8b0c9c4e84e8b2c342620f.tar.gz frameworks_base-6c81defa3d1111c36f8b0c9c4e84e8b2c342620f.tar.bz2 |
Merge "Move package from internal to external and vice versa."
18 files changed, 764 insertions, 121 deletions
@@ -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=""android.permission.MOVE_PACKAGE"" + 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 Binary files differindex 60dda18..d5d2739 100644 --- a/tests/AndroidTests/res/raw/install_loc_auto +++ b/tests/AndroidTests/res/raw/install_loc_auto diff --git a/tests/AndroidTests/res/raw/install_loc_internal b/tests/AndroidTests/res/raw/install_loc_internal Binary files differindex 1bc33ca..eb6279a 100644 --- a/tests/AndroidTests/res/raw/install_loc_internal +++ b/tests/AndroidTests/res/raw/install_loc_internal diff --git a/tests/AndroidTests/res/raw/install_loc_sdcard b/tests/AndroidTests/res/raw/install_loc_sdcard Binary files differindex 6604e35..c774989 100644 --- a/tests/AndroidTests/res/raw/install_loc_sdcard +++ b/tests/AndroidTests/res/raw/install_loc_sdcard diff --git a/tests/AndroidTests/res/raw/install_loc_unspecified b/tests/AndroidTests/res/raw/install_loc_unspecified Binary files differindex 88bbace..ab226c6 100644 --- a/tests/AndroidTests/res/raw/install_loc_unspecified +++ b/tests/AndroidTests/res/raw/install_loc_unspecified 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 |