Loading packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java +1 −1 Original line number Diff line number Diff line Loading @@ -76,7 +76,7 @@ public class UnarchiveActivity extends Activity { boolean hasRequestInstallPermission = Arrays.asList(getRequestedPermissions(callingPackage)) .contains(permission.REQUEST_INSTALL_PACKAGES); boolean hasInstallPermission = getBaseContext().checkPermission(permission.INSTALL_PACKAGES, 0 /* random value for pid */, callingUid) != PackageManager.PERMISSION_GRANTED; 0 /* random value for pid */, callingUid) == PackageManager.PERMISSION_GRANTED; if (!hasRequestInstallPermission && !hasInstallPermission) { Log.e(TAG, "Uid " + callingUid + " does not have " + permission.REQUEST_INSTALL_PACKAGES + " or " Loading packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java +2 −1 Original line number Diff line number Diff line Loading @@ -28,12 +28,13 @@ public class UnarchiveFragment extends DialogFragment implements @Override public Dialog onCreateDialog(Bundle savedInstanceState) { String appTitle = getArguments().getString(UnarchiveActivity.APP_TITLE); String installerTitle = getArguments().getString(UnarchiveActivity.INSTALLER_TITLE); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); dialogBuilder.setTitle( String.format(getContext().getString(R.string.unarchive_application_title), appTitle)); appTitle, installerTitle)); dialogBuilder.setMessage(R.string.unarchive_body_text); dialogBuilder.setPositiveButton(R.string.restore, this); Loading services/core/java/com/android/server/pm/PackageArchiver.java +85 −18 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap; import static android.content.pm.ArchivedActivityInfo.drawableToBitmap; import static android.content.pm.PackageInstaller.EXTRA_UNARCHIVE_STATUS; import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION; import static android.content.pm.PackageInstaller.UNARCHIVAL_OK; import static android.content.pm.PackageManager.DELETE_ARCHIVE; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; Loading Loading @@ -72,10 +73,12 @@ import android.os.Environment; import android.os.IBinder; import android.os.ParcelableException; import android.os.Process; import android.os.RemoteException; import android.os.SELinux; import android.os.UserHandle; import android.text.TextUtils; import android.util.ExceptionUtils; import android.util.Pair; import android.util.Slog; import com.android.internal.R; Loading @@ -93,7 +96,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; Loading Loading @@ -140,15 +145,23 @@ public class PackageArchiver { private final Context mContext; private final PackageManagerService mPm; private final AppStateHelper mAppStateHelper; @Nullable private LauncherApps mLauncherApps; @Nullable private AppOpsManager mAppOpsManager; /* IntentSender store that maps key: {userId, appPackageName} to respective existing attached unarchival intent sender. */ private final Map<Pair<Integer, String>, IntentSender> mLauncherIntentSenders; PackageArchiver(Context context, PackageManagerService mPm) { this.mContext = context; this.mPm = mPm; this.mAppStateHelper = new AppStateHelper(mContext); this.mLauncherIntentSenders = new HashMap<>(); } /** Returns whether a package is archived for a user. */ Loading Loading @@ -235,37 +248,32 @@ public class PackageArchiver { // Return early as the calling UID does not match caller package's UID. return START_CLASS_NOT_FOUND; } String currentLauncherPackageName = getCurrentLauncherPackageName(userId); if ((currentLauncherPackageName == null || !callerPackageName.equals( currentLauncherPackageName)) && callingUid != Process.SHELL_UID) { // TODO(b/311619990): Remove dependency on SHELL_UID for testing Slog.e(TAG, TextUtils.formatSimple( "callerPackageName: %s does not qualify for archival of package: " + "%s!", "callerPackageName: %s does not qualify for unarchival of package: " + "%s!", callerPackageName, packageName)); return START_PERMISSION_DENIED; } // TODO(b/302114464): Handle edge cases & also divert to a dialog based on // permissions + compat options Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName)); try { final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { @Override public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { // TODO(b/302114464): Handle intent sender status codes } }; try { // TODO(b/311709794) Make showUnarchivalConfirmation dependent on the compat options. requestUnarchive(packageName, callerPackageName, new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId)); getOrCreateUnarchiveIntentSender(userId, packageName), UserHandle.of(userId), false /* showUnarchivalConfirmation= */); } catch (Throwable t) { Slog.e(TAG, TextUtils.formatSimple( "Unexpected error occurred while unarchiving package %s: %s.", packageName, t.getLocalizedMessage())); return START_ABORTED; } return START_SUCCESS; } Loading Loading @@ -321,6 +329,20 @@ public class PackageArchiver { return true; } private IntentSender getOrCreateUnarchiveIntentSender(int userId, String packageName) { Pair<Integer, String> key = Pair.create(userId, packageName); synchronized (mLauncherIntentSenders) { IntentSender intentSender = mLauncherIntentSenders.get(key); if (intentSender != null) { return intentSender; } IntentSender unarchiveIntentSender = new IntentSender( (IIntentSender) new UnarchiveIntentSender()); mLauncherIntentSenders.put(key, unarchiveIntentSender); return unarchiveIntentSender; } } /** Creates archived state for the package and user. */ private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId) throws PackageManager.NameNotFoundException { Loading Loading @@ -553,6 +575,15 @@ public class PackageArchiver { @NonNull String callerPackageName, @NonNull IntentSender statusReceiver, @NonNull UserHandle userHandle) { requestUnarchive(packageName, callerPackageName, statusReceiver, userHandle, false /* showUnarchivalConfirmation= */); } private void requestUnarchive( @NonNull String packageName, @NonNull String callerPackageName, @NonNull IntentSender statusReceiver, @NonNull UserHandle userHandle, boolean showUnarchivalConfirmation) { Objects.requireNonNull(packageName); Objects.requireNonNull(callerPackageName); Objects.requireNonNull(statusReceiver); Loading Loading @@ -597,8 +628,8 @@ public class PackageArchiver { + "an unarchival."); } if (!hasInstallPackages) { requestUnarchiveConfirmation(packageName, statusReceiver); if (!hasInstallPackages || showUnarchivalConfirmation) { requestUnarchiveConfirmation(packageName, statusReceiver, userHandle); return; } Loading @@ -622,7 +653,8 @@ public class PackageArchiver { () -> unarchiveInternal(packageName, userHandle, installerPackage, draftSessionId)); } private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver) { private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver, UserHandle user) { final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_DIALOG); dialogIntent.putExtra(EXTRA_UNARCHIVE_INTENT_SENDER, statusReceiver); dialogIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName); Loading @@ -632,6 +664,7 @@ public class PackageArchiver { broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION); broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent); broadcastIntent.putExtra(Intent.EXTRA_USER, user); sendIntent(statusReceiver, packageName, /* message= */ "", broadcastIntent); } Loading @@ -656,6 +689,7 @@ public class PackageArchiver { int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId); // Handles case of repeated unarchival calls for the same package. // TODO(b/316881759) Allow attaching multiple intentSenders to one session. int existingSessionId = mPm.mInstallerService.getExistingDraftSessionId(installerUid, sessionParams, userId); Loading Loading @@ -849,7 +883,13 @@ public class PackageArchiver { void notifyUnarchivalListener(int status, String installerPackageName, String appPackageName, long requiredStorageBytes, @Nullable PendingIntent userActionIntent, IntentSender unarchiveIntentSender, int userId) { @Nullable IntentSender unarchiveIntentSender, int userId) { if (unarchiveIntentSender == null) { // Maybe this can happen if the installer calls reportUnarchivalStatus twice in quick // succession. return; } final Intent broadcastIntent = new Intent(); broadcastIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, appPackageName); broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status); Loading @@ -863,6 +903,7 @@ public class PackageArchiver { return; } broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent); broadcastIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); } final BroadcastOptions options = BroadcastOptions.makeBasic(); Loading @@ -874,6 +915,10 @@ public class PackageArchiver { options.toBundle()); } catch (IntentSender.SendIntentException e) { Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e); } finally { synchronized (mLauncherIntentSenders) { mLauncherIntentSenders.remove(Pair.create(userId, appPackageName)); } } } Loading @@ -883,6 +928,7 @@ public class PackageArchiver { long requiredStorageBytes, PendingIntent userActionIntent, int userId) { final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_ERROR_DIALOG); dialogIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status); dialogIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); if (requiredStorageBytes > 0) { dialogIntent.putExtra(EXTRA_REQUIRED_BYTES, requiredStorageBytes); } Loading Loading @@ -1118,4 +1164,25 @@ public class PackageArchiver { return activities.toArray(new ArchivedActivityParcel[activities.size()]); } private class UnarchiveIntentSender extends IIntentSender.Stub { @Override public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) throws RemoteException { int status = intent.getExtras().getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS, STATUS_PENDING_USER_ACTION); if (status == UNARCHIVAL_OK) { return; } Intent extraIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class); UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class); if (extraIntent != null && user != null && mAppStateHelper.isAppTopVisible( getCurrentLauncherPackageName(user.getIdentifier()))) { extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(extraIntent, user); } } } } Loading
packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java +1 −1 Original line number Diff line number Diff line Loading @@ -76,7 +76,7 @@ public class UnarchiveActivity extends Activity { boolean hasRequestInstallPermission = Arrays.asList(getRequestedPermissions(callingPackage)) .contains(permission.REQUEST_INSTALL_PACKAGES); boolean hasInstallPermission = getBaseContext().checkPermission(permission.INSTALL_PACKAGES, 0 /* random value for pid */, callingUid) != PackageManager.PERMISSION_GRANTED; 0 /* random value for pid */, callingUid) == PackageManager.PERMISSION_GRANTED; if (!hasRequestInstallPermission && !hasInstallPermission) { Log.e(TAG, "Uid " + callingUid + " does not have " + permission.REQUEST_INSTALL_PACKAGES + " or " Loading
packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java +2 −1 Original line number Diff line number Diff line Loading @@ -28,12 +28,13 @@ public class UnarchiveFragment extends DialogFragment implements @Override public Dialog onCreateDialog(Bundle savedInstanceState) { String appTitle = getArguments().getString(UnarchiveActivity.APP_TITLE); String installerTitle = getArguments().getString(UnarchiveActivity.INSTALLER_TITLE); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); dialogBuilder.setTitle( String.format(getContext().getString(R.string.unarchive_application_title), appTitle)); appTitle, installerTitle)); dialogBuilder.setMessage(R.string.unarchive_body_text); dialogBuilder.setPositiveButton(R.string.restore, this); Loading
services/core/java/com/android/server/pm/PackageArchiver.java +85 −18 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap; import static android.content.pm.ArchivedActivityInfo.drawableToBitmap; import static android.content.pm.PackageInstaller.EXTRA_UNARCHIVE_STATUS; import static android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION; import static android.content.pm.PackageInstaller.UNARCHIVAL_OK; import static android.content.pm.PackageManager.DELETE_ARCHIVE; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; Loading Loading @@ -72,10 +73,12 @@ import android.os.Environment; import android.os.IBinder; import android.os.ParcelableException; import android.os.Process; import android.os.RemoteException; import android.os.SELinux; import android.os.UserHandle; import android.text.TextUtils; import android.util.ExceptionUtils; import android.util.Pair; import android.util.Slog; import com.android.internal.R; Loading @@ -93,7 +96,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; Loading Loading @@ -140,15 +145,23 @@ public class PackageArchiver { private final Context mContext; private final PackageManagerService mPm; private final AppStateHelper mAppStateHelper; @Nullable private LauncherApps mLauncherApps; @Nullable private AppOpsManager mAppOpsManager; /* IntentSender store that maps key: {userId, appPackageName} to respective existing attached unarchival intent sender. */ private final Map<Pair<Integer, String>, IntentSender> mLauncherIntentSenders; PackageArchiver(Context context, PackageManagerService mPm) { this.mContext = context; this.mPm = mPm; this.mAppStateHelper = new AppStateHelper(mContext); this.mLauncherIntentSenders = new HashMap<>(); } /** Returns whether a package is archived for a user. */ Loading Loading @@ -235,37 +248,32 @@ public class PackageArchiver { // Return early as the calling UID does not match caller package's UID. return START_CLASS_NOT_FOUND; } String currentLauncherPackageName = getCurrentLauncherPackageName(userId); if ((currentLauncherPackageName == null || !callerPackageName.equals( currentLauncherPackageName)) && callingUid != Process.SHELL_UID) { // TODO(b/311619990): Remove dependency on SHELL_UID for testing Slog.e(TAG, TextUtils.formatSimple( "callerPackageName: %s does not qualify for archival of package: " + "%s!", "callerPackageName: %s does not qualify for unarchival of package: " + "%s!", callerPackageName, packageName)); return START_PERMISSION_DENIED; } // TODO(b/302114464): Handle edge cases & also divert to a dialog based on // permissions + compat options Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName)); try { final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { @Override public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { // TODO(b/302114464): Handle intent sender status codes } }; try { // TODO(b/311709794) Make showUnarchivalConfirmation dependent on the compat options. requestUnarchive(packageName, callerPackageName, new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId)); getOrCreateUnarchiveIntentSender(userId, packageName), UserHandle.of(userId), false /* showUnarchivalConfirmation= */); } catch (Throwable t) { Slog.e(TAG, TextUtils.formatSimple( "Unexpected error occurred while unarchiving package %s: %s.", packageName, t.getLocalizedMessage())); return START_ABORTED; } return START_SUCCESS; } Loading Loading @@ -321,6 +329,20 @@ public class PackageArchiver { return true; } private IntentSender getOrCreateUnarchiveIntentSender(int userId, String packageName) { Pair<Integer, String> key = Pair.create(userId, packageName); synchronized (mLauncherIntentSenders) { IntentSender intentSender = mLauncherIntentSenders.get(key); if (intentSender != null) { return intentSender; } IntentSender unarchiveIntentSender = new IntentSender( (IIntentSender) new UnarchiveIntentSender()); mLauncherIntentSenders.put(key, unarchiveIntentSender); return unarchiveIntentSender; } } /** Creates archived state for the package and user. */ private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId) throws PackageManager.NameNotFoundException { Loading Loading @@ -553,6 +575,15 @@ public class PackageArchiver { @NonNull String callerPackageName, @NonNull IntentSender statusReceiver, @NonNull UserHandle userHandle) { requestUnarchive(packageName, callerPackageName, statusReceiver, userHandle, false /* showUnarchivalConfirmation= */); } private void requestUnarchive( @NonNull String packageName, @NonNull String callerPackageName, @NonNull IntentSender statusReceiver, @NonNull UserHandle userHandle, boolean showUnarchivalConfirmation) { Objects.requireNonNull(packageName); Objects.requireNonNull(callerPackageName); Objects.requireNonNull(statusReceiver); Loading Loading @@ -597,8 +628,8 @@ public class PackageArchiver { + "an unarchival."); } if (!hasInstallPackages) { requestUnarchiveConfirmation(packageName, statusReceiver); if (!hasInstallPackages || showUnarchivalConfirmation) { requestUnarchiveConfirmation(packageName, statusReceiver, userHandle); return; } Loading @@ -622,7 +653,8 @@ public class PackageArchiver { () -> unarchiveInternal(packageName, userHandle, installerPackage, draftSessionId)); } private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver) { private void requestUnarchiveConfirmation(String packageName, IntentSender statusReceiver, UserHandle user) { final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_DIALOG); dialogIntent.putExtra(EXTRA_UNARCHIVE_INTENT_SENDER, statusReceiver); dialogIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName); Loading @@ -632,6 +664,7 @@ public class PackageArchiver { broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, PackageInstaller.STATUS_PENDING_USER_ACTION); broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent); broadcastIntent.putExtra(Intent.EXTRA_USER, user); sendIntent(statusReceiver, packageName, /* message= */ "", broadcastIntent); } Loading @@ -656,6 +689,7 @@ public class PackageArchiver { int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId); // Handles case of repeated unarchival calls for the same package. // TODO(b/316881759) Allow attaching multiple intentSenders to one session. int existingSessionId = mPm.mInstallerService.getExistingDraftSessionId(installerUid, sessionParams, userId); Loading Loading @@ -849,7 +883,13 @@ public class PackageArchiver { void notifyUnarchivalListener(int status, String installerPackageName, String appPackageName, long requiredStorageBytes, @Nullable PendingIntent userActionIntent, IntentSender unarchiveIntentSender, int userId) { @Nullable IntentSender unarchiveIntentSender, int userId) { if (unarchiveIntentSender == null) { // Maybe this can happen if the installer calls reportUnarchivalStatus twice in quick // succession. return; } final Intent broadcastIntent = new Intent(); broadcastIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, appPackageName); broadcastIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status); Loading @@ -863,6 +903,7 @@ public class PackageArchiver { return; } broadcastIntent.putExtra(Intent.EXTRA_INTENT, dialogIntent); broadcastIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); } final BroadcastOptions options = BroadcastOptions.makeBasic(); Loading @@ -874,6 +915,10 @@ public class PackageArchiver { options.toBundle()); } catch (IntentSender.SendIntentException e) { Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e); } finally { synchronized (mLauncherIntentSenders) { mLauncherIntentSenders.remove(Pair.create(userId, appPackageName)); } } } Loading @@ -883,6 +928,7 @@ public class PackageArchiver { long requiredStorageBytes, PendingIntent userActionIntent, int userId) { final Intent dialogIntent = new Intent(ACTION_UNARCHIVE_ERROR_DIALOG); dialogIntent.putExtra(EXTRA_UNARCHIVE_STATUS, status); dialogIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); if (requiredStorageBytes > 0) { dialogIntent.putExtra(EXTRA_REQUIRED_BYTES, requiredStorageBytes); } Loading Loading @@ -1118,4 +1164,25 @@ public class PackageArchiver { return activities.toArray(new ArchivedActivityParcel[activities.size()]); } private class UnarchiveIntentSender extends IIntentSender.Stub { @Override public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) throws RemoteException { int status = intent.getExtras().getInt(PackageInstaller.EXTRA_UNARCHIVE_STATUS, STATUS_PENDING_USER_ACTION); if (status == UNARCHIVAL_OK) { return; } Intent extraIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class); UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle.class); if (extraIntent != null && user != null && mAppStateHelper.isAppTopVisible( getCurrentLauncherPackageName(user.getIdentifier()))) { extraIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(extraIntent, user); } } } }