Skip to content

Commit

Permalink
Suspending app can customize intercepting dialog
Browse files Browse the repository at this point in the history
The suspending app has more context about why a particular app was
suspended by the user, but we do not want to delegate the interception
of the suspended activity out of the system.
Hence allowing it further customizations to the dialog to make
it clearer.

Test: atest com.android.server.pm.SuspendDialogInfoTest \
com.android.server.pm.SuspendPackagesTest \
com.android.server.pm.PackageUserStateTest \
com.android.server.pm.PackageManagerSettingsTests \
com.android.server.am.ActivityStartInterceptorTest

atest GtsSuspendAppsPermissionTestCases GtsSuspendAppsTestCases

Bug: 112486945
Bug: 113150060
Change-Id: If9f4d14587a2b75bb572e7984a90e300a2c72d16
  • Loading branch information
Suprabh Shukla committed Oct 12, 2018
1 parent 20c6621 commit 389cb6f
Show file tree
Hide file tree
Showing 21 changed files with 812 additions and 86 deletions.
19 changes: 18 additions & 1 deletion api/system-current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,8 @@ package android.content.pm {
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo);
method public abstract void setUpdateAvailable(java.lang.String, boolean);
method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
Expand Down Expand Up @@ -1244,6 +1245,22 @@ package android.content.pm {
field public int requestRes;
}

public final class SuspendDialogInfo implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.SuspendDialogInfo> CREATOR;
}

public static final class SuspendDialogInfo.Builder {
ctor public SuspendDialogInfo.Builder();
method public android.content.pm.SuspendDialogInfo build();
method public android.content.pm.SuspendDialogInfo.Builder setIcon(int);
method public android.content.pm.SuspendDialogInfo.Builder setMessage(java.lang.String);
method public android.content.pm.SuspendDialogInfo.Builder setMessage(int);
method public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(int);
method public android.content.pm.SuspendDialogInfo.Builder setTitle(int);
}

}

package android.content.pm.dex {
Expand Down
14 changes: 13 additions & 1 deletion core/java/android/app/ApplicationPackageManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
Expand Down Expand Up @@ -85,6 +86,7 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IconDrawableFactory;
import android.util.LauncherIcons;
Expand Down Expand Up @@ -2255,9 +2257,19 @@ public void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
String dialogMessage) {
final SuspendDialogInfo dialogInfo = !TextUtils.isEmpty(dialogMessage)
? new SuspendDialogInfo.Builder().setMessage(dialogMessage).build()
: null;
return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, dialogInfo);
}

@Override
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras,
SuspendDialogInfo dialogInfo) {
try {
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
launcherExtras, dialogMessage, mContext.getOpPackageName(),
launcherExtras, dialogInfo, mContext.getOpPackageName(),
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
Expand Down
3 changes: 2 additions & 1 deletion core/java/android/content/pm/IPackageManager.aidl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
Expand Down Expand Up @@ -273,7 +274,7 @@ interface IPackageManager {

String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
String dialogMessage, String callingPackage, int userId);
in SuspendDialogInfo dialogInfo, String callingPackage, int userId);

boolean isPackageSuspendedForUser(String packageName, int userId);

Expand Down
69 changes: 65 additions & 4 deletions core/java/android/content/pm/PackageManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -5663,7 +5663,7 @@ public abstract boolean getApplicationHiddenSettingAsUser(String packageName,
* {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true} than the packages will be suspended, if set to
* @param suspended If set to {@code true}, the packages will be suspended, if set to
* {@code false}, the packages will be unsuspended.
* @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
* which will be shared with the apps being suspended. Ignored if
Expand All @@ -5675,15 +5675,76 @@ public abstract boolean getApplicationHiddenSettingAsUser(String packageName,
* suspended app.
*
* @return an array of package names for which the suspended status could not be set as
* requested in this method.
* requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
*
* @deprecated use {@link #setPackagesSuspended(String[], boolean, PersistableBundle,
* PersistableBundle, android.content.pm.SuspendDialogInfo)} instead.
*
* @hide
*/
@SystemApi
@Deprecated
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
@Nullable
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
@Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
@Nullable String dialogMessage) {
throw new UnsupportedOperationException("setPackagesSuspended not implemented");
}

/**
* Puts the given packages in a suspended state, where attempts at starting activities are
* denied.
*
* <p>The suspended application's notifications and all of its windows will be hidden, any
* of its started activities will be stopped and it won't be able to ring the device.
* It doesn't remove the data or the actual package file.
*
* <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
* is suspended will be shown instead.
* The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
* to this api. This dialog will have a button that starts the
* {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
* activity which handles this action.
*
* <p>The packages being suspended must already be installed. If a package is uninstalled, it
* will no longer be suspended.
*
* <p>Optionally, the suspending app can provide extra information in the form of
* {@link PersistableBundle} objects to be shared with the apps being suspended and the
* launcher to support customization that they might need to handle the suspended state.
*
* <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api.
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true}, the packages will be suspended, if set to
* {@code false}, the packages will be unsuspended.
* @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
* which will be shared with the apps being suspended. Ignored if
* {@code suspended} is false.
* @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
* provide which will be shared with the launcher. Ignored if
* {@code suspended} is false.
* @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
* should be shown to the user when they try to launch a suspended app.
* Ignored if {@code suspended} is false.
*
* @return an array of package names for which the suspended status could not be set as
* requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
*
* @see #isPackageSuspended
* @see SuspendDialogInfo
* @see SuspendDialogInfo.Builder
* @see Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
@Nullable
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
@Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
String dialogMessage) {
@Nullable SuspendDialogInfo dialogInfo) {
throw new UnsupportedOperationException("setPackagesSuspended not implemented");
}

Expand Down
9 changes: 5 additions & 4 deletions core/java/android/content/pm/PackageManagerInternal.java
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,15 @@ public abstract Bundle getSuspendedPackageLauncherExtras(String packageName,
public abstract String getSuspendingPackage(String suspendedPackage, int userId);

/**
* Get the dialog message to be shown to the user when they try to launch a suspended
* application.
* Get the information describing the dialog to be shown to the user when they try to launch a
* suspended application.
*
* @param suspendedPackage The package that has been suspended.
* @param userId The user for which to check.
* @return The dialog message to be shown to the user.
* @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
*/
public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
@Nullable
public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);

/**
* Do a straight uid lookup for the given package/application in the given user.
Expand Down
8 changes: 5 additions & 3 deletions core/java/android/content/pm/PackageUserState.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import android.os.PersistableBundle;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;

import java.util.Arrays;
Expand All @@ -50,7 +51,7 @@ public class PackageUserState {
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
public String suspendingPackage;
public String dialogMessage; // Message to show when a suspended package launch attempt is made
public SuspendDialogInfo dialogInfo;
public PersistableBundle suspendedAppExtras;
public PersistableBundle suspendedLauncherExtras;
public boolean instantApp;
Expand Down Expand Up @@ -79,6 +80,7 @@ public PackageUserState() {
installReason = PackageManager.INSTALL_REASON_UNKNOWN;
}

@VisibleForTesting
public PackageUserState(PackageUserState o) {
ceDataInode = o.ceDataInode;
installed = o.installed;
Expand All @@ -87,7 +89,7 @@ public PackageUserState(PackageUserState o) {
hidden = o.hidden;
suspended = o.suspended;
suspendingPackage = o.suspendingPackage;
dialogMessage = o.dialogMessage;
dialogInfo = o.dialogInfo;
suspendedAppExtras = o.suspendedAppExtras;
suspendedLauncherExtras = o.suspendedLauncherExtras;
instantApp = o.instantApp;
Expand Down Expand Up @@ -217,7 +219,7 @@ final public boolean equals(Object obj) {
|| !suspendingPackage.equals(oldState.suspendingPackage)) {
return false;
}
if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
if (!Objects.equals(dialogInfo, oldState.dialogInfo)) {
return false;
}
if (!BaseBundle.kindofEquals(suspendedAppExtras,
Expand Down
18 changes: 18 additions & 0 deletions core/java/android/content/pm/SuspendDialogInfo.aidl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm;

parcelable SuspendDialogInfo;
Loading

0 comments on commit 389cb6f

Please sign in to comment.